从 xmlstarlet 输出中删除名称空间

Delete namespace from xmlstarlet output

背景

希望从以下 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"/>

问题

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

想法

使用正则表达式进行post-处理;然而:

通过 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 元素可以出现在文档的任何深度。

您可以使用 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:copy> 之后添加 <xsl:text>&#xA;</xsl:text>(或第二个示例中的 </inputText>)以显式添加换行符。否则 xmlstartlet 可能会在一行中输出所有元素。 (它对我使用 xmlstarlet 1.6.1 和 indent="yes" on 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>

sed doesn't have a non-greedy match

这还是太贪心了吧?

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

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 = 似乎将空前缀绑定到 null 命名空间。

如果您将 <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 does not mention this use of -N, and the source code's parseNSArr(…) 没有提供任何线索。 也许这是设计使然——他们怎么可能没有注意到呢? - 也许不是: -N = 语法看起来有点可疑。但我肯定会 远离 sed -e 's/ xmlns.*=".*"//g' 中列出的方法 user's guide.