在xmllint验证后删除无效的XML

时间:2014-06-18 13:56:59

标签: bash validation scripting xsd xmllint

我收到一个大型XML文件,并且XML文件通常不会验证到架构文件。 我没有删除整个xml文件,而是删除“无效”内容并保存XML文件的其余部分。

我正在使用xmllint通过此命令验证xml:

xmllint -schema testSchedule.xsd testXML.xml

XSD文件(在此示例中名为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>

XML文件(在本例中名为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>

然后我得到了xmllint命令的结果:

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

这一切都是正确的 - XML文件中有两个错误。

现在我想使用某种工具来删除第3项和第4项,所以我最终得到了这个结果:

<?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>

这里有人有一个可以做到这一点的工具吗? 我目前正在使用bash脚本和xmllint。 我真的希望有人能提供帮助。

1 个答案:

答案 0 :(得分:0)

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

如果您使用以下样式表将原始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等XSLT 2.0功能,而不是将您的值与数字类型进行比较,您可以将它们与xsd:integer等特定类型进行比较。

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

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

xsltproc stylesheet.xsl testXML.xml > fixedXML.xml