2

I wanted to transform repeating group from the sample XML into two delimited text lines. Here is my current code version:

string orderXml = 
@"<?xml version='1.0' encoding='utf-8'?>
<Order id='79223510'>
<Status>new</Status>
<ShipMethod>Standard International</ShipMethod>
<ToCity>Tokyo</ToCity>
<Items>
    <Item>
    <SKU>SKU-1234567890</SKU>
    <Quantity>1</Quantity>
    <Price>99.95</Price>
    </Item>
    <Item>
    <SKU>SKU-1234567899</SKU>
    <Quantity>1</Quantity>
    <Price>199.95</Price>
    </Item>
</Items>
</Order>";

StringReader str = new StringReader(orderXml);

var xslt = new XmlTextReader(new StringReader(
@"<?xml version='1.0' encoding='UTF-8'?>" +
"<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>" +

"<xsl:output method='text' indent='no'/>" +

"<!-- New line character variable -->" +
"<xsl:variable name='newline'>" +
"<xsl:text>&#13;&#10;</xsl:text>" +
"</xsl:variable>" +
"<xsl:variable name='empty'>" +
"<xsl:text></xsl:text>" +
"</xsl:variable>" +

"<xsl:template match='/'>" +         

"<xsl:for-each select='Order'>" +
"<xsl:value-of select='@id'/>|" + 
"<xsl:value-of select='Status'/>|" +
"<xsl:value-of select='ShipMetod'/>|" +
"<xsl:value-of select='ToCity'/>|" + 
"<xsl:value-of select='$newline'/>" +
"</xsl:for-each>" +

"<xsl:for-each select='Order/Items/Item'>" +
"<xsl:value-of select='SKU'/>|" +          
"<xsl:value-of select='Quantity'/>|" +   
"<xsl:value-of select='Price'/>|" +    
"<xsl:value-of select='$newline'/>" +
"</xsl:for-each>" + 

"</xsl:template>" +
"</xsl:stylesheet>"
                ));

var xDoc = new XPathDocument(str);
var xTr = new System.Xml.Xsl.XslCompiledTransform();
xTr.Load(xslt);

StringBuilder sb = new StringBuilder();
StringWriter writer = new StringWriter(sb);
xTr.Transform(xDoc, null, writer);

string[] lines = sb.ToString().Split(new string[] {"\n"}, StringSplitOptions.RemoveEmptyEntries);    

lines.ToList().ForEach(System.Console.Write);     

This code currently produces the following output:

79223510|new||Tokyo|
SKU-1234567890|1|99.95|
SKU-1234567899|1|199.95|

and I wanted it to produce the following one:

79223510|new||Tokyo|SKU-1234567890|1|99.95|
79223510|new||Tokyo|SKU-1234567899|1|199.95|

by just using XSL transformation. Please advise.

P.S. Added XSL in a text form for easier reading:

@<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>

<xsl:output method='text' indent='no'/>

<!-- New line character variable -->
<xsl:variable name='newline'>
<xsl:text>&#13;&#10;</xsl:text>
</xsl:variable>
<xsl:variable name='empty'>
<xsl:text></xsl:text>
</xsl:variable>

<xsl:template match='/'>         

<xsl:for-each select='Order'>
<xsl:value-of select='@id'/>| 
<xsl:value-of select='Status'/>|
<xsl:value-of select='ShipMetod'/>|
<xsl:value-of select='ToCity'/>| 
<xsl:value-of select='$newline'/>
</xsl:for-each>

<xsl:for-each select='Order/Items/Item'>
<xsl:value-of select='SKU'/>|          
<xsl:value-of select='Quantity'/>|   
<xsl:value-of select='Price'/>|    
<xsl:value-of select='$newline'/>
</xsl:for-each> 

</xsl:template>
</xsl:stylesheet>
ekad
  • 14,436
  • 26
  • 44
  • 46
ShamilS
  • 1,410
  • 2
  • 20
  • 40

1 Answers1

2

You are making a few mistakes:

  1. You are using two separate for-each loops rather than a loop in a loop, so your first line of output is from the Order element, and the next two are from the Item elements. (Actually you don't need any loops. Solution below.)
  2. You misspelled ShipMetod, so one of your fields is mistakenly blank.
  3. You don't have any method in place to escape any | values that may be present in the field values. (My solution doesn't solve this problem since I'm not sure what you want to do about it.)

A better solution is to use template matching and string-concatenation.

<xsl:stylesheet version="1.0" xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method="text" indent="no" media-type="text/plain" />

<xsl:variable name='newline'><xsl:text>&#13;&#10;</xsl:text></xsl:variable>
<xsl:variable name='delimiter'>|</xsl:variable>

<!-- by default, don't copy any nodes to output -->
<xsl:template match="node()|@*"><xsl:apply-templates select="node()|@*"/></xsl:template>

<xsl:template match="/Order/Items/Item"><xsl:value-of
select="concat(
../../@id, $delimiter,
../../Status, $delimiter,
../../ShipMethod, $delimiter,
../../ToCity, $delimiter,
SKU, $delimiter,
Quantity, $delimiter,
Price, $delimiter,
$newline)"/></xsl:template>

</xsl:stylesheet>

This produces the following output:

79223510|new|Standard International|Tokyo|SKU-1234567890|1|99.95|
79223510|new|Standard International|Tokyo|SKU-1234567890|1|199.95|
Francis Avila
  • 31,233
  • 6
  • 58
  • 96