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