1

I'm going to transform the structure of this xml file to another one (with new element names) by taking some data from there, namely:

  1. Value of all elements;
  2. Value of all and elements from elements;

I'm just started to play with XSLT and have weak knowledge, so don't judge strictly me. My transform.xsl template is (without printing xml element names):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" />
    <xsl:template match="/">
        <xsl:apply-templates select="/RESPONSE/MULTIPLE/SINGLE/KEY" />
    </xsl:template>

    <!-- transformation to another xml file -->
    <xsl:template match="/MULTIPLE">
    <course>
    <xsl:for-each select="SINGLE">
        <topic>
            <!-- Updated -->
            <chapter><xsl:value-of select="KEY[@name='name']/VALUE" /></chapter>
            <xsl:for-each select="KEY[@name='modules']/MULTIPLE/SINGLE">
                <title><xsl:value-of select="KEY[@name='name']/VALUE" /></title>
                <content><xsl:value-of select="KEY[@name='description']/VALUE" /></content>
            </xsl:for-each>
            <!-- /Updated -->
        </topic>
    </xsl:for-each>
    </course>
</xsl:template>

Expected stucture is [updated]:

<?xml version="1.0" encoding="UTF-8"?>
<course>
    <topic>
        <chapter>Chapter Name 1</chapter>
        <title>Title Name 1</title>
        <content>Content 1</content>
    </topic>
    <!-- Updated -->
    <topic>
        <chapter>Chapter Name 1</chapter>    <!-- print for each <title> and <content> -->
        <title>Title Name 2</title>
        <content>Content 2</content>
    </topic>
    <topic>
        <chapter>Chapter Name n</chapter>
        <title>Title Name n</title>
        <content>Content n</content>
    </topic>
    <!-- Updated -->
    ...
</course>

and php procedure:

$xml = new DOMDocument;
$xml->load("http://dl.dropbox.com/u/72519118/response.xml");

$xsl = new DOMDocument;
$xsl->load("transform.xsl");

// Configure the transformer
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);

echo $proc->transformToXML($xml);

Any help would be appreciated.

0

1 Answer 1

1

Thank you for your well-presented question. You're on the right track, but there are a few issues:

Since you want to apply templates to the top-level multiples, your first apply-templates should look like this:

<xsl:apply-templates select="/RESPONSE/MULTIPLE" />

Since MULTIPLE is not the root element, your second template won't match anything if the match element value starts with a slash. This is what you should use:

<xsl:template match="MULTIPLE">

And when you want to compare something (attribute, element, etc.) against a string value, you need to put quotes around the value:

<xsl:value-of select="KEY[@name = 'name']/VALUE" />

Once those elements are fixed, you get this XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" />
  <xsl:template match="/">
    <xsl:apply-templates select="/RESPONSE/MULTIPLE" />
  </xsl:template>

  <!-- transformation to another xml file -->
  <xsl:template match="MULTIPLE">
    <course>
      <xsl:for-each select="SINGLE">
        <topic>
          <chapter>
            <xsl:value-of select="KEY[@name = 'name']/VALUE" />
          </chapter>
          <title>
            <xsl:value-of 
                select="KEY[@name= 'modules']/MULTIPLE/SINGLE/KEY
                                       [@name = 'name']/VALUE" />
          </title>
          <content>
            <xsl:value-of 
                select="KEY[@name= 'modules']/MULTIPLE/SINGLE/KEY
                                 [@name ='description']/VALUE" />
          </content>
        </topic>
      </xsl:for-each>
    </course>
  </xsl:template>
</xsl:stylesheet>

And here is an updated version to meet your clarified requirements:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" />
  <xsl:template match="/">
    <course>
      <xsl:apply-templates 
         select="/RESPONSE/MULTIPLE/SINGLE/KEY[@name = 'modules']/MULTIPLE/SINGLE" />
    </course>
  </xsl:template>

  <xsl:template match="SINGLE">
    <topic>
      <chapter>
        <xsl:value-of select="../../../KEY[@name = 'name']/VALUE" />
      </chapter>
      <title>
        <xsl:value-of select="KEY[@name = 'name']/VALUE" />
      </title>
      <content>
        <xsl:value-of select="KEY[@name ='description']/VALUE" />
      </content>
    </topic>
  </xsl:template>
</xsl:stylesheet>

Note that this simply applies templates to all of the second-level <SINGLE>s. In order to get the chapter name, it moves back up the XML tree to get that value from a parent node.

And the output when run on your source XML is:

<course>
  <topic>
    <chapter>General</chapter>
    <title>News forum</title>
    <content></content>
  </topic>
  <topic>
    <chapter>ANATOMIE</chapter>
    <title>1.1 Die Haut</title>
    <content>
      &lt;div class="no-overflow"&gt;&lt;p&gt;&lt;span class="nolink"&gt;&lt;img src="http://localhost/pluginfile.php/22/mod_page/intro/die_haut.png" width="auto" style="border: 1px       [SNIP]
    </content>
  </topic>
  <topic>
    <chapter>ANATOMIE</chapter>
    <title>1.2 Der Schädel Page</title>
    <content>
      &lt;div class="no-overflow"&gt;&lt;h3&gt;1.2 Der Schädel&lt;/h3&gt;
      [SNIP]
    </content>
  </topic>
  <topic>
    <chapter>ANATOMIE</chapter>
    <title>1.6 Die Regelkreise</title>
    <content>
      &lt;div class="no-overflow"&gt;&lt;h3&gt;1.6 Die Regelkreise&lt;/h3&gt;
      [SNIP]
    </content>
  </topic>
  <topic>
    <chapter>ANATOMIE</chapter>
    <title>Media</title>
    <content></content>
  </topic>
  <topic>
    <chapter>NOTFÄLLE</chapter>
    <title>2.1 Neurologische Notfälle</title>
    <content></content>
  </topic>
  <topic>
    <chapter>NOTFÄLLE</chapter>
    <title>2.5 Krampfanfälle</title>
    <content></content>
  </topic>
  <topic>
    <chapter>NOTFÄLLE</chapter>
    <title>2.9 Pulmonale Notfälle</title>
    <content></content>
  </topic>
  <topic>
    <chapter>STÖRUNGEN</chapter>
    <title>3.1 Störungen der Lebensfunktionen bei Erwachsenen (ab der Pubertät)</title>
    <content></content>
  </topic>
  <topic>
    <chapter>STÖRUNGEN</chapter>
    <title>3.16 Störungen der Lebensfunktionen bei Säuglingen (bis ein Jahr) und Kindern (bis zur Pubertät)</title>
    <content></content>
  </topic>
  <topic>
    <chapter>STÖRUNGEN</chapter>
    <title>3.25 Starke Blutung</title>
    <content></content>
  </topic>
</course>
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks a lot, that's really what I want. Just one additional question in order to take into account for my future playing with xml processing, i know that there is a variety of methods and solutions, but what is best solution to transform (parse) for cases like above? By the way, above mentioned response.xml is reduced version, normally it is around 10-30 Mb file and of course i want to work with that solution, which is fastest one in transforming or parsing such xml files. thank you in advance
Forgot to mention, the method which you proposed is taking only first SINGLE elemnts of each <KEY name='modules'> elements, if you look at the original response.xml there are other SINGLE elements as well (means each chapter may have two or more topics). Should I add another for-each before above elements or?
In terms of performance, I think either of the solutions above should be about equally fast, because they're both pretty simple. Regarding handling multiple SINGLES under the modules group, it's not clear from your example how you would like that represented in the output XML, so could you explain that? Could you give an example of how that result should look? I'm heading to bed for now, but I can update my answer in about 7 hours once you've clarified this point.
Thank you for your explanation about the solution in terms of performance. I've updated the XSL Template and Expected xml output (marked with Updated comments). So, now I'm getting all 'titles' and 'contents' of a particular 'chapter', which prints only one time at the top. What I want is to put everytime 'cahpter' at the top of each 'title' and 'content', then put them all together into separate 'topic' elements (look at the updated version of expected xml output).
Ok, I've updated my answer. Please try the second XSLT in my post (not the first, which I haven't changed). I believe it does what you're describing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.