XML - 组合和合并元素,同时保留所有元素文本

问题描述 投票:2回答:2

我有一些XML,其中有重复的元素具有不同的文本。重复的元素是连续的和非连续的。我试图合并这些元素的文本,并删除重复的元素。选择的工具是xmlstarlet(在bashosx)。

输入:

<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>
xml bash macos xmlstarlet xmllint
2个回答
3
投票

我要做的是使用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>

0
投票

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"}
© www.soinside.com 2019 - 2024. All rights reserved.