#xml #xslt #merge
#xml #xslt #слияние
Вопрос:
У меня есть несколько XML-элементов, которые фактически ссылаются на один и тот же «объект», они могут иметь одни и те же данные или могут иметь разные данные. У любого могут быть элементы, которых нет у других. Я хочу, чтобы мой xsl выводил сумму элементов всех этих xml-«объектов». Мы не будем беспокоиться о перекрытии. Как вы можете выполнить подобное слияние с помощью XSL?
Пример:
<root>
<object>
<property1>value</property1>
</object>
<object>
<property1>value</property1>
<property2>value</property2>
</object>
...
<object>
<propertyN>value</propertyN>
</object>
</root>
Результат:
<root>
<object>
<property1>value</property1>
<property2>value</property2>
...
<propertyN>value</propertyN>
</object>
</root>
Комментарии:
1. Сначала вы говорите, что вам нужна «сумма» всех элементов, затем вы говорите, что вас не будет беспокоить «перекрытие». Я нахожу это противоречивым. Если у вас есть N /root/object/property1, что вы хотите в качестве результата?
2. Если есть 2 «свойства1», я не беспокоюсь о том, что там происходит (пока), это может быть комбинация, первая или последняя. Я не беспокоюсь об этом сейчас, потому что для этого нужно будет написать что-то более сложное. Сначала я убираю «простую» часть. Под простым я подразумеваю часть без конфликтов.
3. @aepeus. Хорошо, является ли XSLT строгим требованием? Вероятно, гуру XSLT могут это сделать, но я бы выбрал библиотеку, похожую на DOM.
Ответ №1:
Это преобразование применяется к вашему sample .xml
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:ext="www.foo.com">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<root>
<object>
<xsl:for-each select="/root/object//*">
<xsl:copy-of select="."/>
</xsl:for-each>
</object>
</root>
</xsl:template>
</xsl:stylesheet>
Выдает этот вывод :
<?xml version="1.0" encoding="UTF-8"?>
<root>
<object>
<property1>value</property1>
<property1>value</property1>
<property2>value</property2>
<propertyN>value</propertyN>
</object>
</root>
Хотя позже вы, вероятно, захотите сделать что-то лучше, это должно заставить вас двигаться дальше.
Ответ №2:
Это может быть достигнуто группировкой по Меншиану.
Сначала определите ключ, соответствующий элементам свойств (в данном случае я предполагаю, что все дочерние узлы объекта являются свойствами), используя имя в качестве справочного.
Далее вам нужно сопоставить первое вхождение каждого имени свойства, проверив, что это первое такое свойство в справочном ключе
<xsl:apply-templates
select="//object/*[generate-id() = generate-id(key('property', local-name())[1])]" />
Затем вы можете просто скопировать такие совпадения.
Вот полный XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="property" match="object/*" use="local-name()"/>
<xsl:template match="/">
<object>
<xsl:apply-templates select="//object/*[generate-id() = generate-id(key('property', local-name())[1])]"/>
</object>
</xsl:template>
<xsl:template match="*">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
При применении к вашему образцу XML вывод выглядит следующим образом:
<object>
<property1>value</property1>
<property2>value</property2>
<propertyN>value</propertyN>
</object>
В этом случае возвращается только первое вхождение имени свойства. Если вы хотите объединить элементы, вы могли бы, например (предполагая, что все значения были числовыми), суммировать их
<xsl:template match="*">
<xsl:copy>
<xsl:value-of select="sum(key('property', local-name()))" />
</xsl:copy>
</xsl:template>
Итак, учитывая этот входной XML….
<root>
<object>
<property1>1</property1>
</object>
<object>
<property1>2</property1>
<property2>3</property2>
</object>
...
<object>
<propertyN>4</propertyN>
</object>
</root>
Результат был бы таким…
<object>
<property1>3</property1>
<property2>3</property2>
<propertyN>4</propertyN>
</object>
Комментарии:
1. Спасибо, это, скорее всего, тоже окажется полезным.