我有一些XML,其中有重复的元素具有不同的文本。重复的元素是连续的和非连续的。我试图合并这些元素的文本,并删除重复的元素。选择的工具是xmlstarlet
(在bash
的osx
)。
输入:
<wrapper>
<data>
<item_b>fun</item_b>
<item_a>foo</item_a>
<item_a>bar</item_a>
<item_b>times</item_b>
</data>
</wrapper>
期望的输出:
<wrapper>
<data>
<item_a>foo bar</item_a>
<item_b>fun times</item_b>
</data>
</wrapper>
我要做的是使用xmlstarlet tr
command用XSLT进行转换。
然后,您可以使用Muenchian Grouping按名称对元素进行分组。
例...
XML输入(test.xml;基于注释中的问题编辑)
<wrapper>
<data>
<item_b>fun</item_b>
<item_a>foo</item_a>
<ignore>bad</ignore>
<item_a>bar</item_a>
<item_b>times</item_b>
<ignore>times</ignore>
</data>
<data>
<item_a>Uh oh should be</item_a>
<item_a>in own element</item_a>
</data>
</wrapper>
XSLT 1.0(test.xsl)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="items" match="data/*" use="concat(generate-id(..),name())"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each select="*[not(self::ignore)][count(.|key('items',concat(generate-id(..),name()))[1])=1]">
<xsl:sort select="name()"/>
<xsl:copy>
<xsl:apply-templates select="key('items',concat(generate-id(..),name()))"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="data/*">
<xsl:if test="position() > 1">
<xsl:text> </xsl:text>
</xsl:if>
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
xmlstarlet命令行
xmlstarlet tr test.xsl test.xml
XML输出
<wrapper>
<data>
<item_a>foo bar</item_a>
<item_b>fun times</item_b>
</data>
<data>
<item_a>Uh oh should be in own element</item_a>
</data>
</wrapper>
Daniel的XSLT解决方案将是最好的解决方案。但是,我喜欢让编程语言为我关心XML的细节。 Ruby非常适合处理XML:
gem install xml-simple
ruby -e '
require "xmlsimple"
data = XmlSimple.xml_in(ARGV.shift, {"keeproot" => true})
items = data["wrapper"][0]["data"][0]
items.each_key {|n| items[n] = [ items[n].join(" ") ]}
out = XmlSimple.xml_out(data, {"keeproot" => true})
puts out
' file.xml
<wrapper>
<data>
<item_b>fun times</item_b>
<item_a>foo bar</item_a>
</data>
</wrapper>
我在评论中看到你要过滤掉一些标签(把所有要求都放在你的问题中!)。在items.each_key
之前添加此行:
items.select! {|name, value| name.start_with? "item"}