简体   繁体   中英

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?

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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM