Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

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

Posted on 2009-03-30
4
Medium Priority
?
266 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 2000 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

Setting up LaraDock for Laravel

Learn how to set up LaraDock in a Laravel project - LaraDock gives us an easy way to run a Laravel application using Docker in a single command.

Question has a verified solution.

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

Styling your websites can become very complex. Here I'll show how SASS can help you better organize, maintain and reuse your CSS code.
Without even knowing it, most of us are using web applications on a daily basis.  In fact, Gmail and Yahoo email, Twitter, Facebook, and eBay are used by most of us daily—and they are web applications. We generally confuse these web applications to…
The viewer will learn the benefit of using external CSS files and the relationship between class and ID selectors. Create your external css file by saving it as style.css then set up your style tags: (CODE) Reference the nav tag and set your prop…
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…

704 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