XSLT 无效令牌导致 XML 文档无效

Posted

技术标签:

【中文标题】XSLT 无效令牌导致 XML 文档无效【英文标题】:XSLT invalid token results in invalid XML document 【发布时间】:2019-04-12 05:18:11 【问题描述】:

我正在使用 XSLT 文件将 XML 文件转换为另一个 XML 文件,然后在本地创建此 XML 文件。我收到此错误:

System.InvalidOperationException: '处于 Start 状态的令牌文本将导致无效的 XML 文档。如果要编写 XML 片段,请确保将 ConformanceLevel 设置设置为 ConformanceLevel.Fragment 或 ConformanceLevel.Auto。 '

XSLT 文件在 Visual Studio 中进行了调试,看起来可以正常工作,但我不明白这个错误。这是什么意思,如何解决?

这是我的 XML:

<?xml version="1.0" encoding="utf-8"?>
<In xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="take.xsd">
  <Submit ID="1234">
    <Values>
      <Code>34</Code>
      <Source>27</Source>
    </Values>
    <Information>
      <Number>55</Number>
      <Date>2018-05-20</Date>
      <IsFile>1</IsFile>
      <Location></Location>
      <Files>
        <File>
          <Name>Red.pdf</Name>
          <Type>COLOR</Type>
        </File>
        <File>
          <Name>picture.pdf</Name>
          <Type>IMAGE</Type>
        </File>        
      </Files>
    </Information>
  </Submit>
</In>

我的 XSLT 代码:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>

  <!-- identity template - copies all elements and its children and attributes -->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/In">
    <!-- Remove the 'In' element -->
    <xsl:apply-templates select="node()"/>
  </xsl:template>

  <xsl:template match="Submit">
    <!-- Create the 'Q' element and its sub-elements -->
    <Q xmlns:tns="Q" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://schema.xsd" Source="Values/Source" Notification="true">
      <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates select="Values" />
        <xsl:apply-templates select="Information" />
        <xsl:apply-templates select="Information/Files" />
      </xsl:copy>
    </Q>
  </xsl:template>

  <xsl:template match="Information">
    <!-- Create the 'Data' sub-element without all of its children -->
    <xsl:copy>
      <xsl:copy-of select="Number"/>
      <xsl:copy-of select="Date"/>
      <xsl:copy-of select="IsFile"/>
      <xsl:copy-of select="Location"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

这是用于转换文件的 C# 代码:

        XslCompiledTransform xslt = new XslCompiledTransform();
        xslt.Load(@"D:\\Main\XLSTFiles\Test.xslt");
        string xmlPath = @"D:\Documents\Test2.xml";
        using (XmlWriter w = XmlWriter.Create(@"D:\Documents\NewFile.xml"))
        
            xslt.Transform(xmlPath, w);
        

另外,有没有办法生成具有适当缩进的新 XML 文件?它似乎在最后一个节点关闭后创建每个节点,并且在自定义模板上它只是一个接一个地附加每个项目。

【问题讨论】:

Transform 方法有一个重载 xslt.Transform(@"D:\Documents\Test2.xml", @"D:\Documents\NewFile.xml"),所以使用它而不是创建您自己的 XmlWriter,这样 XslCompiledTransform 将在内部使用您的 xsl:output 中的正确设置创建一个。您收到的消息表明您的 XSLT 创建了一个包含多个***元素的片段,如果您想使用自己的 XmlWriter 来获得这样的结果,您需要使用正确的 XmlWriterSettings 和 ConformanceLevel.Fragment 谢谢@MartinHonnen。出于某种原因,将转换切换到您建议的重载转换甚至在不更改 XmlWriterSettings 的情况下摆脱了我的片段错误。甚至应该以这种方式发生吗?它生成的文件看起来也正确。 【参考方案1】:

这是一个非常无用的信息,不是吗?但我想我可以为你破译。

XSLT 处理器通过将诸如开始文档、开始元素、输出文本等事件写入 XML Writer 来生成其输出。

如果您想生成一个格式良好的 XML 文档,那么您不能在第一个元素的开始之前有任何文本。该消息是说,如果您做的最后一件事是发出 start-document,那么接下来的事情不允许是文本,因为该文档格式不正确(它说无效,但它意味着格式不正确) .

现在,XSLT 样式表被允许生成“格式良好的片段”,而不仅仅是被允许编写“格式良好的文档”。实际上,XML规范中使用的术语是“格式良好的外部通用解析实体”,但这有点拗口,所以每个人都称它们为“片段”,因为这就是DOM所说的,并且没有使用正确的术语在如果没有人理解错误消息。不同之处在于,一个片段可以在顶层包含多个元素和文本节点,例如this &lt;b&gt;really&lt;/b&gt; is a &lt;i&gt;well-formed&lt;/i&gt; fragment。问题是您将 XSLT 输出写入到的目的地可能无法处理片段,在这种特殊情况下,XML 编写器只有在配置为这样做时才能处理片段。

我怀疑您实际上并不打算生成片段,并且您需要修复您的 XSLT 代码,以便它输出格式正确的文档。

【讨论】:

【参考方案2】:

为了扩展 Michael Kay 的出色答案(因为这在 cmets 中写得太长了),对于您的特定输入 XML,问题在于空格。在匹配/In 的模板中,你这样做...

<xsl:template match="/In">
  <!-- Remove the 'In' element -->
  <xsl:apply-templates select="node()"/>
</xsl:template>

但是通过选择node(),您将选择子Submit 之前和之后的空白节点,因此您最终会在导致错误的根Q 元素之前得到一个文本节点。

因此,在这种情况下,您可以做的就是通过将其添加到您的 XSLT 中,从 XML 中去除空格

<xsl:strip-space elements="*" />

或者,您也可以这样做,仅选择元素,而不是其他节点(尽管这会省略 cmets 和处理指令)

<xsl:apply-templates select="*" />

但是,如果您的 XML 中有多个 Submit 元素,那么您会在输出中获得多个 Q 元素,这将是一个片段,因为只有一个根元素。如果这是您真正想要的,那么您应该对您的 C# 进行以下更改...

 using (XmlWriter w = XmlWriter.Create(@"C:\Users\tcase.BGT\Documents\NewFile.xml", xslt.OutputSettings ))

默认的ConformanceLevel 是ConformanceLevel.Auto,我认为它允许片段。添加它也将解决您的缩进问题,因为它将使用您的 xsl:output 中的设置。

【讨论】:

据我所知,只有一个提交元素。如果不是这种情况,那么 XSLT 将不得不更改对吗? 是的。如果有多个 submit 元素,并且您想输出格式正确的 XML(而不是片段),则必须输出一个包含所有子 Q 元素的新根元素。 小细节:XML 允许格式良好的文档在最外层元素节点之前和之后包含空白字符。但从这个证据看来,微软的 XMLWriter 不允许这样做。

以上是关于XSLT 无效令牌导致 XML 文档无效的主要内容,如果未能解决你的问题,请参考以下文章

APNS:无效令牌导致所有后续推送通知失败

包含 ∈ 的 XML 文档无效 - 如何使用 XSLT 输出?

使用 Firebase 登录 Facebook 导致无效 OAuth 访问令牌错误

python - xml.etree.ElementTree.ParseError:格式不正确(无效令牌)

文档中官方代码上的 Unity3D 无效令牌错误

直接访问登录表单时未设置会话 cookie,导致 CSRF 令牌无效