XML - 分组和合并元素,同时保留所有元素文本
XML - group and merge elements, whilst keeping all element text
我有一些 XML,其中重复的元素具有不同的文本。重复的元素既有连续的也有不连续的。我正在尝试合并这些元素中的文本,并删除重复的元素。选择的工具是 xmlstarlet
(在 osx
上的 bash
中)。
输入:
<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>
我在评论中看到您想过滤掉一些标签(在您的问题中输入 所有 要求!)。添加此行 before items.each_key
:
items.select! {|name, value| name.start_with? "item"}
我有一些 XML,其中重复的元素具有不同的文本。重复的元素既有连续的也有不连续的。我正在尝试合并这些元素中的文本,并删除重复的元素。选择的工具是 xmlstarlet
(在 osx
上的 bash
中)。
输入:
<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>
我在评论中看到您想过滤掉一些标签(在您的问题中输入 所有 要求!)。添加此行 before items.each_key
:
items.select! {|name, value| name.start_with? "item"}