【问题标题】:Xml Cleaning based on XSD in C#C#中基于XSD的Xml清洗
【发布时间】:2018-05-03 18:50:59
【问题描述】:

如何清理 XML 文件,删除提供的 XSD 中不存在的所有元素?

这不起作用:

public static void Main()
{
    XmlTextReader xsdReader = new XmlTextReader(@"books.xsd");
    XmlSchema schema = XmlSchema.Read(xsdReader, null);

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.Schemas.Add(schema);
    settings.ValidationType = ValidationType.Schema;
    settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);

    XmlReader xmlReader = XmlReader.Create(@"books.xml", settings);
    XmlWriter xmlWriter = XmlWriter.Create(@"books_clean.xml");
    xmlWriter.WriteNode(xmlReader, true);
    xmlWriter.Close();
    xmlReader.Close();
}
private static void ValidationCallBack(object sender, ValidationEventArgs args)
{
    ((XmlReader)sender).Skip();
}

当我使用上述方法时,它不会删除所有“垃圾”标签,而是仅删除第一个垃圾标签并留下第二个。至于为什么我需要接受这个文件,我使用的是旧的 SQLServer 2012 实例,它 需要 XML 与 XSD 完全匹配,即使应用程序不使用 XML 中的额外元素。我无法控制由带有未发布 XSD 的 3rd 方工具提供的源 XML。

示例文件:
书籍.xsd

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="bookstore">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" maxOccurs="unbounded" minOccurs="0">
          <xs:complexType>
            <xs:sequence>
              <xs:element type="xs:string" name="title"/>
              <xs:element type="xs:float" name="price"/>
            </xs:sequence>
            <xs:attribute type="xs:string" name="genre" use="optional"/>
            <xs:attribute type="xs:string" name="ISBN" use="optional"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

书籍.xml

<bookstore>
  <book genre='novel' ISBN='10-861003-324'>
    <title>The Handmaid's Tale</title>
    <price>19.95</price>
    <junk>skdjgklsdg</junk>
    <junk2>skdjgklsdg</junk2>
  </book>
  <book genre='novel' ISBN='1-861001-57-5'>
    <title>Pride And Prejudice</title>
    <price>24.95</price>
    <junk>skdjgssklsdg</junk>
  </book>
</bookstore>

代码大部分复制自:Validating an XML against referenced XSD in C#

【问题讨论】:

  • 源 XML 文件是 ~500mb,最后一个输入文件有 ~120K 可用节点和 ~800K 未使用节点。因此,首选基于流的方法。
  • 您缺少以下内容:while (reader.Read()) ;
  • @jdweng 尝试了这个并修改了问题。 Reader.Read() 仅在每个节点的第一个无效元素上引发 ValidationEvent。它至少删除了两个 标签,而不是 标签。
  • 问题是当你有像 1,2,3,4,5 这样的项目并且你删除项目“3”时,4 变成了 3,5 变成了 4。然后你最终会跳过第 4 项。所以解决方案是通过类似 for(i = list.Count() - 1; i >=0; i--) 向后枚举

标签: c# xml xsd xsd-validation


【解决方案1】:

如果只是删除名称未出现在架构中任何位置的所有元素的问题,那么它可能是可行的,如下所述。但是,在一般情况下,(a)这并不能确保实例对模式有效(例如,元素的顺序可能错误),并且(b)它可能会删除模式实际允许的元素(因为通配符)。

如果去除未知元素的方法看起来有用,你可以这样做:

(a) 编写一个 XSLT 样式表,通过查找 xs:element[@name] 声明从架构中提取所有元素名称,生成具有以下格式的文档:

<allowedElements>
  <allow name="book" namespace=""/>
  <allow name="isbn" namespace=""/>
</allowedElement>

(b) 编写第二个(可流式传输的)XSLT 样式表:

<xsl:transform version="3.0" xmlns:xsl="....">
  <xsl:mode on-no-match="shallow-copy" streamable="yes"/>
  <xsl:key name="k" match="allow" use="@name, @namespace" composite="yes"/>
  <xsl:template match="*[not(key('k', (local-name(), namespace-uri()), doc('allowed-elements.xml'))]"/>
</xsl:transform> 

【讨论】:

  • 这听起来很有希望,试图弄清楚如何使用多个 XSLT 文档并运行转换。
  • 无法获得工作的 B 部分 XSLT 文档。某处缺少一个括号,我还没能猜出它的位置。
【解决方案2】:

以下成功从提供的示例中删除了所有垃圾标签。第二个 xsl:template 标记首先应用并匹配除专门列入白名单的标记之外的所有内容。然后第一个 xsl:template 标记将节点的副本写入 XmlWriter。

代码:

public static void Main()
{
    XmlReader xmlReader = XmlReader.Create("books.xml");
    XslCompiledTransform myXslTrans = new XslCompiledTransform();
    myXslTrans.Load("books.xslt");
    XmlTextWriter myWriter = new XmlTextWriter("books_clean.xml", null);
    myXslTrans.Transform(xmlReader, null, myWriter);
    xmlReader.Close();
    myWriter.Close();
}

books.xslt

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:mode streamable="yes"/>
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="*[
  not(name()='bookstore') and
  not(name()='book') and
  not(name()='title') and
  not(name()='price')
  ]" />
</xsl:stylesheet>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    • 1970-01-01
    • 2019-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多