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工具。
答
像@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>
您需要一个XSLT不XPath的,你知道,当XPath返回节点与状态=“失败”有像内部状态的内部元件= '通过'你也会得到。 – SomeDude