从 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-处理;然而:
- sed 没有非贪婪匹配;
- perl 太重量级(并且需要复杂的正则表达式)。
通过 xmllint 或 xmlstarlet 进行第二次传递,但这需要格式正确的 XML 文档。
使用 xmllint 会带来一系列命名空间问题。
生成仅包含 ui:composition
和 h: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>
</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>
</xsl:text>
</xsl:template>
<xsl:template match="h:inputText">
<xsl:text>
</xsl:text>
<h:inputText>
<xsl:copy-of select="@*"/>
</h:inputText>
<xsl:text>
</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.
背景
希望从以下 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-处理;然而:
- sed 没有非贪婪匹配;
- perl 太重量级(并且需要复杂的正则表达式)。
通过 xmllint 或 xmlstarlet 进行第二次传递,但这需要格式正确的 XML 文档。
使用 xmllint 会带来一系列命名空间问题。
生成仅包含 ui:composition
和 h: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>
</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>
</xsl:text>
</xsl:template>
<xsl:template match="h:inputText">
<xsl:text>
</xsl:text>
<h:inputText>
<xsl:copy-of select="@*"/>
</h:inputText>
<xsl:text>
</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.