XML - группирует и объединяет элементы, сохраняя весь текст элемента - PullRequest
3 голосов
/ 22 марта 2019

У меня есть несколько XML, где есть повторяющиеся элементы с различным текстом. Повторяющиеся элементы являются как последовательными, так и непоследовательными. Я пытаюсь объединить текст из этих элементов и удалить дубликаты элементов. Инструмент выбора - xmlstarletbash на 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>

Ответы [ 2 ]

3 голосов
/ 22 марта 2019

Я бы использовал команду xmlstarlet tr для преобразования с 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 голосов
/ 22 марта 2019

Лучшим будет решение 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"}
...