Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8158199
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 6, 20262026-06-06T17:31:55+00:00 2026-06-06T17:31:55+00:00

This question’s solution makes hard-coded transformation of a sample XML tree into a flat

  • 0

This question’s solution makes hard-coded transformation of a sample XML tree into a flat delimited text file:

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(

    @"<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,
        $newline)'
        />
    </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);     

producing output as the following:

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

Is there a way to produce the same output using generic XSL-transformation traversing source XML tree and concatenating parent nodes and attributes values to the child ones?

Notes:

  1. If there are any attributes of a node then that node will not have a value to concatenate.

  2. If there are several attributes of a node then their values should be concatenated using slash character.

  3. The true generic solution should work and flatten XML trees with more than two hierarchy levels.

Here is another sample document with additional parent node with two attributes:

<Order id='79223510'>
<Status>new</Status>
<ShipMethod>Standard International</ShipMethod>
<ToCity>Tokyo</ToCity>
<Marketplace id="123-45678-9089808" name="MyBooks" />
<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>

And here is a desired delimited flattened text file output:

79223510|new|Standard International|Tokyo|123-45678-908980/MyBooks|SKU-1234567890|1|99.95
79223510|new|Standard International|Tokyo|123-45678-908980/MyBooks|SKU-1234567899|1|199.95

Solution by Dimitre Novatchev working well here for both original sample document and XML document with higher level nodes hierarchy.

    string orderXml = 
//      @"<?xml version='1.0' encoding='utf-8'?>
//      <Order id='79223510'>
//      <Status>new</Status>
//      <ShipMethod>Standard International</ShipMethod>
//      <ToCity>Tokyo</ToCity>
//      <Marketplace id='123-45678-9089808' name='MyBooks'/>
//      <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>";

@"<?xml version='1.0' encoding='utf-8'?>
<Order id='79223510'>
    <Status>new</Status>
    <ShipMethod>Standard International</ShipMethod>
    <ToCity>Tokyo</ToCity>
    <Marketplace id=""123-45678-9089808"" name=""MyBooks"" />
    <Items>
        <Item>
        <X>
            <SKU>SKU-1234567890</SKU>
            <Quantity>1</Quantity>
            <Price>99.95</Price>
        </X>
        <X>
            <SKU>SKU-1234554321</SKU>
            <Quantity>1</Quantity>
            <Price>199.95</Price>
        </X>
        </Item>
        <Item>
        <Y>
            <SKU>SKU-0987654321</SKU>
            <Quantity>1</Quantity>
            <Price>299.95</Price>
        </Y>
        <Y>
            <SKU>SKU-0987667890</SKU>
            <Quantity>1</Quantity>
            <Price>399.95</Price>
        </Y>
        </Item>
    </Items>
</Order>";

StringReader str = new StringReader(orderXml);

var xslt = new XmlTextReader(new StringReader(   
    @"<xsl:stylesheet version='1.0'
        xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
        xmlns:ext='http://exslt.org/common'>
    <xsl:output method='text'/>
    <xsl:strip-space elements='*'/>

        <xsl:param name='pLeafNodes' select=
        '//*[not(*[*])
            and
            (
            name() = name(following-sibling::*[1])
            or
            name() = name(preceding-sibling::*[1])
            )
            ]'/>

        <xsl:template match='/'>
        <xsl:variable name='vrtfPass1'>
            <t>
                <xsl:call-template name='StructRepro'/>
            </t>
        </xsl:variable>

        <xsl:apply-templates mode='pass2'
            select='ext:node-set($vrtfPass1)/*/*' />
        </xsl:template>

    <xsl:template match='Order' mode='pass2'>
    <xsl:apply-templates select='.//@* | .//text()' mode='pass2'/>
    <xsl:text>&#xA;</xsl:text>
    </xsl:template>

    <xsl:template match='@*|text()' mode='pass2'>
    <xsl:if test='not(position()=1) and not(self::text())'>/</xsl:if>
    <xsl:if test='not(position()=1) and self::text()'>|</xsl:if>
    <xsl:value-of select='.'/>
    </xsl:template>

        <xsl:template name='StructRepro'>
        <xsl:param name='pLeaves' select='$pLeafNodes'/>

        <xsl:for-each select='$pLeaves'>
            <xsl:apply-templates mode='build' select='/*'>
            <xsl:with-param name='pChild' select='.'/>
            <xsl:with-param name='pLeaves' select='$pLeaves'/>
            </xsl:apply-templates>
        </xsl:for-each>
        </xsl:template>

        <xsl:template mode='build' match='node()|@*'>
            <xsl:param name='pChild'/>
            <xsl:param name='pLeaves'/>

            <xsl:copy>
            <xsl:apply-templates mode='build' select='@*'/>

            <xsl:variable name='vLeafChild' select=
                '*[count(.|$pChild) = count($pChild)]'/>

            <xsl:choose>
                <xsl:when test='$vLeafChild'>
                <xsl:apply-templates mode='build'
                    select='$vLeafChild
                            |
                            node()[not(count(.|$pLeaves) = count($pLeaves))]'>
                    <xsl:with-param name='pChild' select='$pChild'/>
                    <xsl:with-param name='pLeaves' select='$pLeaves'/>
                </xsl:apply-templates>
                </xsl:when>
                <xsl:otherwise>
                <xsl:apply-templates mode='build' select=
                'node()[not(.//*[count(.|$pLeaves) = count($pLeaves)])
                        or
                        .//*[count(.|$pChild) = count($pChild)]
                        ]
                '>

                    <xsl:with-param name='pChild' select='$pChild'/>
                    <xsl:with-param name='pLeaves' select='$pLeaves'/>
                </xsl:apply-templates>
                        </xsl:otherwise>
                    </xsl:choose>
                    </xsl:copy>
                </xsl:template>
                <xsl:template match='text()'/>
            </xsl:stylesheet>"
                ));


//
// White space cannot be stripped from input documents that have already been loaded. 
// Provide the input document as an XmlReader instead. 
//+
//var xDoc = new XPathDocument(str);
XmlReaderSettings settings;
settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Document;
var xDoc = XmlReader.Create(str, settings);
//-

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);     

//  test output 1
//  79223510|new|Standard International|Tokyo/123-45678-9089808/MyBooks|SKU-1234567890|1|99.95
//  79223510|new|Standard International|Tokyo/123-45678-9089808/MyBooks|SKU-1234567899|1|199.95

// test output 2
//  79223510|new|Standard International|Tokyo/123-45678-9089808/MyBooks|SKU-1234567890|1|99.95
//  79223510|new|Standard International|Tokyo/123-45678-9089808/MyBooks|SKU-1234554321|1|199.95
//  79223510|new|Standard International|Tokyo/123-45678-9089808/MyBooks|SKU-0987654321|1|299.95
//  79223510|new|Standard International|Tokyo/123-45678-9089808/MyBooks|SKU-0987667890|1|399.95 
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-06T17:31:58+00:00Added an answer on June 6, 2026 at 5:31 pm

    This transformation:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:ext="http://exslt.org/common">
         <xsl:output method="text"/>
         <xsl:strip-space elements="*"/>
    
         <xsl:param name="pLeafNodes" select=
         "//*[not(*[*])
            and
             (
              name() = name(following-sibling::*[1])
             or
              name() = name(preceding-sibling::*[1])
              )
             ]"/>
    
         <xsl:template match="/">
          <xsl:variable name="vrtfPass1">
              <t>
                <xsl:call-template name="StructRepro"/>
              </t>
          </xsl:variable>
    
          <xsl:apply-templates mode="pass2"
             select="ext:node-set($vrtfPass1)/*/*" />
         </xsl:template>
    
     <xsl:template match="Order" mode="pass2">
      <xsl:apply-templates select=".//@* | .//text()" mode="pass2"/>
      <xsl:text>&#xA;</xsl:text>
     </xsl:template>
    
     <xsl:template match="@*|text()" mode="pass2">
      <xsl:if test="not(position()=1) and not(self::text())">/</xsl:if>
      <xsl:if test="not(position()=1) and self::text()">|</xsl:if>
      <xsl:value-of select="."/>
     </xsl:template>
    
    
         <xsl:template name="StructRepro">
           <xsl:param name="pLeaves" select="$pLeafNodes"/>
    
           <xsl:for-each select="$pLeaves">
             <xsl:apply-templates mode="build" select="/*">
              <xsl:with-param name="pChild" select="."/>
              <xsl:with-param name="pLeaves" select="$pLeaves"/>
             </xsl:apply-templates>
           </xsl:for-each>
         </xsl:template>
    
          <xsl:template mode="build" match="node()|@*">
              <xsl:param name="pChild"/>
              <xsl:param name="pLeaves"/>
    
             <xsl:copy>
               <xsl:apply-templates mode="build" select="@*"/>
    
               <xsl:variable name="vLeafChild" select=
                 "*[count(.|$pChild) = count($pChild)]"/>
    
               <xsl:choose>
                <xsl:when test="$vLeafChild">
                 <xsl:apply-templates mode="build"
                     select="$vLeafChild
                            |
                              node()[not(count(.|$pLeaves) = count($pLeaves))]">
                     <xsl:with-param name="pChild" select="$pChild"/>
                     <xsl:with-param name="pLeaves" select="$pLeaves"/>
                 </xsl:apply-templates>
                </xsl:when>
                <xsl:otherwise>
                 <xsl:apply-templates mode="build" select=
                 "node()[not(.//*[count(.|$pLeaves) = count($pLeaves)])
                        or
                         .//*[count(.|$pChild) = count($pChild)]
                        ]
                 ">
    
                     <xsl:with-param name="pChild" select="$pChild"/>
                     <xsl:with-param name="pLeaves" select="$pLeaves"/>
                 </xsl:apply-templates>
                </xsl:otherwise>
               </xsl:choose>
             </xsl:copy>
         </xsl:template>
         <xsl:template match="text()"/>
    </xsl:stylesheet>
    

    when applied on the provided XML document:

    <Order id='79223510'>
        <Status>new</Status>
        <ShipMethod>Standard International</ShipMethod>
        <ToCity>Tokyo</ToCity>
        <Marketplace id="123-45678-9089808" name="MyBooks" />
        <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>
    

    produces the wanted, correct result:

    79223510|new|Standard International|Tokyo/123-45678-9089808/MyBooks|SKU-1234567890|1|99.95
    79223510|new|Standard International|Tokyo/123-45678-9089808/MyBooks|SKU-1234567899|1|199.95
    

    Here is a more complicated XML document, which I created based on the provided one:

    <Order id='79223510'>
        <Status>new</Status>
        <ShipMethod>Standard International</ShipMethod>
        <ToCity>Tokyo</ToCity>
        <Marketplace id="123-45678-9089808" name="MyBooks" />
        <Items>
            <Item>
             <X>
                <SKU>SKU-1234567890</SKU>
                <Quantity>1</Quantity>
                <Price>99.95</Price>
             </X>
             <X>
                <SKU>SKU-1234554321</SKU>
                <Quantity>1</Quantity>
                <Price>199.95</Price>
             </X>
            </Item>
            <Item>
             <Y>
                <SKU>SKU-0987654321</SKU>
                <Quantity>1</Quantity>
                <Price>299.95</Price>
             </Y>
             <Y>
                <SKU>SKU-0987667890</SKU>
                <Quantity>1</Quantity>
                <Price>399.95</Price>
             </Y>
            </Item>
        </Items>
    </Order>
    

    When the same transformation is applied on this second document, again the wanted, correct result is produced:

    79223510|new|Standard International|Tokyo/123-45678-9089808/MyBooks|SKU-1234567890|1|99.95
    79223510|new|Standard International|Tokyo/123-45678-9089808/MyBooks|SKU-1234554321|1|199.95
    79223510|new|Standard International|Tokyo/123-45678-9089808/MyBooks|SKU-0987654321|1|299.95
    79223510|new|Standard International|Tokyo/123-45678-9089808/MyBooks|SKU-0987667890|1|399.95
    

    Explanation:

    1. This is a two-pass transformation.

    2. The first pass transforms the source XML document into a shredded one. The solution for generic shredding from this answer is used. Here the most important thing is to specify correctly the “leaf” nodes for the shredding. These are any element node that: 1) doesn’t have children elements that themselves have children element; and 2) whose name is the same as the name of its immediate preceding-sibling element or is the same as the name of its immediate following sibling.

    The intermediate result is:

    <t>
       <Order id="79223510">
          <Status>new</Status>
          <ShipMethod>Standard International</ShipMethod>
          <ToCity>Tokyo</ToCity>
          <Marketplace id="123-45678-9089808" name="MyBooks"/>
          <Items>
             <Item>
                <X>
                   <SKU>SKU-1234567890</SKU>
                   <Quantity>1</Quantity>
                   <Price>99.95</Price>
                </X>
             </Item>
          </Items>
       </Order>
       <Order id="79223510">
          <Status>new</Status>
          <ShipMethod>Standard International</ShipMethod>
          <ToCity>Tokyo</ToCity>
          <Marketplace id="123-45678-9089808" name="MyBooks"/>
          <Items>
             <Item>
                <X>
                   <SKU>SKU-1234554321</SKU>
                   <Quantity>1</Quantity>
                   <Price>199.95</Price>
                </X>
             </Item>
          </Items>
       </Order>
       <Order id="79223510">
          <Status>new</Status>
          <ShipMethod>Standard International</ShipMethod>
          <ToCity>Tokyo</ToCity>
          <Marketplace id="123-45678-9089808" name="MyBooks"/>
          <Items>
             <Item>
                <Y>
                   <SKU>SKU-0987654321</SKU>
                   <Quantity>1</Quantity>
                   <Price>299.95</Price>
                </Y>
             </Item>
          </Items>
       </Order>
       <Order id="79223510">
          <Status>new</Status>
          <ShipMethod>Standard International</ShipMethod>
          <ToCity>Tokyo</ToCity>
          <Marketplace id="123-45678-9089808" name="MyBooks"/>
          <Items>
             <Item>
                <Y>
                   <SKU>SKU-0987667890</SKU>
                   <Quantity>1</Quantity>
                   <Price>399.95</Price>
                </Y>
             </Item>
          </Items>
       </Order>
    </t>
    

    .3. The second pass processes the children elements of the top element of the intermediate result — the processing is done in mode “pass2”.

    This second-pass processing is rather simple — all descendent attributes or descendent text nodes are processed in document order, and their values are output with a separator corresponding to the type of the node (‘|’ for text node and ‘/’ for attribute).

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

This question is specific to MonoDevelop I think. I've got a test monorail solution
This question is the opposite of Collect several input fields and insert into a
This question has been asked before but the accepted solution (given by the question
This question started out with someone wanting to import every apparent 'line of text'
This question is continuation of my previous one. Without going into too much details,
This question is about having a button and text box. And when you click
This question was asked in an interview. First, I came up with B-tree. He
This question has been asked before but with no answer. I'm running into brick
This question is directly related to this SO question I posed about 15 minutes
This question is kind of a follow up to this question I asked a

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.