xslt - Follow-up: create (grand)parent-child elements based on delimiter in attribute-values -
hereby followup question posted here last year. still being newbie i'm struggling (again...) transform - using xslt 1.0 - following xml describes objects (note slight change in input - 'b.c.*' - previous question):
<data> <object> <property name="id" value="001"/> <property name="p.id" value="id p"/> <property name="p.description" value="descr p"/> <property name="a.id" value="id a" /> <property name="a.description" value="descr a"/> <property name="b.id" value="id b"/> <property name="b.description" value="descr b"/> <property name="b.c.id" value="b.c.id"/> <property name="b.c.description" value="b.c.description"/> </object> </data>
the following rules should apply desired output:
- for each 'property'-element not contain separator '.' in 'name'-attribute, transform 'name'-attribute child-element , select value of 'value'-attribute.
- for each 'property'-element does contain separator(s) '.' in 'name'-attribute, create:
- a) (grand)parent element using 'substring-before' separator in 'name'-attribute - 'recursively until last occurence' (not sure how describe; see desired output below), and
- b) child element using 'substring-after' last separator in 'name'-attribute , select value of 'value'-attribute.
the desired output should therefore this:
<?xml version="1.0" encoding="utf-8"?> <root> <objectdata> <id>001</id> <p> <id>id p</id> <description>descr p</description> </p> <a> <id>id a</id> <description>descr a</description> </a> <b> <id>id b</id> <description>descr b</description> <c> <id>b.c.id</id> <description>b.c.description</c.description> </c> </b> </objectdata> </root>
currently have following code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform"> <xsl:output indent="yes"/> <xsl:key name="kpropertybyname" match="property[contains(@name, '.')]" use="concat(generate-id(..), '|', substring-before(@name,'.'))"/> <xsl:template match="data"> <root> <xsl:apply-templates/> </root> </xsl:template> <xsl:template match="object"> <objectdata> <xsl:apply-templates select="property[not(contains(@name, '.'))]"/> <xsl:for-each select="property[generate-id(.) = generate-id(key('kpropertybyname', concat(generate-id(..), '|', substring-before(@name,'.')))[1]) ] "> <xsl:apply-templates select="." mode="parent"/> </xsl:for-each> </objectdata> </xsl:template> <xsl:template match="property[not(contains(@name, '.'))]"> <xsl:element name="{@name}"> <xsl:value-of select="@value"/> </xsl:element> </xsl:template> <xsl:template match="property[(contains(@name, '.'))]" mode="parent"> <xsl:element name="{substring-before(@name,'.')}"> <xsl:apply-templates mode="child" select="../property[ substring-before(current()/@name,'.') = substring-before(./@name,'.')]"/> </xsl:element> </xsl:template> <xsl:template match="property[(contains(@name, '.'))]" mode="child"> <xsl:element name="{substring-after(@name,'.')}"> <xsl:value-of select="@value"/> </xsl:element> </xsl:template> </xsl:stylesheet>
which gives me following output - having (unwanted) 'non-separated' c.*-elements:
<?xml version="1.0" encoding="utf-8"?> <root> <objectdata> <id>001</id> <p> <id>id p</id> <description>descr p</description> </p> <a> <id>id a</id> <description>descr a</description> </a> <b> <id>id b</id> <description>descr b</description> <c.id>b.c.id</c.id> <c.description>b.c.description</c.description> </b> </objectdata> </root>
not i'm looking for... appreciated again!
interesting problem, have time solve xslt 2.0:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/xsl/transform" xmlns:xs="http://www.w3.org/2001/xmlschema" xmlns:mf="http://example.com/mf" exclude-result-prefixes="xs mf" version="2.0"> <xsl:output indent="yes"/> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:function name="mf:group" as="element()*"> <xsl:param name="elements" as="element()*"/> <xsl:param name="index" as="xs:integer"/> <xsl:for-each-group select="$elements" group-adjacent="tokenize(@name, '\.')[$index]"> <xsl:choose> <xsl:when test="not(current-group()[2])"> <xsl:apply-templates select="."/> </xsl:when> <xsl:otherwise> <xsl:element name="{current-grouping-key()}"> <xsl:apply-templates select="current-group()[$index = count(tokenize(@name, '\.'))]"/> <xsl:sequence select="mf:group(current-group()[not($index = count(tokenize(@name, '\.')))], $index + 1)"/> </xsl:element> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </xsl:function> <xsl:template match="object"> <xsl:copy> <xsl:sequence select="mf:group(*, 1)"/> </xsl:copy> </xsl:template> <xsl:template match="property[@name]"> <xsl:element name="{tokenize(@name, '\.')[last()]}"> <xsl:value-of select="@value"/> </xsl:element> </xsl:template> </xsl:stylesheet>
Comments
Post a Comment