0
votes

I'm having a XML document which I transform with XSLT 1.0:

<?xml version="1.0" encoding="UTF-8"?>
<element tag="container">
    <data handle="$1">
        ...
    </data>
    <assignments/>
    <expression context="$1">
        Object_Text
    </expression>
    <expression context="$1">
        Object_Text
    </expression>
    <expression context="$1">
        Object_Heading
    </expression>   
    <element tag="container">
        <data handle="$2">
            ...
        </data>
        <assignments/>
        <element tag="container">
            <data handle="$3">
                ...
            </data>
            <assignments/>
            <expression context="$3">
                Object_Text
            </expression>
        </element>
        <element tag="container">
            ...
        </element>
    </element>
    <element tag="container">
        <element tag="container">
            <element tag="container">
                <expression context="$1">
                    Object_Text
                </expression>
            </element>
        </element>  
    </element>
    <element tag="container">
    <expression context="$1">
        Object_Identifier
    </expression>   
    </element>
</element>

I have to add assignments for each expression type (Object_Text, Object_Heading, ...) to the tag of the next parent container node that contains a tag (not all container have that tag, those should be ignored) and the expression's context value has to fit with the handle value of the container. Because of the fact that I need an assignment for each expression type, I only should have one assignment for each type in its context no matter how much it appears. So the desired output is:

<?xml version="1.0" encoding="UTF-8"?>
<element tag="container">
    <data handle="$1">
        ...
    </data>
    <assignments> <!--Added assignments here (one for each type with @context='$1')-->
        <assignment name="Object_Text">
        </assignment>
        <assignment name="Object_Heading">
        </assignment>
        <assignment name="Object_Identifier">
        </assignment>
    </assignments>
    <expression context="$1">
        Object_Text
    </expression>
    <expression context="$1">
        Object_Text
    </expression>
    <expression context="$1">
        Object_Heading
    </expression>   
    <element tag="container">
        <data handle="$2">
            ...
        </data>
        <assignments/>
        <element tag="container">
            <data handle="$3">
                ...
            </data>
            <assignments> <!--Added assignments here (one for each type with @context='$3')-->
                <assignment name="Object_Text">
                </assignment>
            </assignments>
            <expression context="$3">
                Object_Text
            </expression>
        </element>
        <element tag="container">
            ...
        </element>
    </element>

    <element tag="container">
    <expression context="$1">
        Object_Identifier
    </expression>   
    </element>
</element>

Currently I am able to get one group of expression type (Object_Text, etc.) with Muenchian Grouping method. But my problem is that this I am not able to distinguish those expression types by their attribute @context, so that the container just contains assignments for the right expressions which it really contains with the same @context.

I'm grateful for any help. Does someone know a way to achieve the desired output? I've tried many things, but currently lacking experience/knowledge for this task.

EDIT: it should be added that this is a sample structure for a xml document. So the xslt functions should recognize expressions in arbitrarily nested, wrapped structures and in any number of times.

2

2 Answers

0
votes

Let us start by declaring a key for expressions

<xsl:key name="kExp" match="expression" use="concat(@context, '|', normalize-space(.))"/>

under assignments node, we will place the @handle attribute of the first preceding-sibling node data

<xsl:variable name="dataHandle" select="preceding-sibling::data/@handle"/>

when this variable is used in the assignments node and using Muenchian method (I have assumed that the expressions are siblings or descendants of the siblings of assignments node):

    <xsl:template match="assignments">
        <xsl:variable name="dataHandle" select="preceding-sibling::data/@handle"/>
        <xsl:copy>
            <xsl:for-each select="../descendant::expression[@context=$dataHandle 
                                  and generate-id()=generate-id(key('kExp', concat(@context, '|', normalize-space(.)))[1])]">
                <assignment name="{normalize-space(.)}"></assignment>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>

The whole stylesheet is below:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>

    <xsl:key name="kExp" match="expression" use="concat(@context, '|', normalize-space(.))"/>


    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="assignments">
        <xsl:variable name="dataHandle" select="preceding-sibling::data/@handle"/>
        <xsl:copy>
            <xsl:for-each select="../descendant::expression[@context=$dataHandle 
                                  and generate-id()=generate-id(key('kExp', concat(@context, '|', normalize-space(.)))[1])]">
                <assignment name="{normalize-space(.)}"></assignment>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

see it in action in http://xsltransform.net/a9Giwr.

0
votes

This will get you going. You have some suppression going on, that this code does not account for.

  <!-- Handle each container that has an assignments node. -->
  <xsl:template match="element[@tag='container' and .//assignments]">
    <!-- Bind to the expressions for this container. -->
    <xsl:variable name="expressions" select="expression"/>
    <xsl:copy>
      <xsl:apply-templates select="node()|@*">
        <xsl:with-param name="expressions" select="$expressions"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <!-- Create the assignments for each container. -->
  <xsl:template match="assignments">
    <xsl:param name="expressions"/>
    <xsl:copy>
      <xsl:for-each select="$expressions">
        <xsl:element name="assignment">
          <xsl:attribute name="name">
            <xsl:value-of select="normalize-space(.)"/>
          </xsl:attribute>
        </xsl:element>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

  <!-- Identity template. -->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>          
    </xsl:copy>
  </xsl:template>