-2

I'll be happy if someone gives me hints how to create xslt. May be muenchian groupping could make it? Is it possible to make code for unlimited path or I have to limit count of path elements? Many thanks :-)

    <DETAILS>
       <DETAIL>
          <PATH>A\B\C\D</PATH>
          <VALUE>Value1<VALUE>
       <DETAIL>
       <DETAIL>
          <PATH>A\B\C\E</PATH>
          <VALUE>Value2<VALUE>
       <DETAIL>
       <DETAIL>
          <PATH>A\B\C\F</PATH>
          <VALUE>Value3<VALUE>
       <DETAIL>
       <DETAIL>
          <PATH>A\C\F</PATH>
          <VALUE>Value4<VALUE>
       <DETAIL>
       <DETAIL>
          <PATH>A\C\G</PATH>
          <VALUE>Value5<VALUE>
       <DETAIL>
    <DETAILS>

==> Convert to

    <A>
      <B>
        <C> 
          <D>Value1</D>
          <E>Value2</E>
          <F>Value3</F>
        </C>
      </B>
      <C> 
          <F>Value4</F>
          <G>Value5</G>
      </C>
     </A>
Bkmz
  • 19
  • 6
  • 1
    Please show your best effort so far. That way, responders will have a better idea what concept you might be struggling with. – Robᵩ Jun 05 '15 at 13:48
  • This is not at all trivial. I am not sure XSLT is the best tool to use here. Can you at least use XSLT 2.0 or are you limited to XSLT 1.0? – michael.hor257k Jun 05 '15 at 14:05
  • I limited xslt to 1.0. – Bkmz Jun 05 '15 at 14:37
  • I do not have best effort for a while. I'm trying to solve problem by fixing path items. With some assumptions I will solve my task, because I have almost fixed input. And it very big, so I will write a lot of monkey code :-( – Bkmz Jun 05 '15 at 14:50

1 Answers1

0

As I said in the comments, this is not simple at all, esp. if limited to XSLT 1.0. My suggestion would be to do this in several steps. The first step would be to convert your input to something like this:

<elems>
  <elem level="1" parent="">A</elem>
  <elem level="2" parent="A">B</elem>
  <elem level="3" parent="B">C</elem>
  <elem level="4" parent="C" value="Value1">D</elem>
  <elem level="1" parent="">A</elem>
  <elem level="2" parent="A">B</elem>
  <elem level="3" parent="B">C</elem>
  <elem level="4" parent="C" value="Value2">E</elem>
  <elem level="1" parent="">A</elem>
  <elem level="2" parent="A">B</elem>
  <elem level="3" parent="B">C</elem>
  <elem level="4" parent="C" value="Value3">F</elem>
  <elem level="1" parent="">A</elem>
  <elem level="2" parent="A">C</elem>
  <elem level="3" parent="C" value="Value4">F</elem>
  <elem level="1" parent="">A</elem>
  <elem level="2" parent="A">C</elem>
  <elem level="3" parent="C" value="Value5">G</elem>
</elems>

In the second step, you create an element for each distinct elem of the 1st level.

Finally, you apply templates recursively to each element's distinct children - i.e. elements that are one level deeper than the current level and their parent's name matches the current element's name.

The following stylesheet uses EXSLT extension functions to tokenize the paths and to select distinct nodes only. If your XSLT processor does not support these functions, you will have to use a named recursive template to do the tokenizing, and Muenchian grouping to select the distinct nodes.

XSLT 1.0 (+EXSLT)

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:set="http://exslt.org/sets"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="exsl set str">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="children" match="elem" use="concat(@parent, '|', @level)" />

<xsl:variable name="elems">
    <xsl:for-each select="/DETAILS/DETAIL">
        <xsl:variable name="value" select="VALUE" />
        <xsl:for-each select="str:tokenize(PATH, '\')">
            <elem level="{position()}" parent="{preceding-sibling::token[1]}">
                <xsl:if test="position()=last()">
                    <xsl:attribute name="value">
                        <xsl:value-of select="$value"/>
                    </xsl:attribute>
                </xsl:if>
                <xsl:value-of select="."/>
            </elem>
        </xsl:for-each>
    </xsl:for-each>
</xsl:variable>

<xsl:variable name="elems-set" select="exsl:node-set($elems)" />

<xsl:template match="/">
    <output>
        <xsl:apply-templates select="set:distinct($elems-set/elem[@level=1])"/>
    </output>
</xsl:template>     

<xsl:template match="elem">
    <xsl:element name="{.}">
        <xsl:value-of select="@value"/>
        <xsl:apply-templates select="set:distinct(key('children', concat(., '|', @level + 1)))"/>
    </xsl:element>
</xsl:template> 

</xsl:stylesheet>

Note that this all based on an assumption that there can be no sibling elements with the same name.

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51