2

I receive a large XML file and often the XML file do not validate to schema file. Instead of droping the whole xml file I would like to remove the "invalid" content and save the rest of the XML file.

I'm using xmllint to validate the xml by this command:

xmllint -schema testSchedule.xsd testXML.xml

The XSD file (in this example named testSchedule.xsd):

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.testing.dk" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="MasterData">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Items">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="Item" maxOccurs="unbounded" minOccurs="0">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element type="xs:integer" name="Id" minOccurs="1"/>
                    <xs:element type="xs:integer" name="Width" minOccurs="1"/>
                    <xs:element type="xs:integer" name="Height" minOccurs="0"/>
                    <xs:element type="xs:string" name="Remark"/>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

And the XML file (In this example named testXML.xml):

<?xml version="1.0" encoding="ISO-8859-1" ?>
<MasterData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.testing.dk">
    <Items>
        <Item>
            <Id>1</Id>
            <Width>10</Width>
            <Height>100</Height>
            <Remark>This is OK</Remark>
        </Item>
        <Item>
            <Id>2</Id>
            <Width>20</Width>
            <Height>200</Height>
            <Remark>This is OK - But is missing Height a non mandatory field</Remark>
        </Item>
        <Item>
            <Id>3</Id>
            <Height>300</Height>
            <Remark>This is NOT OK - Missing the mandatory Width</Remark>
        </Item>
        <Item>
            <Id>4</Id>
            <Width>TheIsAString</Width>
            <Height>200</Height>
            <Remark>This is NOT OK - Width is not an integer but a string</Remark>
        </Item>
        <Item>
            <Id>5</Id>
            <Width>50</Width>
            <Height>500</Height>
            <Remark>This is OK and the last</Remark>
        </Item>
    </Items>
</MasterData>

Then I get the this result of the xmllint command:

testXML.xml:18: element Height: Schemas validity error : Element '{http://www.testing.dk}Height': This element is not expected. Expected is ( {http://www.testing.dk}Width ).
testXML.xml:23: element Width: Schemas validity error : Element '{http://www.testing.dk}Width': 'TheIsAString' is not a valid value of the atomic type 'xs:integer'.
testXML.xml fails to validate

And that is all correct - There is two errors in the XML file.

Now I would like to have a tool of some kind to remove entry 3 and 4 so I end up with this result:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<MasterData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.testing.dk">
    <Items>
        <Item>
            <Id>1</Id>
            <Width>10</Width>
            <Height>100</Height>
            <Remark>This is OK</Remark>
        </Item>
        <Item>
            <Id>2</Id>
            <Width>20</Width>
            <Height>200</Height>
            <Remark>This is OK - But is missing Height a non mandatory field</Remark>
        </Item>
        <Item>
            <Id>5</Id>
            <Width>50</Width>
            <Height>500</Height>
            <Remark>This is OK and the last</Remark>
        </Item>
    </Items>
</MasterData>

Does anybody in here have a tool that can do this? I'm currently using bash scripting and the xmllint. I really hope somebody can help.

4

1 回答 1

0

您可以使用这个 XSLT 样式表来实现这一点,您可以在任何支持 XSLT 1.0(大多数语言)的环境中运行它,使用命令行工具(例如xsltproc( libxslt) 或 Saxon、浏览器或在线工具)。这是一个例子。

如果您使用以下样式表将原始 XML 文件作为输入提供给 XSLT 转换器,它将产生您在第二个 XML 中显示的结果:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
    xmlns:t="http://www.testing.dk">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="t:Item[t:Id     and not(number(t:Id))]"/>
    <xsl:template match="t:Item[t:Width  and not(number(t:Width))]"/>
    <xsl:template match="t:Item[t:Height and not(number(t:Height))]"/>
    <xsl:template match="t:Item[not(t:Width)]"/>
    <xsl:template match="t:Item[not(t:Id)]"/>
    <xsl:template match="t:Item[not(t:Remark)]"/>

</xsl:stylesheet>

第一个<xsl:template>块只是将所有节点从源树复制到结果树。它的优先级低于按名称匹配节点的特定模板。

由于匹配是在需要命名空间限定选择器的 XPath 中完成的,因此您的默认命名空间在<xsl:stylesheet>开始标记中声明并映射到用于限定标记名称的前缀。

每个模板都使用 XPath 表达式来测试特定子元素是否存在于 中Item,或者该子元素是否存在,是否为数字(根据 XSD)。

我使用的是 XSLT 1.0,它得到了更广泛的支持,应该更容易在您的环境中找到。但是,如果您可以使用 XSLT 2.0 处理器,则可以使用 XSLT 2.0 功能,例如对 XSD 类型的支持,而不是将您的值与数字类型进行比较,您可以将它们与特定类型进行比较,例如xsd:integer.

您可以通过此XSLT Fiddle中的样式表验证对示例 XML 执行的转换。

如果您创建一个包含上述代码的 XML 文档并将其放在一个名为的文件中,stylesheet.xsl您可以使用xsltproc(可能存在于您的环境中)运行转换:

xsltproc stylesheet.xsl testXML.xml > fixedXML.xml
于 2014-06-18T15:59:25.547 回答