1

I am in phase of learning XSLT,

Following is my XML

    <?xml version="1.0" encoding="UTF-8"?>
     <catalog>
     <cd>
      <title>xyz</title>
      <artist>pqr</artist>
      <country>USA</country>
     </cd>
      <cd>
      <title>abc</title>
      <artist>def</artist>
      <country>France</country>
     </cd>

</catalog>

Output Expected

  Title |Artist |Country
  xyz |pqr |USA
  abc |def |France

My First attempt is to read the title.

 <?xml version="1.0" encoding="UTF-8"?>
 <xsl:stylesheet version="1.0" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="title">
   <xsl:value-of select="catalog/cd/title"/>
   </xsl:template>
 </xsl:stylesheet>

But the display is

      pqr 
      USA
     
      
      
      def
      France

I am just trying to read the title here. Don't understand what is wrong . I am sure I have to use looping and concatenation to achieve the output.

Can someone help?

Thank you Regards Prat


Edit New xml

  <?xml version='1.0' encoding='UTF-8'?>
   <multimap:Messages xmlns:multimap="http://sap.com/xi/XI/SplitAndMerge">
    <multimap:Message1>
     <Products>
      <Product>
        <OrderID>9000625868</OrderID>
      </Product>
     </Products>
</multimap:Message1>
<multimap:Message2>
 <CustomerOrderCollection>
    <CustomerOrder>
        <ZZCASE_KUT/>
        <DivisionCodeText>Test</DivisionCodeText>
        <ApprovalStatusCode/>
        <DateTime>2022-06-07T12:06:25.068</DateTime>
        <TaxAmount>0.000000</TaxAmount> 
    </CustomerOrder>
    <CustomerOrder>
        <ZZCASE_KUT/>
        <DivisionCodeText>Test2</DivisionCodeText>
        <ApprovalStatusCode/>
        <DateTime>2022-06-08T12:06:25.068</DateTime>
        <TaxAmount>10.000000</TaxAmount>    
    </CustomerOrder>

Output desired

   ZZCASE_KUT|DivisionCodeText|ApprovalStatusCode|DateTime|TaxAmount
    |Test||2022-06-07T12:06:25.068|0.000000
    |Test1||2022-06-08T12:06:25.068|10.000000

XSLT i used

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

   <xsl:variable name="delimiter" select="' |'"/>

   <xsl:template match="CustomerOrderCollection">
    <xsl:for-each select="CustomerOrder[1]/*">
        <xsl:value-of select="concat(
            translate(substring(local-name(), 1, 1), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') , 
            substring(local-name(), 2))"/>
        <xsl:if test="following-sibling::*">
            <xsl:value-of select="$delimiter"/>
        </xsl:if>
    </xsl:for-each>
    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select="*" />
  </xsl:template>

  <xsl:template match="CustomerOrder">
    <xsl:for-each select="*">
        <xsl:value-of select="."/>
        <xsl:if test="following-sibling::*">
            <xsl:value-of select="$delimiter"/>
        </xsl:if>
    </xsl:for-each>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>  

 </xsl:stylesheet>

Output is very different form expected.

               9000625868
      
     


 ZZCASE_KUT |DivisionCodeText |ApprovalStatusCode |DateTime |TaxAmount
 |Test | |2022-06-07T12:06:25.068 |0.000000
 |Test2 | |2022-06-08T12:06:25.068 |10.000000

Version 3

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

     <xsl:variable name="delimiter" select="' |'"/>

    <xsl:template match="/">
       <xsl:apply-templates select="/*/*/CustomerOrderCollection" />

     <xsl:for-each select="CustomerOrder[1]/*">
    <xsl:value-of select="concat(
        translate(substring(local-name(), 1, 1), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') , 
        substring(local-name(), 2))"/>
    <xsl:if test="following-sibling::*">
        <xsl:value-of select="$delimiter"/>
    </xsl:if>
  </xsl:for-each>
  <xsl:text>&#10;</xsl:text>
  <xsl:apply-templates select="*" />
 </xsl:template>

 <xsl:template match="CustomerOrder">
   <xsl:for-each select="*">
    <xsl:value-of select="."/>
    <xsl:if test="following-sibling::*">
        <xsl:value-of select="$delimiter"/>
    </xsl:if>
 </xsl:for-each>
 <xsl:text>&#10;</xsl:text>
 </xsl:template>  

</xsl:stylesheet>

Output

     |0E-14 | | |2022-06-07T12:06:25.068 |0.000000 | |1





    
        9000625868
    




     |0E-14 | | |2022-06-07T12:06:25.068 |0.000000 | |1
2
  • I think spending a few moments with an XSLT tutorial would greatly help you. Commented Jul 4, 2022 at 13:00
  • 1
    The key mistake here is that you need to understand the concept of the context item. In a template rule with match='title', any relative path expressions select starting at the title, so to get the value of the title you want select=".", not select="catalog/cd/title". Commented Jul 4, 2022 at 15:12

1 Answer 1

2

It is important to know that there are built-in template rules that will be applied to the content. Unless you provide your own that override the default behavior, XSLT will match on any node() apply-templates to the children, and text() will return their value. Therefore, if all you did was execute the stylesheet, it will generate output will all of the text().

Now, for your current template, you are matching on title elements, and then attempt to get the value-of with a relative XPath expression that is looking for catalog/cd/title that are children of the title (there are none, so it produces nothing. This has the effect of filtering out all of the matched title, rather than producing the desired output. If you wanted to select the value-of that title, you could select . (the context node).

To produce your desired output, you would want a stylesheet like:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" />
    
    <xsl:variable name="delimiter" select="' |'"/>
    
    <xsl:template match="catalog">
        <xsl:for-each select="cd[1]/*">
            <xsl:value-of select="concat(
                translate(substring(local-name(), 1, 1), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') , 
                substring(local-name(), 2))"/>
            <xsl:if test="following-sibling::*">
                <xsl:value-of select="$delimiter"/>
            </xsl:if>
        </xsl:for-each>
        <xsl:text>&#10;</xsl:text>
        <xsl:apply-templates select="*" />
    </xsl:template>
    
    <xsl:template match="cd">
        <xsl:for-each select="*">
            <xsl:value-of select="."/>
            <xsl:if test="following-sibling::*">
                <xsl:value-of select="$delimiter"/>
            </xsl:if>
        </xsl:for-each>
        <xsl:text>&#10;</xsl:text>
    </xsl:template>  

</xsl:stylesheet>

For your second XML, you could add a template matching the root node, and push the CustomerOrderCollection, bypassing all of the built-in template rules that would be processing those multimap elements before they hit on CustomerOrderCollection:

<xsl:template match="/">
  <xsl:apply-templates select="/*/*/CustomerOrderCollection" />
</xsl:template>

You could further consolidate and avoid some duplication by using named templates and modes, but this demonstrates one way to achieve the desired output.

Sign up to request clarification or add additional context in comments.

7 Comments

I cannot understand why you are willing to hardcode the names of the catalog and cd elements, but then go to such great lengths to avoid a simple <xsl:text>Title |Artist |Country&#10;</xsl:text>.
Just demonstrating how to transform things
Columns are more likely to vary in the data than the document element or cd rows elements also.
The multimap:Message1 is being processed by those built-in templates, so it's OrderID/text() is bleeding through. You can bypass and start processing at the CustomerOrderCollection by defining a template on the root node and pushing the CustomerOrderCollection: <xsl:template match="/"> <xsl:apply-templates select="/*/*/CustomerOrderCollection" /> </xsl:template>
You didn't add that root node template, you merged it with the other catalog template.
|

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.