Я бы использовал команду 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>