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> </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> </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> </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> </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
CodePudding user response:
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> </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> </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.