Home > Back-end >  Transform RNG using XSLT 1.0
Transform RNG using XSLT 1.0

Time:10-19

I am trying to transform a RNG file into xml with XSLT. My source file models a tree structure with references to create a recursive graph. The file looks like this:

<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0">
    <rng:start combine="choice">
        <rng:ref name="Root"/>
    </rng:start>
    <rng:define name="Root">
        <rng:element name="Root">
            <rng:ref name="Root1"/>
        </rng:element>
    </rng:define>
    <rng:define name="Root1">
        <rng:element name="Object">
            <rng:ref name="Object1"/>
        </rng:element>
    </rng:define>
    <rng:define name="Object1">
        <rng:element name="Name">
            <rng:ref name="Name1"/>
        </rng:element>
        <rng:oneOrMore>
            <rng:element name="Object">
                <rng:ref name="Object2"/>
            </rng:element>
        </rng:oneOrMore>
        <rng:element name="Object">
            <rng:ref name="Object3"/>
        </rng:element>
    </rng:define>
    <rng:define name="Object2">
        <rng:element name="Name">
            <rng:ref name="Name2"/>
        </rng:element>
    </rng:define>
    <rng:define name="Object3">
        <rng:element name="Name">
            <rng:ref name="Name3"/>
        </rng:element>
    </rng:define>
    <rng:define name="Name1">
        <rng:value>Value level 1</rng:value>
    </rng:define>
    <rng:define name="Name2">
        <rng:value>Value level 1.1</rng:value>
    </rng:define>
    <rng:define name="Name3">
        <rng:value>Value level 1.2</rng:value>
    </rng:define>
</rng:grammar> 

I would like to get as a result an xml file that presents the tree structure with nodes that fit into each other like this:

<Root>
    <Object>
        <Name>Value level 1</Name>
        <Object>
            <Name>Value level 1.1</Name>
        </Object>
        <Object>
            <Name>Value level 1.2</Name>
        </Object>
    </Object>
</Root>

I'm trying to use xsl:key to store child node references but it doesn't work. Here is my stylesheet:

<xsl:strip-space elements="*"/>
    <xsl:output indent="yes" method="xml" encoding="utf-8" omit-xml-declaration="yes"/>
    <xsl:key name="Object" match="rng:element[@name = 'Object']/rng:ref" use="@name"/>
    <xsl:key name="Name" match="rng:element[@name = 'Name']/rng:ref" use="@name"/>    

    <xsl:template match="/">
        <xsl:apply-templates select="//rng:define"/>
    </xsl:template>

    <xsl:template match="rng:define">
        <Level1>
            <Name>
                <xsl:value-of select="key('Object', ./@name)/rng:define/key('Name', ./@name)/rng:Value"/>
            </Name>
        </Level1>
    </xsl:template>

I also tried to use xsl:variable to store node references but that doesn't work either. Do you have any idea? Thanks in advance.

CodePudding user response:

I am not familiar with the RELAX NG schema. I took a guess at what the logic behind your XML structure might be. Perhaps I was right or perhaps it's just a coincidence, but the following stylesheet:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rng="http://relaxng.org/ns/structure/1.0"
exclude-result-prefixes="rng">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="child" match="rng:define" use="@name" />

<xsl:template match="/rng:grammar">
    <xsl:apply-templates select="key('child', rng:start/rng:ref/@name)"/>
</xsl:template>

<xsl:template match="rng:element">
    <xsl:element name="{@name}">
        <xsl:apply-templates select="key('child', rng:ref/@name)"/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet> 

produces:

Result

<?xml version="1.0" encoding="UTF-8"?>
<Root>
   <Object>
      <Name>Value level 1</Name>
      <Object>
         <Name>Value level 1.1</Name>
      </Object>
      <Object>
         <Name>Value level 1.2</Name>
      </Object>
   </Object>
</Root>

which I believe is the result you're after.

CodePudding user response:

Thank you very much, it works very well. But in fact, I need to categorize the tree levels of the Objects. Something like that :

<Root>
   <Level1>
      <Name>Value level 1</Name>
      <Level2>
         <Name>Value level 1.1</Name>
      </Level2>
      <Level2>
         <Name>Value level 1.2</Name>
      </Level2>
   </Level1>
</Root> 

I tried to add a condition in the XSLT code propagated by @michael.hor257k

<xsl:template match="rng:element">
        <xsl:for-each select="@name = 'Object'">
            <Level1>
                <xsl:apply-templates select="key('child', rng:ref/@name)"/>
            </Level1>
        </xsl:for-each>

But that doesn't work either. Thanks in advance

  • Related