从xmlstarlet输出

时间:2016-11-10 21:27:44

标签: xml xpath namespaces xmlstarlet xmllint

背景

希望从以下XML内容中提取元素:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:ui="http://java.sun.com/jsf/facelets">
    <h:inputText id="id"/>
    ...
</ui:composition>

提取

可以使用以下方式选择所有h:inputText元素:

xmlstarlet sel -t -c "//h:inputText" filename.xml

问题

这会产生以下名称空间感染的输出:

<h:inputText
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets" id="id"/>

问题

如何从输出中抑制名称空间?

使用正则表达式进行后期处理;但是:

  • sed没有非贪婪的比赛;
  • perl太重了(并且需要复杂的正则表达式)。

通过xmllint或xmlstarlet传递第二遍,但这需要格式良好的XML文档。

使用xmllint会产生一组自己的命名空间问题。

制作仅包含ui:compositionh:inputText元素的文档:

<ui:composition
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets">
  <h:inputText id="id"/>
  <h:inputText id="id"/>
</ui:composition>

这很棘手,因为h:inputText元素可以出现在文档的任何深度。

3 个答案:

答案 0 :(得分:1)

您可以使用XSLT。如果您希望按原样输出h:inputText,则无法取消将前缀h:绑定到uri http://java.sun.com/jsf/html的名称空间声明。

XSLT 1.0

创建input.xsl

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:h="http://java.sun.com/jsf/html">
  <xsl:output omit-xml-declaration="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/">
    <xsl:apply-templates select="//h:inputText"/>
  </xsl:template>

  <xsl:template match="h:inputText">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

xmlstarlet命令

xmlstarlet tr input.xsl filename.xml

输出

<h:inputText xmlns:h="http://java.sun.com/jsf/html" id="id"/>

您可以在没有命名空间的情况下输出inputText,但是......

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:h="http://java.sun.com/jsf/html" exclude-result-prefixes="h">
  <xsl:output omit-xml-declaration="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/">
    <xsl:apply-templates select="//h:inputText"/>
  </xsl:template>

  <xsl:template match="h:inputText">
    <inputText>
      <xsl:copy-of select="@*"/>
    </inputText>
  </xsl:template>

</xsl:stylesheet>

输出

使用上面的命令行:

<inputText id="id"/>

注意:您可能需要在<xsl:text>&#xA;</xsl:text>(或第二个示例中为</xsl:copy>)之后添加</inputText>以明确添加换行符。否则xmlstartlet可能会输出一行上的所有元素。 (我在indent="yes"上使用xmlstarlet 1.6.1和xsl:output并没有帮助我。)

JSF输出

由于涉及JSF,请考虑:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:c="http://java.sun.com/jsp/jstl/core"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:a4j="http://richfaces.org/a4j"
                exclude-result-prefixes="h f c ui a4j">
    <xsl:output method="xml" omit-xml-declaration="yes" />
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <h:html>
            <xsl:apply-templates select="//h:inputText"/>
        </h:html>
        <xsl:text>&#xA;</xsl:text>
    </xsl:template>

    <xsl:template match="h:inputText">
        <xsl:text>&#xA;</xsl:text>
        <h:inputText>
            <xsl:copy-of select="@*"/>
        </h:inputText>
        <xsl:text>&#xA;</xsl:text>
    </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:1)

一个 XSLT-stylesheet 解决方案已经发布了一段时间,但是通过 我最近在 xmlstarlet 1.6.1 版上进行的实验 产生所需输出的命令行同上 <inputText id="id"/>,

xmlstarlet sel -N = -t -m '//h:inputText' -e '{local-name()}' -c '@*' -b -n file.xml

其中 -N = 似乎将空前缀绑定到空命名空间。

如果您将 <f:inputText id="id"/><ui:inputText id="id"/> 添加到 输入文件并将上面命令中的 -m 子句更改为 -m '//f:inputText | //h:inputText | //ui:inputText' 它产生 每个匹配节点所需的输出。这将是一个回旋处, 和详细的,在 exclude-result-prefixes="f h ui" 上做的方式 命令行。

不出所料,user's guide 没有提到 -N 的这种用法,源代码的 parseNSArr(…) 也没有提供任何线索。 也许是有意为之——他们怎么会没有注意到呢? - 也许不是: -N = 语法看起来有些可疑。但我肯定 远离 sed -e 's/ xmlns.*=".*"//g' 中列出的方法 user's guide

答案 2 :(得分:0)

<块引用>

sed 没有非贪婪匹配

这还是太贪心了吗?

sed -e 's/ xmlns[^=]*="[^"]*"//g'