Link to home
Start Free TrialLog in
Avatar of paragpjoshi
paragpjoshiFlag for United Kingdom of Great Britain and Northern Ireland

asked on

XSL Transformation

Hi,

I have an Input XML File - "Input XML File.xml".  This file contains a) Product/Product Group Hierarchy, and b) Individual Products within each Product Group.

Each Product Group XML tag defines the list of products within that group.  (E.g. "Nokia Handsets" product group has handsets Nokia E61 and Nokia E71)

I would like to use XSL and convert the input file into two separate output files as follows:

1. Output XML File - ProductGroups.xml: Containing the Product Groups

I will use the XML file using my application to display a product hierarchy as below.  [Please note, that I need help only in creating the Output XML file, the application already takes care of the rest.  The below hierarchy is shown just to give the overall picture]

E.g.

-> Handsets
   --> Nokia Handsets
       - Nokia E61
       - Nokia E71
   --> Samsung Handsets
       - Samsung Wave
       - Samsung Galaxy
-> Accesories
   --> Car Chargers
       - Nokia Car Chargers
       - Samsung Car Chargers


2. Output XML File - Products.xml: Containing individual products.

PS - I am attaching sample Input and expected Output files for ready reference.

Please could you help me with the XSL transformation to do this.

I appreciate your help!

Thanks,
PJoshi
Avatar of sweetfa2
sweetfa2
Flag of Australia image

Sample input and expected output files?
Avatar of paragpjoshi

ASKER

Not sure how the attachments are missing!

I am attaching the files again.

Thanks,
PJoshi
Input-XML-File.xml
Output-XML-File---ProductGroups.xml
Output-XML-File---Products.xml
Avatar of Gertone (Geert Bormans)
Here is your first XSLT

The next one is very similar and you can do that yourself, no?
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:output indent="yes"/>
    <xsl:template match="release">
        <ProductGroupList>
            <xsl:apply-templates select="productDefinition[@type='Product_Group']"/>
        </ProductGroupList>
    </xsl:template>
    <xsl:template match="productDefinition">
        <ProductGroup>
            <ProductGroupID><xsl:value-of select="attribute[@name = 'GroupID']"/></ProductGroupID>
            <ParentProductGroupID><xsl:value-of select="attribute[@name = 'ParentGroupID']"/></ParentProductGroupID>
            <Description><xsl:value-of select="@name"/></Description>
        </ProductGroup>
    </xsl:template>
</xsl:stylesheet>

Open in new window

Well, there is an issue with the second output XML
There is no relation in the XML that indicates that the Nokia E61 belongs to the Nokia Handsets
I think you are missing identifiers in your XML
If this is a relational database export,
make sure you copy the keys
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:output indent="yes"/>
    <xsl:template match="release">
        <ProductGroupList>
            <xsl:apply-templates select="productDefinition[@type='Device' or @type='Accessory']"/>
        </ProductGroupList>
    </xsl:template>
    <xsl:template match="productDefinition">
        <Product>
            <ProductID><xsl:value-of select="attribute[@name = 'Barcode']"/></ProductID>
            <ProductGroupID>???</ProductGroupID>
            <Description><xsl:value-of select="@name"/></Description>
            <StartDate><xsl:value-of select="attribute[@name = 'StartDate']"/></StartDate>
            <ReturnPeriod><xsl:value-of select="attribute[@name = 'Return Period']"/></ReturnPeriod>
        </Product>
    </xsl:template>
</xsl:stylesheet>

Open in new window

The relation between Nokia and Nokia E61 is to be established from the following tag in the Input XML.  Is this possible?

    <productDefinition name="Nokia Handsets" type="Product_Group">
        <attribute name="GroupID">Nokia Handsets</attribute>
        <attribute name="ParentGroupID">Handsets</attribute>
        <prodRef name="Nokia E61" type="Device" />
        <prodRef name="Nokia E71" type="Device" />
    </productDefinition>

Thanks for your help!
My bad, I did not spot the prodrefs, sorry about that, hang on a minute
ASKER CERTIFIED SOLUTION
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi,

Thank you very much for this.  It worked!  However, two minor issues which I observed.

1.  I could redirect the output into the ProductGroup.xml output file.  However, the Product.xml output file is not getting generated (even though output can be seen on console).  

2. For ProductId=1234567890444 (Samsung Galaxy), the ProductGroup (Samsung Handsets) does not get populated in the output file.

Thanks once again,
PJoshi
1. I consider that outside this question. It means that whatever architecture you have to get this done, is redirecting the stream to the wrong place.
So you need to closely look into that yourself

2. I know, that is an error in your source XML
 <prodRef name="Samgung Galaxy" type="Device" />
Look at that. The @name has a typo (missing 's')
Hi,

Apologies, I am probably asking a very silly question.

Where is the missing 's' ?

Sorry, but I cant locate the missing 's'!

Thanks,
PJoshi
"Samgung" should be "Samsung" in the @name of the prodRef element
Oh yes!  I can't believe I did not spot that!  Thanks once again!

Could you help as to how to create two files?  I am not able to create the Product output file.  I am only able to create the productgroup xml.  

PS - I am accepting the solution.

Thank a lot once again,
Pjoshi
Well, without more context, I can't help you.
There are a zillion ways for running an XSLT.
You need to be aware that you need two parallel execution steps,
so, which ever way you run the XSLT, you need to do that twice.

Can you tell me more about the architecture?
Hi,  I have been able to successfully create both the output files.  Thanks for your help!

I had to parse the input xml twice.  Once for product xml and once for product group xml.  Is it possible to create both the output files with only one parse?

Thanks,
Pjoshi
Hi Pjoshi,

Still I don't have the context to answer that question.
I am still unaware on how you do the transforms.

XSLT2 has a concept of writing multiple files in one pass.
But that will mean rewriting the XSLTs completely

You have 2 XSLTs and expect 2 output files.
You should launch two processes.
But depending on how you execute the XSLT,
you can avoid having to parse the source twice.
And you could compile the XSLTs

but for that, I need a lot more detail
Hi,

I am attaching the XSLT that I am using to create the two output files.  I am having to parse the input file twice to create the two output files.

1. Is there any way you could suggest to do this in one parse?

2. Also, one more question (not related to this one, so let me know if you want me to raise this as a new question).  How do I modify the XSLT to output only 1000 products in one file (i.e. create new Product Output file for every 1000 products)

Thanks for your help in advance!

Thanks,
PJoshi
Create-Product-Files.txt
I don't need the XSLT for knowing what you are doing, I need the process that executes the XSLT
Hi,

Apologies! Its my fault! I did not realise that you wanted the process that executes the XSLT.

Please find attached Java program which uses the XSLT to do the transformation and create the output files.

Thanks,
PJoshi
ProductTransformer.java
you could restructure your ProductTransformer method so that it takes 2 XSL as a parameter,
make two transformers in the method and run both transformers on the same input
instead of using the output stream System.out, use two file references for the output, soit gets piped in the right output file
that way, you have only one call to the method and you only need to parse the input file once

I am not a Java programmer, so you need to do that bit yourself
(I just now realised you were doing this in Java)

To answer your other question.
Usual approach for that is
- First (by using selectNodes with an XPath that gets the nodes) you can count (without XSLT) how many nodes there are in the document
- Then calculate how many calls you need to make to teh XSLT (eg. if you have 2559 records, you need a call for 1-1000, 1001-2000, 2000-2559)
- pass in two parameters eg. 1,1000 for the first call
- in the XSLT select the nodes with the lower and upper boundary as found in the parameters

The bulk of the work is in the Java, not in the XSLT for that
Aha, I see you are using Xalan, did not spot the redirect:open and redirect:write before.
Does that work? I mean, did you pick the code up somewhere, without realising that you are not using Xalan?
Or did you test the redirect to work?

In case you are successfully using redirect, you can merge the two XSLTs that I did before and create all the stuff in one go

please let me know:
- that you are using Xalan and can safely use redirect:open (if you currently don't you can bind the java to it

please also send me the two XSLTs as you currnetly use them, so I can wrap them together

No need to change the java then