[Webinar] Streamline your web hosting managementRegister Today

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 268
  • Last Modified:

XML Style Sheet that generates unique names from sibling nodes with identical names

Dear Experts:

We interface with an e-commerce site where Sales Orders are generated and exported to an XML file.  We transform that xml file, using a style sheet, that generates a second xml file.
This file is then imported to a relational db.  The  following code is a paired down example of the XML file that we receive, and must transform.  The problem comes in when we have to deal with the custom fields that may, or may not be, included in the XML file we receive.

In the element <customDocFields>,  we have up to six custom <field> elements that may be included in the XML we receive.  I say "may be", because these are user-defined fields and may be intialized.  If initalized, they appear in the XML, otherwise they do not.

We can have up to three <field> elements with a field type of "reseller_q" and three with a field type of "company_q".  Although the element tag ,<customDocfields> will always appear in the XML file we receive, the <field> elements may or may not.

What I am hoping to do, is ALWAYS generate six elements in our OUTPUT xml file, even when they don't all exist in the SOURCE xml file.  

<field> elements with a fieldType of "reseller_q", need to be mapped to child elements of the <docHeader> element with unique element names of their own.  The same is true for the <field> elements with a fieldType of "company_q".

What we need in our transformed XML file, is standardized output of six elements for the custom field names, and six field elements for the custom field text.  Again, whether they are in the SOURCE xml file or not.

It appears that I need some kind of for-each loop that generates 3 elements for each category of custom field.  Somehow the loop counter number is concatenated to the element name so we end up with:

  <reseller_q1_name> and also
  <reseller_q1_data>
  <company_q1_name>
  <company_q1_data>
 
  along with q2 and q3 elements for each of the above.

I have the same issue with the line items, but if someone can help me get over the hump understanding this, I believe I can apply the same concept there.

Any direction is greatly appreciated.

This is the Source XML file:
 
<?xml version="1.0" encoding="UTF-8"?>
<export_documents_response >
<documents>
 <document documentNumber="64794" documentType="Sales Order">
  <customDocFields>
   <field name="Cost Center" fieldType="reseller_q">"4600-000"</field>
   <field name="Invoice Number" fieldType="reseller_q">"0043001"</field>
   <field name="Shipping Notes" fieldType="reseller_q"/>
   <field name="Job ID" fieldType="company_q"/>
  </customDocFields>
  <itemList>
   <product lineItemNumber="1" productId="M004675190" >
    <customLineFields>
     <field name="Department" fieldType="reseller_qp"/>
     <field name="Field Test 2" fieldType="reseller_qp"/>
     <field name="Field Test 3" fieldType="reseller_qp"/>
     <field name="Contract Type" fieldType="company_qp"/>
     <field name="Length" fieldType="company_qp"/>
    </customLineFields>
   </product>
 </itemList>
 </document>
</documents>
</export_documents_response>
 
The data below is what we are trying to achieve.
 
<documents>
 <document> <docHeader>
   <reseller_q1_name>"Cost Center"</reseller_q1_name>
   <reseller_q1_data>"4600-000"</reseller_q1_data>
   <reseller_q2_name>"Invoice Number"</reseller_q2_name>
   <reseller_q2_data>"0043001"</reseller_q2_data>
   <reseller_q3_name>"Shipping Notes"</reseller_q3_name>
   </reseller_q3_data>
   <company_q1_name>"Job ID"</company_q1_name>
   </company_q1_data>
   </company_q2_name>
   </company_q2_data>
   </company_q3_name>
   </company_q3_data>
  </docHeader>
  <docLines>
   <reseller_qp1_name>"Department"</reseller_qp1_name>
   </reseller_qp1_data>
   <reseller_qp2_name>"Field Test 2"</reseller_qp2_name>
   </reseller_qp2_data>
   <reseller_qp3_name="Field Test 3"</reseller_qp3_name>
   </reseller_qp3_data>
  </docLines>
 <document>
</documents>
 
 
Below is the style sheet we currently use to transpose the SOURCE XML file.  We run into problems when there are multiple child nodes of the <customDocFields> and/or
<customLineFields> that have the same field type.
 
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="export_documents_response">
<data>
<xsl:for-each select="documents/document">
 <docHeader>
  <documentNumber><xsl:value-of select="@documentNumber"/></documentNumber>
  <customDocFields_fieldType_reseller_q_name><xsl:value-of select=" _
    customDocFields/field /@fieldType='reseller_q']/@name"/> _
   </customDocFields_fieldType_reseller_q_name>
</docHeader>
</xsl:for-each>
<xsl:for-each select="documents/document/itemList/product">
 <docLines>
  <documentNumber><xsl:value-of select="../../@documentNumber"/></documentNumber>
  <customLineFields_fieldType_reseller_qp_name><xsl:value-of select="customLineFields/field _
    [@fieldType='reseller_qp']/@name"/> 
  </customLineFields_fieldType_reseller_qp_name>
  <customLineFields_fieldType_reseller_qp> _
   <xsl:value-of select="customLineFields/field[@fieldType='reseller_qp']"/>
   </customLineFields_fieldType_reseller_qp>
 </docLines>
</xsl:for-each>
</data>
</xsl:template>
</xsl:stylesheet>

Open in new window

0
csaintaz
Asked:
csaintaz
  • 2
  • 2
1 Solution
 
Geert BormansInformation ArchitectCommented:
There is a way to use lookup tables in XSLT
For that you need to allow the document() function (that is sometimes switched off, depending on the library you use)
Using such a lookup table in the XSLT (look at the map: namespace in my example) you can use document('') to point inside the XSLT,
you can actually build an iterator, without recursion (that would be the alternative)

I have built part of the solution,
now you can either copy or put everything in a named template

good luck

Geert
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:map="urn:internal:xslt:iterator"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     exclude-result-prefixes="map">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="export_documents_response">
        <data>
            <xsl:apply-templates/>
        </data>
    </xsl:template>
    <xsl:template match="documents">
        <documents>
            <xsl:apply-templates select="document"/>
        </documents>
    </xsl:template>
    <xsl:template match="document">
        <document>
            <docHeader>
                <documentNumber><xsl:value-of select="@documentNumber"/></documentNumber>
                <xsl:variable name="this-doc" select="."/>
                <xsl:for-each select="document('')//map:iterator/map:value">
                    <xsl:variable name="this-param"><xsl:value-of select="."/></xsl:variable>
                    <xsl:element name="reseller_q{$this-param}_name">
                        <xsl:value-of select="$this-doc/customDocFields/field[@fieldType = 'reseller_q'][position() = $this-param]/@name"/>
                    </xsl:element>
                    <xsl:element name="reseller_q{$this-param}_data">
                        <xsl:value-of select="$this-doc/customDocFields/field[@fieldType = 'reseller_q'][position() = $this-param]"/>
                    </xsl:element>
                </xsl:for-each>
                <xsl:for-each select="document('')//map:iterator/map:value">
                    <xsl:variable name="this-param"><xsl:value-of select="."/></xsl:variable>
                    <xsl:element name="company_q{$this-param}_name">
                        <xsl:value-of select="$this-doc/customDocFields/field[@fieldType = 'company_q'][position() = $this-param]/@name"/>
                    </xsl:element>
                    <xsl:element name="company_q{$this-param}_data">
                        <xsl:value-of select="$this-doc/customDocFields/field[@fieldType = 'company_q'][position() = $this-param]"/>
                    </xsl:element>
                </xsl:for-each>
            </docHeader>
            <xsl:apply-templates select="itemList/product"/>
        </document>
    </xsl:template>
    <xsl:template match="temList/product">
        <docLines>
            <documentNumber><xsl:value-of select="../../@documentNumber"/></documentNumber>
<!--            <customLineFields_fieldType_reseller_qp_name><xsl:value-of select="customLineFields/field _
                [@fieldType='reseller_qp']/@name"/> 
            </customLineFields_fieldType_reseller_qp_name>
            <customLineFields_fieldType_reseller_qp> _
                <xsl:value-of select="customLineFields/field[@fieldType='reseller_qp']"/>
            </customLineFields_fieldType_reseller_qp>
-->        </docLines>
    </xsl:template>
 
    <map:iterator>
        <map:value>1</map:value>
        <map:value>2</map:value>
        <map:value>3</map:value>
    </map:iterator>
    
    
</xsl:stylesheet>

Open in new window

0
 
csaintazAuthor Commented:
Thanks Geert,

I will give it a try and let you know how it works out.
0
 
csaintazAuthor Commented:
Geert,
The solution you provided worked great.  I really appreciate  the time you spent.  Thamk you.
0
 
Geert BormansInformation ArchitectCommented:
welcome
0

Featured Post

Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

  • 2
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now