user1772421
user1772421

Reputation: 147

XSL Transform - Multiple Child Nodes = Multiple Relationships

<Records count="1">
  <Metadata>
    <FieldDefinitions>
      <FieldDefinition id="25675" name="GrandpaID" alias="GrandpaID" />
      <FieldDefinition id="123" name="Father ID" alias="FatherID" />
      <FieldDefinition id="1923" name="Son ID" alias="SonID" />
    </FieldDefinitions>
  </Metadata>
  <LevelCounts>
    <LevelCount id="1" count="2" />
    <LevelCount id="2" count="2" />
    <LevelCount id="3" count="3" />
  </LevelCounts>
  <Record contentId="578859" levelId="1" moduleId="648" parentId="0">
    <Record contentId="138286" levelId="2" moduleId="68" parentId="0">
      <Record contentId="107826" levelId="3" moduleId="152" parentId="0">
        <Field id="1923" type="1">Grandson Record 1</Field>
      </Record>
      <Record contentId="107830" levelId="3" moduleId="152" parentId="0">
        <Field id="1923" type="1">Grandson Record 2</Field>
      </Record>
      <Field id="123" type="1">Son Record</Field>
    </Record>
    <Field id="25675" type="6">Grandpa Record</Field>
  </Record>
</Records>

I have the aforementioned XML. What I need to do is look at each "Grandson Record" and create a record for each. That is to say that for each Grandson Record found under Grandpa/Son, I need Grandpa/Son/Grandson 1 and a second one for Grandpa/Son/Grandson 2. I have the following XSLT that gives me both Grandson Records at the same time.

XSLT:

    <?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>

  <xsl:variable name ="fields" select="//Metadata/FieldDefinitions" />

  <!--match the root node-->
  <xsl:template match="Records">
    <ArcherRecords >
      <xsl:apply-templates select="Record" />
    </ArcherRecords>
  </xsl:template>

    <!-- match child relationships -->
  <xsl:template match="Record">
    <xsl:variable name="fieldName" select="translate(@levelId, ': ', '__')" />
    <xsl:element name="Relationship_{$fieldName}">
      <xsl:apply-templates select="@contentId" />
      <xsl:apply-templates select="Field" />
      <xsl:apply-templates select="Record" />
    </xsl:element>
  </xsl:template>

  <!--get field name-->
  <xsl:template name="getName">
    <xsl:param name="fieldId" />

    <xsl:choose>
      <xsl:when test="$fields/FieldDefinition[@id=$fieldId]">        
        <xsl:value-of select="$fields/FieldDefinition[@id=$fieldId]/@alias"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="'Field_'"/>
        <xsl:value-of select="translate(@id, ': ', '__')" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

Outcome:

<?xml version="1.0" encoding="UTF-8"?>
<ArcherRecords xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <Relationship_1>
      <Field_contentId>578859</Field_contentId>
      <GrandpaID>Grandpa Record</GrandpaID>
      <Relationship_2>
         <Field_contentId>138286</Field_contentId>
         <FatherID>Son Record</FatherID>
         <Relationship_3>
            <Field_contentId>107826</Field_contentId>
            <SonID>Grandson Record 1</SonID>
         </Relationship_3>
         <Relationship_3>
            <Field_contentId>107830</Field_contentId>
            <SonID>Grandson Record 2</SonID>
         </Relationship_3>
      </Relationship_2>
   </Relationship_1>
</ArcherRecords>

Desired Outcome:

<?xml version="1.0" encoding="UTF-8"?>
<ArcherRecords xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <Relationship_1>
      <Field_contentId>578859</Field_contentId>
      <GrandpaID>Grandpa Record</GrandpaID>
      <Relationship_2>
         <Field_contentId>138286</Field_contentId>
         <FatherID>Son Record</FatherID>
         <Relationship_3>
            <Field_contentId>107826</Field_contentId>
            <SonID>Grandson Record 1</SonID>
         </Relationship_3>
      </Relationship_2>
   </Relationship_1>
   <Relationship_1>
      <Field_contentId>578859</Field_contentId>
      <GrandpaID>Grandpa Record</GrandpaID>
      <Relationship_2>
         <Field_contentId>138286</Field_contentId>
         <FatherID>Son Record</FatherID>    
            <Relationship_3>
                <Field_contentId>107830</Field_contentId>
                <SonID>Grandson Record 2</SonID>
         </Relationship_3>
      </Relationship_2>
   </Relationship_1>
</ArcherRecords>

Any help would be greatly appreciated!

Upvotes: 0

Views: 878

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 117140

I am probably missing something here, because I can't see why this needs to be so complicated. I believe the following stylesheet will return the requested result, for an input with any number of levels:

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="fieldDefinition" match="FieldDefinition" use="@id" />

<xsl:template match="/Records">
    <ArcherRecords>
        <!-- for each leaf node -->
        <xsl:for-each select=".//Record[not(Record)]">
            <!-- replicate the tree, starting from the top -->
            <xsl:apply-templates select="ancestor::Record[last()]">
                <xsl:with-param name="leafId" select="@contentId"/>
            </xsl:apply-templates>
        </xsl:for-each>
    </ArcherRecords>
</xsl:template>     

<xsl:template match="Record">
    <xsl:param name="leafId"/>
    <xsl:element name="Relationship_{@levelId}">
        <!-- deal with the current level -->
        <Field_contentId>
            <xsl:value-of select="@contentId" />
        </Field_contentId>
        <xsl:element name="{key('fieldDefinition', Field/@id)/@alias}">
            <xsl:value-of select="Field" />
        </xsl:element>
        <!-- continue to the next lower level, branching to current leaf node only -->
        <xsl:apply-templates select="Record[descendant-or-self::Record/@contentId=$leafId]">
            <xsl:with-param name="leafId" select="$leafId"/>
        </xsl:apply-templates>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

Note that we are assuming that each Record has a unique @contentId value.

Upvotes: 1

Related Questions