3

I have a source xml that contains the addresses in spot and need to transform into an xml that holds all addresses into a single element and references each one. I am using Saxon 9.1 processor and stylesheet version 1.0. Thank you for helping.

Source Code:

<?xml version="1.0" encoding="utf-8"?>
<ContactDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <AddressDetails StartDate="1992-04-03" Type="Previous">
        <Address>
            <City City="Wien" />
            <Postcode Postcode="LSP-123" />
        </Address>
    </AddressDetails>
    <AddressDetails StartDate="1982-09-19" Type="Current">
        <Address>
            <City City="Toronto" />
            <Postcode Postcode="LKT-947" />
        </Address>
    </AddressDetails>
    <AddressDetails StartDate="1977-05-27" Type="Mailing">
        <Address>
            <City City="Sydney" />
            <Postcode Postcode="OKU-846" />
        </Address>
    </AddressDetails>
</ContactDetails>

Target Code:

<?xml version="1.0" encoding="utf-8"?>
<Application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <ContactDetails>
        <AddressDetails StartDate="1992-04-03" Type="Previous">
            <AddressRef ReferedID="Prev_1" />
        </AddressDetails>
        <AddressDetails StartDate="1982-09-19" Type="Current">
            <AddressRef ReferedID="Curr_2" />
        </AddressDetails>
        <AddressDetails StartDate="1977-05-27" Type="Mailing">
            <AddressRef ReferedID="Mail_3" />
        </AddressDetails>
    </ContactDetails>
    <AddressSegment>
            <Address>
                <ID ID="Prev_1" />
                <City City="Wien" />
                <Postcode Postcode="LSP-123" />
            </Address>
            <Address>
                <ID UniqueID="Curr_2" />
                <City City="Toronto" />
                <Postcode Postcode="LKT-947" />
            </Address>        
            <Address>
                <ID UniqueID="Mail_3" />
                <City City="Sydney" />
                <Postcode Postcode="OKU-846" />
            </Address>        
    </AddressSegment>
</Application>

Have played with key and generate-id as I was trying to Generate the ID's first and copy them in the address. Here is my last trial of the xslt (best result I got was to have the UniqueID's empty so I have no idea how far off this solution is :) )

<?xml version='1.0' ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="ID_key" match="*[@ReferedID]" use="@ReferedID"/>
<xsl:template match="/">
    <Application>
    <ContactDetails>
        <xsl:for-each select="Application/Person/ContactDetails/AddressDetails">
        <AddressDetails>
            <xsl:attribute name="StartDate">
            <xsl:value-of select="@StartDate"/>
            </xsl:attribute>
                        <xsl:attribute name="Type">
                            <xsl:value-of select="@Type" />
            </xsl:attribute>
                <AddressRef>
            <xsl:attribute name="ReferedID">
                <xsl:value-of select="generate-id()"/>
            </xsl:attribute>
            </AddressRef>
        </AddressDetails>
        </xsl:for-each>
    </ContactDetails>
    <AddressSegment>
        <xsl:for-each select="Application/Person/ContactDetails/AddressDetails">
        <Address>
            <ID>
            <xsl:attribute name="UniqueID">
                <xsl:value-of select="Address/ID[generate-id()=generate-id(key('ID_key',@UniqueID))]" />
            </xsl:attribute>
            </ID>
            <City>
            <xsl:attribute name="City">
                <xsl:value-of select="Address/City/@City"/>
            </xsl:attribute>
            </City>
                        <Postcode>
                                    <sl:attribute name="Postcode">
                    <xsl:value-of select="Address/Postcode/@Postcode"/>
                    </xsl:attribute>
            </Postcode>
        </Address>
            </xsl:for-each>
     </AddressSegment>
   </Application>
   </xsl:template>
</xsl:stylesheet>
4
  • If you use an XSLT 2.0 processor like Saxon 9.1, why are you working with XSLT version 1.0? And what identifies an Address, could there duplicates you need to identify and merge into single records in the resulting XML? Commented Jul 7, 2011 at 14:18
  • +1 for a good and detailed question. Commented Jul 7, 2011 at 23:12
  • Answer to first question is I have no idea. I am barely acquainted to xslt yet. I started trying to solve each specific case at a time, so i guess that was the default on the editor i used. Hardly can go into what the differences are between the 2 standards. Would appreciate tips on documentation on the subject. Second, An address will always have an unique identifier and no merging is necessary. And definitely not least thank you very much for helping. Commented Jul 11, 2011 at 8:53
  • Thank you to everybody. Learned quite a bit just by going through the answers trying to understand exactly what each line does. Commented Jul 11, 2011 at 9:07

3 Answers 3

2

To give you an example of how you could use generate-id and modes, the sample stylesheet

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

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

  <xsl:template match="ContactDetails">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <ContactDetails>
        <xsl:apply-templates select="AddressDetails/Address" mode="det"/>
      </ContactDetails>
      <AddressSegment>
        <xsl:apply-templates select="AddressDetails/Address"/>
      </AddressSegment>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Address" mode="det">
    <AddressDetails StartDate="{../@StartDate}" Type="{../@Type}">
      <AddressRef ReferedID="{generate-id()}"/>
    </AddressDetails>
  </xsl:template>

  <xsl:template match="Address">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <ID ID="{generate-id()}"/>
      <xsl:copy-of select="*"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

transforms the input

<?xml version="1.0" encoding="utf-8"?>
<ContactDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <AddressDetails StartDate="1992-04-03" Type="Previous">
        <Address>
            <City City="Wien" />
            <Postcode Postcode="LSP-123" />
        </Address>
    </AddressDetails>
    <AddressDetails StartDate="1982-09-19" Type="Current">
        <Address>
            <City City="Toronto" />
            <Postcode Postcode="LKT-947" />
        </Address>
    </AddressDetails>
    <AddressDetails StartDate="1977-05-27" Type="Mailing">
        <Address>
            <City City="Sydney" />
            <Postcode Postcode="OKU-846" />
        </Address>
    </AddressDetails>
</ContactDetails>

with Saxon 6.5.5 into the output

<ContactDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <ContactDetails>
      <AddressDetails StartDate="1992-04-03" Type="Previous">
         <AddressRef ReferedID="d0e3"/>
      </AddressDetails>
      <AddressDetails StartDate="1982-09-19" Type="Current">
         <AddressRef ReferedID="d0e7"/>
      </AddressDetails>
      <AddressDetails StartDate="1977-05-27" Type="Mailing">
         <AddressRef ReferedID="d0e11"/>
      </AddressDetails>
   </ContactDetails>
   <AddressSegment>
      <Address>
         <ID ID="d0e3"/>
         <City City="Wien"/>
         <Postcode Postcode="LSP-123"/>
      </Address>
      <Address>
         <ID ID="d0e7"/>
         <City City="Toronto"/>
         <Postcode Postcode="LKT-947"/>
      </Address>
      <Address>
         <ID ID="d0e11"/>
         <City City="Sydney"/>
         <Postcode Postcode="OKU-846"/>
      </Address>
   </AddressSegment>
</ContactDetails>
Sign up to request clarification or add additional context in comments.

2 Comments

A minor remark: <xsl:apply-templates select="@*"/> creates unwanted text node(s) in case the current node has attributes. Worth correcting.
Yes, you are right, guess I started out intending to provide an identity template. I will edit the code sample and use copy-of for the attributes.
0

The following stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*" />
    <xsl:output indent="yes" />
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="ContactDetails">
        <Application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <ContactDetails>
                <xsl:apply-templates select="AddressDetails" />
            </ContactDetails>
            <AddressSegment>
                <xsl:apply-templates select="AddressDetails/Address"
                    mode="ref" />
            </AddressSegment>
        </Application>
    </xsl:template>
    <xsl:template match="AddressDetails/Address">
        <AddressRef ReferedID="{generate-id()}" />
    </xsl:template>
    <xsl:template match="AddressDetails/Address" mode="ref">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <ID ID="{generate-id(../*)}" />
            <xsl:apply-templates select="node()" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

On this input:

<?xml version="1.0" encoding="utf-8"?>
<ContactDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <AddressDetails StartDate="1992-04-03" Type="Previous">
        <Address>
            <City City="Wien" />
            <Postcode Postcode="LSP-123" />
        </Address>
    </AddressDetails>
    <AddressDetails StartDate="1982-09-19" Type="Current">
        <Address>
            <City City="Toronto" />
            <Postcode Postcode="LKT-947" />
        </Address>
    </AddressDetails>
    <AddressDetails StartDate="1977-05-27" Type="Mailing">
        <Address>
            <City City="Sydney" />
            <Postcode Postcode="OKU-846" />
        </Address>
    </AddressDetails>
</ContactDetails>

Produces the desired result:

<Application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <ContactDetails>
        <AddressDetails StartDate="1992-04-03" Type="Previous">
            <AddressRef ReferedID="d1e3" />
        </AddressDetails>
        <AddressDetails StartDate="1982-09-19" Type="Current">
            <AddressRef ReferedID="d1e7" />
        </AddressDetails>
        <AddressDetails StartDate="1977-05-27" Type="Mailing">
            <AddressRef ReferedID="d1e11" />
        </AddressDetails>
    </ContactDetails>
    <AddressSegment>
        <Address>
            <ID ID="d1e3" />
            <City City="Wien" />
            <Postcode Postcode="LSP-123" />
        </Address>
        <Address>
            <ID ID="d1e7" />
            <City City="Toronto" />
            <Postcode Postcode="LKT-947" />
        </Address>
        <Address>
            <ID ID="d1e11" />
            <City City="Sydney" />
            <Postcode Postcode="OKU-846" />
        </Address>
    </AddressSegment>
</Application>

Note the use of the Identity Transform, the most fundamental transformation.

Comments

0

Create Unique IDs and reference them in the same document

If you need to create unique ID for an element, use thegenerate-id() function. Do note that this function generates a unique identifier for a given element in the input document. Therefore if you call the function on the same element, you'll always obtain the same ID. This is really what you want.

For simplicity, in the following example transform, I've applied the templates to AddressDetails two times, each time with a different mode.

Do note the correct generation and reference of the id in the output by applying the generate-id() function on the AddressDetails node.


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

    <xsl:template match="ContactDetails">
        <Application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <ContactDetails>
                <xsl:apply-templates select="AddressDetails" mode="contact"/>
            </ContactDetails>
            <AddressSegment>
                <xsl:apply-templates select="AddressDetails" mode="segment"/>
            </AddressSegment>
        </Application>
    </xsl:template>

    <xsl:template match="AddressDetails" mode="contact">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <AddressRef ReferedID="{generate-id(.)}"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="AddressDetails" mode="segment">
        <Address>
            <ID ID="{generate-id(.)}"/>
            <xsl:copy-of select="Address/*"/>
        </Address>
    </xsl:template>

</xsl:stylesheet>

When applied to the input provided in the question, returns:

<Application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <ContactDetails>
      <AddressDetails StartDate="1992-04-03" Type="Previous">
         <AddressRef ReferedID="d1e3"/>
      </AddressDetails>
      <AddressDetails StartDate="1982-09-19" Type="Current">
         <AddressRef ReferedID="d1e13"/>
      </AddressDetails>
      <AddressDetails StartDate="1977-05-27" Type="Mailing">
         <AddressRef ReferedID="d1e23"/>
      </AddressDetails>
   </ContactDetails>
   <AddressSegment>
      <Address>
         <ID ID="d1e3"/>
         <City City="Wien"/>
         <Postcode Postcode="LSP-123"/>
      </Address>
      <Address>
         <ID ID="d1e13"/>
         <City City="Toronto"/>
         <Postcode Postcode="LKT-947"/>
      </Address>
      <Address>
         <ID ID="d1e23"/>
         <City City="Sydney"/>
         <Postcode Postcode="OKU-846"/>
      </Address>
   </AddressSegment>
</Application>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.