user1357872
user1357872

Reputation: 803

Multiple XML merging using XSLT -c#

I have the below code two merge two xml using xslt.

               XmlWriterSettings settings = new XmlWriterSettings();
               settings.OmitXmlDeclaration = true;
               settings.ConformanceLevel = ConformanceLevel.Fragment;
               settings.CloseOutput = false;
               XElement root = new XElement("root",
               XElement.Load("C:\\first.xml"),
               XElement.Load("C:\\second.xml"));

                       XDocument newTree = new XDocument();

                       using (XmlWriter writer = XmlWriter.Create(newTree.CreateWriter(), settings))
               {
                   XslCompiledTransform xslt = new XslCompiledTransform();

                   xslt.Load(@"..\..\XSDParser.xslt");

                 xslt.Transform(root.CreateReader(), writer);
                 newTree.Save("result.xml");
                 writer.Close();
               }

Root contains the below xml

<root>
  <Promotions>
    <Promotion>
      <Category>Arts &amp; Entertainment</Category>
      <Client>Client1</Client>
      <Title>Get your Free 2</Title>
    </Promotion>
    <Promotion>
      <Category>Arts &amp; Entertainment</Category>
      <Client>Client1</Client>
      <Title>Get your Free 4</Title>
    </Promotion>
    <Promotion>
      <Category>Arts &amp; Entertainment</Category>
      <Client>client1</Client>
      <Title>Get your Free 5</Title>
    </Promotion>
    <Promotion>
      <Category>Community &amp; Neighborhood</Category>
      <Client>Client2</Client>
      <Title>Get your Free 1</Title>
    </Promotion>
    <Promotion>
      <Category>Education</Category>
      <Client>Client3</Client>
      <Title>Get Your Free 3</Title>
    </Promotion>
  </Promotions>
  <Promotions>
    <Promotion>
      <Category>Arts &amp; Entertainment</Category>
      <Client>Client11111</Client>
      <Title>Get your Free 2</Title>
    </Promotion>
    <Promotion>
      <Category>Arts &amp; Entertainment</Category>
      <Client>Client1</Client>
      <Title>Get your Free 4</Title>
    </Promotion>
    <Promotion>
      <Category>Arts &amp; Entertainment</Category>
      <Client>client1</Client>
      <Title>Get your Free 5</Title>
    </Promotion>
    <Promotion>
      <Category>Community &amp; Neighborhood</Category>
      <Client>Client2</Client>
      <Title>Get your Free 1</Title>
    </Promotion>
    <Promotion>
      <Category>Education</Category>
      <Client>Client3</Client>
      <Title>Get Your Free 3</Title>
    </Promotion>
  </Promotions>
</root>

and the below XSLT used

   <xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
      <xsl:output method='xml' />
      <xsl:key name='categories' match='Category' use='.' />
      <xsl:template match='/'>
        <xsl:for-each select="/Promotions/Promotion/Category[ 
            generate-id(.) = generate-id(key('categories', .)[1]) 
          ]">
          <xsl:variable name='cname' select='.' />
          <Category title='{.}'>
            <xsl:for-each select='/Promotions/Promotion[Category=$cname]'>
              <Title>
                <xsl:value-of select='Title' />
              </Title>
            </xsl:for-each>
          </Category>
        </xsl:for-each>
      </xsl:template>
    </xsl:stylesheet>

but newTree.Save("result.xml"); returns the error

Additional information: Token EndDocument in state Document would result in an invalid XML document.

Where it gone wrong? Can someone provides hint on this?

Upvotes: 0

Views: 449

Answers (1)

Tim C
Tim C

Reputation: 70618

The first problem you have is that you should be closing the writer before saving the tree....

xslt.Transform(root.CreateReader(), writer);
writer.Close();
newTree.Save("result.xml");

Additionally, you have not accounted for the root element in the Xpath in your XSLT, meaning your xsl:for-each selects nothing, and so outputs nothing. So, in both cases, the xsl:for-each should be like this....

 <xsl:for-each select="/root/Promotions/Promotion....

Actually, in the inner xsl:for-each, you could actually use the key:

 <xsl:for-each select='key("categories", .)/..'>

It might be slightly more elegant if you changed the key to match Promotion based on the Category though.

Try this XSLT (Note I have also changed it to output a single root element).

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

  <xsl:key name='promotions' match='Promotion' use='Category' />

  <xsl:template match='/'>
    <Root>
        <xsl:for-each select="/root/Promotions/Promotion[ 
            generate-id(.) = generate-id(key('promotions', Category)[1]) 
          ]">
          <xsl:variable name='cname' select='Category' />
          <Category title='{$cname}'>
            <xsl:for-each select="key('promotions', Category)">
              <Title>
                <xsl:value-of select='Title' />
              </Title>
            </xsl:for-each>
          </Category>
        </xsl:for-each>
    </Root>
  </xsl:template>
</xsl:stylesheet>

Upvotes: 1

Related Questions