Solved

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

Posted on 2009-03-30
4
265 Views
Last Modified: 2013-11-18
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
Comment
Question by:csaintaz
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 2
  • 2
4 Comments
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 500 total points
ID: 24021970
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
 

Author Comment

by:csaintaz
ID: 24023525
Thanks Geert,

I will give it a try and let you know how it works out.
0
 

Author Closing Comment

by:csaintaz
ID: 31564418
Geert,
The solution you provided worked great.  I really appreciate  the time you spent.  Thamk you.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24040808
welcome
0

Featured Post

Get 15 Days FREE Full-Featured Trial

Benefit from a mission critical IT monitoring with Monitis Premium or get it FREE for your entry level monitoring needs.
-Over 200,000 users
-More than 300,000 websites monitored
-Used in 197 countries
-Recommended by 98% of users

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

What is Node.js? Node.js is a server side scripting language much like PHP or ASP but is used to implement the complete package of HTTP webserver and application framework. The difference is that Node.js’s execution engine is asynchronous and event…
Introduction Knockoutjs (Knockout) is a JavaScript framework (Model View ViewModel or MVVM framework).   The main ideology behind Knockout is to control from JavaScript how a page looks whilst creating an engaging user experience in the least …
The viewer will learn the basics of jQuery including how to code hide show and toggles. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery…
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …
Suggested Courses

621 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question