我在这里做错了什么,使用 C# 的 XSLT 出现问题

发布于 2024-08-20 10:14:26 字数 4515 浏览 3 评论 0原文

请看一下下面的代码,因为我在这一行收到错误:

xslt.Transform(mydoc.CreateReader(), writer);

Error:

Step into: Stepping over non-user code 'System.Xml.Linq.XNode.CreateReader' 中发生了“System.NullReferenceException”类型的第一次机会异常,

System.Data.SqlXml.dll ((System.NullReferenceException)(ex))

PromotionsDataContext db = new PromotionsDataContext();
//XmlDocument myxml;

XElement Categories =
    new XElement("Promotions",
        from b in db.GetPromotions()
        select new XElement("Promotion",
            new XElement("Category", b.CategoryName),
               new XElement("Client", b.ClientName),
               new XElement("Title", b.Title)));



XDocument mydoc = new XDocument();
mydoc.Add(Categories);


try
{

    XDocument newTree = new XDocument();


    XmlWriterSettings settings = new XmlWriterSettings();
    settings.OmitXmlDeclaration = true;
    settings.ConformanceLevel = ConformanceLevel.Fragment;
    settings.CloseOutput = false;

    //using (XmlWriter writer = newTree.CreateWriter())
    using (XmlWriter writer = XmlWriter.Create(newTree.CreateWriter(), settings))
    {

        // Load the style sheet.
        XslCompiledTransform xslt = new XslCompiledTransform();


        xslt.Load(XmlReader.Create(new FileStream(@"C:\1\TransList.xslt", System.IO.FileMode.Open)));

        // Execute the transform and output the results to a writer.

        xslt.Transform(mydoc.CreateReader(), writer);
    }

    Console.WriteLine(newTree);
}
catch (Exception ex)
{
    Console.Write(ex);

}

这是 XSLT:

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

错误:

System.NullReferenceException:未将对象引用设置为实例一个物体。 在System.Xml.Xsl.Runtime.XmlMergeSequenceWriter.StartTree(XPathNodeType rootType,IXmlNamespaceResolver nsResolver,XmlNameTable nameTable) 在 System.Xml.Xsl.Runtime.XmlQueryOutput.StartTree(XPathNodeType rootType) 在 System.Xml.Xsl.Runtime.XmlQueryOutput.WriteStartRoot() 在根(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}运行时) 在执行(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}运行时) 在 System.Xml.Xsl.XmlILCommand.Execute(对象 defaultDocument、XmlResolver dataSources、XsltArgumentList argumentList、XmlWriter writer、布尔 closeWriter) 在 System.Xml.Xsl.XmlILCommand.Execute(XmlReader contextDocument、XmlResolver dataSources、XsltArgumentList argumentList、XmlWriter 结果) 在 System.Xml.Xsl.XslCompiledTransform.Transform(XmlReader 输入,XsltArgumentList 参数,XmlWriter 结果) 在 c:\1\promo.ascx.cs 中的 Promo.Page_Load(Object sender, EventArgs e):line 144

现在,如果我这样做,它就可以工作:

StringWriter sw = new StringWriter(); 
xslt.Transform(mydoc.CreateReader(),null, sw);

我对 XmlWriter 做错了什么?

崩溃前 xdoc 的值:

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

Please take a look at the following code, as I get the error at this line:

xslt.Transform(mydoc.CreateReader(), writer);

Error:

Step into: Stepping over non-user code 'System.Xml.Linq.XNode.CreateReader'
A first chance exception of type 'System.NullReferenceException' occurred in System.Data.SqlXml.dll

((System.NullReferenceException)(ex))

PromotionsDataContext db = new PromotionsDataContext();
//XmlDocument myxml;

XElement Categories =
    new XElement("Promotions",
        from b in db.GetPromotions()
        select new XElement("Promotion",
            new XElement("Category", b.CategoryName),
               new XElement("Client", b.ClientName),
               new XElement("Title", b.Title)));



XDocument mydoc = new XDocument();
mydoc.Add(Categories);


try
{

    XDocument newTree = new XDocument();


    XmlWriterSettings settings = new XmlWriterSettings();
    settings.OmitXmlDeclaration = true;
    settings.ConformanceLevel = ConformanceLevel.Fragment;
    settings.CloseOutput = false;

    //using (XmlWriter writer = newTree.CreateWriter())
    using (XmlWriter writer = XmlWriter.Create(newTree.CreateWriter(), settings))
    {

        // Load the style sheet.
        XslCompiledTransform xslt = new XslCompiledTransform();


        xslt.Load(XmlReader.Create(new FileStream(@"C:\1\TransList.xslt", System.IO.FileMode.Open)));

        // Execute the transform and output the results to a writer.

        xslt.Transform(mydoc.CreateReader(), writer);
    }

    Console.WriteLine(newTree);
}
catch (Exception ex)
{
    Console.Write(ex);

}

Here is the XSLT:

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

The error:

System.NullReferenceException: Object reference not set to an instance of an object.
at System.Xml.Xsl.Runtime.XmlMergeSequenceWriter.StartTree(XPathNodeType rootType, IXmlNamespaceResolver nsResolver, XmlNameTable nameTable)
at System.Xml.Xsl.Runtime.XmlQueryOutput.StartTree(XPathNodeType rootType)
at System.Xml.Xsl.Runtime.XmlQueryOutput.WriteStartRoot()
at Root(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime)
at Execute(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime)
at System.Xml.Xsl.XmlILCommand.Execute(Object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter writer, Boolean closeWriter)
at System.Xml.Xsl.XmlILCommand.Execute(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter results)
at System.Xml.Xsl.XslCompiledTransform.Transform(XmlReader input, XsltArgumentList arguments, XmlWriter results)
at Promo.Page_Load(Object sender, EventArgs e) in c:\1\promo.ascx.cs:line 144

Now if I do this it works:

StringWriter sw = new StringWriter(); 
xslt.Transform(mydoc.CreateReader(),null, sw);

What am I doing wrong with the XmlWriter?

Value of xdoc right before crash:

<Promotions>
  <Promotion>
    <Category>Arts & Entertainment</Category>
    <Client>Client1</Client>
    <Title>Get your Free 2</Title>
  </Promotion>
  <Promotion>
    <Category>Arts & Entertainment</Category>
    <Client>Client1</Client>
    <Title>Get your Free 4</Title>
  </Promotion>
  <Promotion>
    <Category>Arts & Entertainment</Category>
    <Client>client1</Client>
    <Title>Get your Free 5</Title>
  </Promotion>
  <Promotion>
    <Category>Community & 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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

鱼忆七猫命九 2024-08-27 10:14:26

我认为问题确实在于您似乎想要使用样式表和 XmlWriter 创建 XML 片段,而 LINQ to XML 对象模型(即 System.Xml.Linq.XDocument/XNode)没有任何表示片段的类。

如果我获取您的 XML 输入(例如

<Promotions>
  <Promotion>
    <Category>Arts & Entertainment</Category>
    <Client>Client1</Client>
    <Title>Get your Free 2</Title>
  </Promotion>
  <Promotion>
    <Category>Arts & Entertainment</Category>
    <Client>Client1</Client>
    <Title>Get your Free 4</Title>
  </Promotion>
  <Promotion>
    <Category>Arts & Entertainment</Category>
    <Client>client1</Client>
    <Title>Get your Free 5</Title>
  </Promotion>
  <Promotion>
    <Category>Community & 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>

)和发布的第二个样式表(例如

<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='/'>
    <Categories>
      <!-- Added a root element here -->
      <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>
    </Categories>
  </xsl:template>
</xsl:stylesheet>

),然后使用以下 C# 代码,那么该代码对我来说可以完美运行:

    XDocument mydoc = XDocument.Load(@"..\..\XMLFile1.xml");

    XDocument newTree = new XDocument();

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

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

        xslt.Transform(mydoc.CreateReader(), writer);
        writer.Close();
    }

    Console.WriteLine(newTree);

当我使用原始样式表(例如

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

)时,我收到一个 InvalidOperationException,显示“Token StartElement in state” EndRootElement 将导致无效的 XML 文档。如果要编写 XML 片段,请确保将 ConformanceLevel 设置设置为 ConformanceLevel.Fragment 或 ConformanceLevel.Auto。”我怀疑您试图通过使用 ConformanceLevel.Fragment 将您的 XmlWriter 包装到另一个 XmlWriter 中来解决这个问题,就像您在 C# 代码中所做的那样,但我认为这不起作用,它只会导致不同的异常。

在我看来,应该有效的是在 XElement 实例上使用 CreateWriter(),因为应该可以将片段添加到 XElement。然而,我的测试仍然抛出异常,因此我已就此提交了一个错误,请参阅 https://connect.microsoft.com/VisualStudio/feedback/details/530052/xslt-stylesheet-writing -an-xml-fragment-to-an-xmlwriter-created-with-xelementinstance-createwriter-causes-nullreferenceexception

I think the issue is indeed that you seem to want to create an XML fragment with your stylesheet and the XmlWriter while the LINQ to XML object model (i.e. System.Xml.Linq.XDocument/XNode) does not have any class representing fragments.

The code works flawlessly for me if I take your XML input (e.g.

<Promotions>
  <Promotion>
    <Category>Arts & Entertainment</Category>
    <Client>Client1</Client>
    <Title>Get your Free 2</Title>
  </Promotion>
  <Promotion>
    <Category>Arts & Entertainment</Category>
    <Client>Client1</Client>
    <Title>Get your Free 4</Title>
  </Promotion>
  <Promotion>
    <Category>Arts & Entertainment</Category>
    <Client>client1</Client>
    <Title>Get your Free 5</Title>
  </Promotion>
  <Promotion>
    <Category>Community & 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>

) and the second stylesheet posted (e.g.

<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='/'>
    <Categories>
      <!-- Added a root element here -->
      <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>
    </Categories>
  </xsl:template>
</xsl:stylesheet>

) and then use the following C# code:

    XDocument mydoc = XDocument.Load(@"..\..\XMLFile1.xml");

    XDocument newTree = new XDocument();

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

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

        xslt.Transform(mydoc.CreateReader(), writer);
        writer.Close();
    }

    Console.WriteLine(newTree);

When I use the original stylesheet (e.g.

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

) I get an InvalidOperationException saying "Token StartElement in state EndRootElement would result in an invalid XML document. Make sure that the ConformanceLevel setting is set to ConformanceLevel.Fragment or ConformanceLevel.Auto if you want to write an XML fragment.". I suspect you tried to counter that by wrapping your XmlWriter into another one with ConformanceLevel.Fragment, as you have in your C# code, but that does not work I think, it only results in a different exception.

What should work in my view is to use CreateWriter() on an XElement instance as it should be possible to add a fragment to an XElement. My test however still throws an exception therefore I have filed a bug on that, see https://connect.microsoft.com/VisualStudio/feedback/details/530052/xslt-stylesheet-writing-an-xml-fragment-to-an-xmlwriter-created-with-xelementinstance-createwriter-causes-nullreferenceexception

仅一夜美梦 2024-08-27 10:14:26

看起来您的 XSLT 正在生成一个包含 元素列表的 XML 片段,而不是完整的 XML 文档。并且您尝试将片段写入空的 XDocument。这将导致无效的 XML 文档,因为您始终需要 XML 中的一个根元素。我不知道这是否是导致异常的具体原因,但您应该看看当您将 XSLT 修改为如下所示时会发生什么:

<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='/'>
    <Categories> <!-- Added a root element here -->
        <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>
    </Categories>
  </xsl:template>
</xsl:stylesheet>

It looks like your XSLT is producing an XML fragment containing a list of <Category> elements, rather than a full XML document. And you're trying to write the fragment to an empty XDocument. That would result in an invalid XML document, since you always need one root element in XML. I don't know if that's specifically what's causing your exception, but you should see what happens when you modify your XSLT to look like this:

<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='/'>
    <Categories> <!-- Added a root element here -->
        <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>
    </Categories>
  </xsl:template>
</xsl:stylesheet>
浅语花开 2024-08-27 10:14:26

你在哪里定义mydoc?我打赌它是空的。

Where did you define mydoc? I bet it's null.

紙鸢 2024-08-27 10:14:26

您是否打算在 new XDeclaration("2.0","utf-8","true") 代码中创建 XML 2.0 文档?我不知道这是否导致了问题,但这确实是一件奇怪的事情,因为 XML 2.0 并不存在。尝试完全删除它;你不应该需要它。

Did you mean to be creating an XML 2.0 document in your new XDeclaration("2.0","utf-8","true") code? I don't know if that's causing the problem, but it's certainly a strange thing to be doing, since XML 2.0 doesn't exist. Try removing it altogether; you shouldn't need it.

天气好吗我好吗 2024-08-27 10:14:26

由于 XML 是通过 LINQ to XML 构建的,因此该代码的执行可能会延迟:

XElement Categories = new XElement(
    "Promotions",
    from b in db.GetPromotions()
    select new XElement("Promotion",
        new XElement("Category", b.CategoryName),
           new XElement("Client", b.ClientName),
           new XElement("Title", b.Title)));

您的错误很可能发生在该代码中。尝试在 LINQ 中放置一个断点来逐步执行 XElement 创建。或者,为了确保执行不会延迟,您可以执行以下操作:

XElement Categories = new XElement(
    "Promotions",
    (
        from b in db.GetPromotions()
        select new XElement("Promotion",
            new XElement("Category", b.CategoryName),
            new XElement("Client", b.ClientName),
            new XElement("Title", b.Title))
    ).ToList()
);

Because the XML is being built through LINQ to XML, the execution of this code may be deferred:

XElement Categories = new XElement(
    "Promotions",
    from b in db.GetPromotions()
    select new XElement("Promotion",
        new XElement("Category", b.CategoryName),
           new XElement("Client", b.ClientName),
           new XElement("Title", b.Title)));

Your error is likely taking place in this code. Try placing a breakpoint in your LINQ to step through the XElement creation. Or, to ensure that the execution is not deferred, you could do the following:

XElement Categories = new XElement(
    "Promotions",
    (
        from b in db.GetPromotions()
        select new XElement("Promotion",
            new XElement("Category", b.CategoryName),
            new XElement("Client", b.ClientName),
            new XElement("Title", b.Title))
    ).ToList()
);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文