XPath表达式得到节点

问题描述:

我有以下XML输入文件:XPath表达式得到节点

<rootnode> 
<section id="1" status="fail"> 
    <outer status="fail"> 
    <inner status="fail"/> 
    <inner status="pass"/> 
    </outer> 
    <outer status="pass"> 
    <inner status="pass"/> 
    </outer> 
    <outer status="pass"/> 
    <outer status="fail"/> 
</section> 
<section id="2" status="fail"> 
    <outer status="fail"> 
    <inner status="pass"/> 
    <inner status="fail"/> 
    <inner status="inc"/> 
    </outer> 
</section> 
</rootnode> 

我要过滤掉,这样的结果看起来像这样所有的非故障状态的节点:

<rootnode> 
<section id="1" status="fail"> 
    <outer status="fail"> 
    <inner status="fail"/> 
    </outer> 
    <outer status="fail"/> 
</section> 
<section id="2" status="fail"> 
    <outer status="fail"> 
    <inner status="fail"/> 
    </outer> 
</section> 
</rootnode> 

<rootnode>不一定包含在结果中。我试图用xpath表达式使用xmllint。我可以提取特定的节点与

xmllint --xpath "//inner" input.xml 
xmllint --xpath "//@status" input.xml 

,但他们只是没有考虑或者返回的节点的status值或只返回属性没有周围节点。

有没有办法用xpath表达来做到这一点?如果没有的话,简单的解决方案也包含了其他的bash工具。

+2

您需要一个XSLT不XPath的,你知道,当XPath返回节点与状态=“失败”有像内部状态的内部元件= '通过'你也会得到。 – SomeDude

像@svasa在评论中说的,你应该使用XSLT。您可以轻松地xsltproc处理在bash的XSLT,xmlstarlet(使用tr命令),Saxon(在command line JAVA)等

下面是一个使用xsltproc的一个例子:

$ xsltproc so.xsl so.xml 
<?xml version="1.0"?> 
<rootnode> 
    <section id="1" status="fail"> 
    <outer status="fail"> 
     <inner status="fail"/> 
    </outer> 
    <outer status="fail"/> 
    </section> 
    <section id="2" status="fail"> 
    <outer status="fail"> 
     <inner status="fail"/> 
    </outer> 
    </section> 
</rootnode> 

XML输入( so.xml)

<rootnode> 
    <section id="1" status="fail"> 
     <outer status="fail"> 
      <inner status="fail"/> 
      <inner status="pass"/> 
     </outer> 
     <outer status="pass"> 
      <inner status="pass"/> 
     </outer> 
     <outer status="pass"/> 
     <outer status="fail"/> 
    </section> 
    <section id="2" status="fail"> 
     <outer status="fail"> 
      <inner status="pass"/> 
      <inner status="fail"/> 
      <inner status="inc"/> 
     </outer> 
    </section> 
</rootnode> 

XSLT 1.0(so.xsl)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <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="*[@status[not(normalize-space()='fail')]]"/> 

</xsl:stylesheet> 

我有一个小的后续问题,如果你不介意的话。当 input.xml文件不包含任何status = fail节点时,则输出 只是两行:<?xml version="1.0"?><rootnode/>。在这种情况下,是否可能有两个完全抑制输出的 ?这不是 确实是一个问题,我知道如何在bash中解决它。我只是 如果有通过xslt干净的解决方案感兴趣。

你可以做的是省略了XML声明(omit-xml-declaration="yes"xsl:output),并检查是否有与status="fail"任何元素。我会使用这个键(xsl:key)...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output indent="yes" omit-xml-declaration="yes"> 
    <!--If you need to output the declaration when there 
    are elements with status="fail", it might be best to post process files that 
    only contain the xml declaration.--> 
    </xsl:output> 
    <xsl:strip-space elements="*"/> 

    <!--Key of all elements with status="fail".--> 
    <xsl:key name="fails" match="*[@status='fail']" use="@status"/> 

    <xsl:template match="/*[not(key('fails','fail'))]"> 
    <!--If there aren't any elements with status="fail", don't process 
    anything else.--> 
    </xsl:template> 

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

    <xsl:template match="*[@status[not(normalize-space()='fail')]]"/> 

</xsl:stylesheet> 
+0

我有一个小问题,如果你不介意的话。当input.xml文件不包含任何'status = fail'节点时,输出只有两行:''和''。在这种情况下,是否有可能完全抑制输出?这不是一个真正的问题,我知道如何在bash中解决它。我只是感兴趣,如果有一个干净的解决方案,通过xslt。 – nautical

+1

@nautical - 请参阅我的编辑一个选项。 –

+1

谢谢。非常感激。 – nautical