2016-01-19 3 views
1

Я новичок в XSLT и пытаюсь объединить узлы, которые соответствуют ключу (элементу). Я пробовал некоторые другие решения, но не верю себе в правильность своего набора данных.XSLT 2.0 Group/Объединение узлов на основе соответствующих элементов

Вход:

<coll> 
    <rootNode> 
    <Header> 
     <code> 1234 </code> <-- key to match on 
     <name> Name1 </name> 
    </Header> 
    <node2> Any text </node2> 
    <node4> Any data here </node4> 
    <children> 
     <childID> 3456 </childID> 
     <type> Child </type> 
    </children> 
    </rootNode> 
    <rootNode> 
    <Header> 
     <code> 1234 </code> 
     <name> Name1 </name> 
    </Header> 
    <node2> Different Text </node2> 
    <node4> Different data here </node4> 
    <children> 
     <childID> 789 </childID> 
     <type> Parent </type> 
    </children> 
    </rootNode> 
</coll> 

Ожидаемый результат:

<coll> 
    <rootNode> 
    <Header> 
     <code> 1234 </code> 
     <name> Name1 </name> 
    </Header> 
    <node2> Any text </node2> 
    <node4> Any data here </node4> 
    <node2> Different Text </node2> 
    <node4> Different data here </node4> 
    <children> 
     <childID> 3456 </childID> 
     <type> Child </type> 
     <childID> 789 </childID> 
     <type> Parent </type> 
    </children> 
    </rootNode> 
</coll> 

Т.е. совпадение по значению заголовка/кода, а затем слияние, где любой поднод имеет разное значение. Поэтому любые узлы, имеющие одинаковое значение, не дублируются.

Надеюсь, что это имеет смысл, мой первый пост, спасибо!

ответ

2

Вот пример:

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:functx="http://www.functx.com" 
    xmlns:mf="http://example.com/mf" 
    exclude-result-prefixes="xs functx"> 

<xsl:output indent="yes"/> 

<xsl:function name="functx:index-of-node" as="xs:integer*"> 
    <xsl:param name="nodes" as="node()*"/> 
    <xsl:param name="nodeToFind" as="node()"/> 

    <xsl:sequence select=" 
    for $seq in (1 to count($nodes)) 
    return $seq[$nodes[$seq] is $nodeToFind] 
"/> 

</xsl:function> 

<xsl:function name="mf:eliminate-deep-equal-duplicates" as="node()*"> 
    <xsl:param name="nodes"/> 
    <xsl:sequence 
    select="for $node in $nodes 
      return $node[not(some $preceding-node in $nodes[position() lt functx:index-of-node($nodes, $node)] satisfies deep-equal($node, $preceding-node))]"/> 
</xsl:function> 

<xsl:template match="@* | node()"> 
    <xsl:copy> 
    <xsl:apply-templates select="@* , node()"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="coll"> 
    <xsl:copy> 
    <xsl:for-each-group select="rootNode" group-by="Header/code"> 
     <xsl:copy> 
     <xsl:apply-templates select="Header, 
            mf:eliminate-deep-equal-duplicates(current-group()/(* except (Header, children))), 
            children"/> 
     </xsl:copy> 
    </xsl:for-each-group> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="rootNode/children"> 
    <xsl:copy> 
    <xsl:apply-templates select="mf:eliminate-deep-equal-duplicates(current-group()/children/*)"/> 
    </xsl:copy> 
</xsl:template> 

</xsl:stylesheet> 

Что касается объяснения: Опубликованная таблица стилей имеют три шаблона, первые из которых шаблон тождественного преобразования (что позволяет нам копию элементы, которые мы хотим скопировать, выполнив apply-templates на них в других шаблонах), второй соответствующий coll элементов, чтобы создать их мелкую копию, а затем применить текстовую книгу for-each-group на элементах rootNode, сгруппировав их по Header/code, как вы просили. Внутри for-each-group, для каждой группы xsl:copy создает rootNode и заполнит его путем обработки Header элемента первого элемента в группе (таким образом мы получаем только один Header результирующий элемент в каждой группе), детей, за исключением Header и children каждого пункта в группе, в которой нет предыдущего элемента deep-equals в группе и элемента children первого элемента в группе, чтобы обеспечить, чтобы каждая группа получала дочерний элемент children. В шаблоне для этого элемента нам нужно затем убедиться, что мы обрабатываем всех великих детей в текущей группе, у которых нет дубликата deep-equal перед ними.

Я отредактировал длинное выражение в функции mf:eliminate-deep-equal-duplicates, он выбирает эти узлы в последовательности узлов, для которых ни один предшествующий узел в той же последовательности не равен deep-equal.

Решение использует функцию http://www.xsltfunctions.com/xsl/functx_index-of-node.html библиотеки functx, которая дает нам индекс узла в последовательности.

Как Владимир Нестеровского отметил в комментарии, функция mf:eliminate-deep-equal-duplicates также может быть реализована без использования functx functx:index-of-node:

<xsl:function name="mf:eliminate-deep-equal-duplicates" as="node()*"> 
    <xsl:param name="nodes"/> 
    <xsl:sequence 
    select="for $i in (1 to count($nodes)), 
      $node in $nodes[$i] 
      return $node[not(some $preceding-node in $nodes[position() lt $i] satisfies deep-equal($node, $preceding-node))]"/> 
</xsl:function> 
+0

Спасибо! Я не уверен, зачем нужен второй шаблон? Не могли бы вы объяснить две ключевые строки шаблона запроса, поскольку у меня возникают проблемы с тем, как они работают? –

+1

Я отредактировал ответ с исправленной версией и некоторым объяснением. Оказалось, что предыдущее предложение имело правильный подход, но не совсем правильное осуществление, я думаю. Я тестировал несколько выборочных данных с дублирующими элементами, и новое решение устраняет их. –

+0

Awesome спасибо много Мартин, объяснение помогает много –

Смежные вопросы