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"}