为啥包含 XML 标头时 C# XmlDocument.LoadXml(string) 会失败?

Posted

技术标签:

【中文标题】为啥包含 XML 标头时 C# XmlDocument.LoadXml(string) 会失败?【英文标题】:Why does C# XmlDocument.LoadXml(string) fail when an XML header is included?为什么包含 XML 标头时 C# XmlDocument.LoadXml(string) 会失败? 【发布时间】:2010-09-23 13:33:40 【问题描述】:

有谁知道为什么下面的代码示例失败并出现 XmlException “根级别的数据无效。第 1 行,位置 1。”

var body = "<?xml version="1.0" encoding="utf-16"?><Report> ......"
XmlDocument bodyDoc = new XmlDocument();            
bodyDoc.LoadXml(body);

【问题讨论】:

Dan 是对的 - 代码很好。检查 xml 的一种快速简便的方法是在 Internet Explorer 中打开它 您确定将正文中的编码设置为 utf-16 而不是别的吗?此外,您的身体字符串是否真的逃脱了,例如body = "\n这是一个测试"; 【参考方案1】:

背景

虽然您的问题确实将编码设置为 UTF-16,但您没有正确转义字符串,所以我不确定您是否确实将字符串准确地转义到您的问题中。

我遇到了同样的异常:

System.Xml.XmlException: 数据在 根级别无效。 1号线, 位置 1。

但是,我的代码如下所示:

string xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<event>This is a Test</event>";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);

问题

问题是字符串在 .NET 中内部存储为 UTF-16,但是 XML 文档标头中指定的编码可能不同。例如:

<?xml version="1.0" encoding="utf-8"?>

来自字符串 here 的 MSDN 文档:

字符串中的每个 Unicode 字符都是 由 Unicode 标量值定义, 也称为 Unicode 代码点或 的序数(数字)值 Unicode 字符。每个代码点是 使用 UTF-16 编码进行编码,并且 的每个元素的数值 编码由 Char 表示 对象。

这意味着当您通过 XmlDocument.LoadXml() 传递带有 XML 标头的字符串时,它必须说编码是 UTF-16。否则,实际的底层编码将与标头中报告的编码不匹配,并将导致抛出 XmlException。

解决方案

此问题的解决方案是确保在您传递 Load 或 LoadXml 方法的任何内容中使用的编码与您在 XML 标头中所说的内容相匹配。在我上面的示例中,要么将您的 XML 标头更改为 UTF-16,要么将输入编码为 UTF-8 并使用 XmlDocument.Load methods 之一。

以下示例代码演示了如何使用 MemoryStream 使用定义 UTF-8 编码 XML 文档的字符串构建 XmlDocument(当然,存储的是 UTF-16 .NET 字符串)。

string xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<event>This is a Test</event>";

// Encode the XML string in a UTF-8 byte array
byte[] encodedString = Encoding.UTF8.GetBytes(xml);

// Put the byte array into a stream and rewind it to the beginning
MemoryStream ms = new MemoryStream(encodedString);
ms.Flush();
ms.Position = 0;

// Build the XmlDocument from the MemorySteam of UTF-8 encoded bytes
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(ms);

【讨论】:

别忘了 ms.close() 或在 MemoryStream 上使用语句【参考方案2】:

简单有效的解决方案:不要使用LoadXml() 方法,而是使用Load() 方法

例如:

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("sample.xml");

【讨论】:

这更容易做和理解。我的文档没有任何 xml 标头。【参考方案3】:

我想通了。阅读 MSDN 文档,它说从字符串读取时使用 .Load 而不是 LoadXml。发现这 100% 的时间有效。奇怪的是,使用 StringReader 会导致问题。我认为主要原因是这是一个 Unicode 编码的字符串,这可能会导致问题,因为 StringReader 仅是 UTF-8。

MemoryStream stream = new MemoryStream();
            byte[] data = body.PayloadEncoding.GetBytes(body.Payload);
            stream.Write(data, 0, data.Length);
            stream.Seek(0, SeekOrigin.Begin);

            XmlTextReader reader = new XmlTextReader(stream);

            // MSDN reccomends we use Load instead of LoadXml when using in memory XML payloads
            bodyDoc.Load(reader);

【讨论】:

在此处阅读 XmlDocument.LoadXml(String) 的 MSDN 文档:msdn.microsoft.com/en-us/library/… 方法摘要指出:“从指定的字符串加载 XML 文档。”但是,正如您所说,它确实说:“如果您想从 Stream、String、TextReader 或 XmlReader 加载,请使用 Load 方法而不是此方法。”此外,如果您查看 XmlDocument.Load(String) 它会显示:包含要加载的 XML 文档的文件的 URL。 “URL 可以是本地文件,也可以是 HTTP URL(Web 地址)。” (更多内容在另一条评论中) 我相信“如果您想从流、字符串...加载”这行的预期目的实际上是“如果您想从流、文件...加载”但他们有 String 因为从文件加载需要文件名的字符串参数。我不相信他们的意图是“如果您想从内存中的字符串加载 XmlDocument,请使用 Load”。毕竟,这就是 LoadXml(String) 的既定目的!虽然您的解决方案确实提供了解决方法,但我认为它不能解决 XmlDocument.LoadXml(String) 的实际陈述问题(我也有)。【参考方案4】:

试试这个:

XmlDocument bodyDoc = new XmlDocument();
bodyDoc.XMLResolver = null;
bodyDoc.Load(body);

【讨论】:

【参考方案5】:

这对我有用:

var xdoc = new XmlDocument  XmlResolver = null ;  
xdoc.LoadXml(xmlFragment);

【讨论】:

【参考方案6】:

这真的拯救了我的一天。

我已经根据 Zach 的回答编写了一个扩展方法,我还扩展了它以使用编码作为参数,允许使用除 UTF-8 之外的不同编码,并且我将 MemoryStream 包装在“使用”中声明。

public static class XmlHelperExtentions

    /// <summary>
    /// Loads a string through .Load() instead of .LoadXml()
    /// This prevents character encoding problems.
    /// </summary>
    /// <param name="xmlDocument"></param>
    /// <param name="xmlString"></param>
    public static void LoadString(this XmlDocument xmlDocument, string xmlString, Encoding encoding = null) 

        if (encoding == null) 
            encoding = Encoding.UTF8;
        

        // Encode the XML string in a byte array
        byte[] encodedString = encoding.GetBytes(xmlString);

        // Put the byte array into a stream and rewind it to the beginning
        using (var ms = new MemoryStream(encodedString)) 
            ms.Flush();
            ms.Position = 0;

            // Build the XmlDocument from the MemorySteam of UTF-8 encoded bytes
            xmlDocument.Load(ms);
        
    

【讨论】:

【参考方案7】:

当我的 xml 文件从绝对路径切换到相对路径时,我遇到了同样的问题。 以下解决了加载和使用相对源路径问题。 使用在 xaml 中定义的 XmlDataProvider(代码中也应该可以):

    <Window.Resources>
    <XmlDataProvider 
        x:Name="myDP"
        x:Key="MyData"
        Source=""
        XPath="/RootElement/Element"
        IsAsynchronous="False"
        IsInitialLoadEnabled="True"                         
        debug:PresentationTraceSources.TraceLevel="High"  /> </Window.Resources>

一旦设置了源,数据提供者就会自动加载文档。这是代码:

        m_DataProvider = this.FindResource("MyData") as XmlDataProvider;
        FileInfo file = new FileInfo("MyXmlFile.xml");

        m_DataProvider.Document = new XmlDocument();
        m_DataProvider.Source = new Uri(file.FullName);

【讨论】:

【参考方案8】:

简单的线:

bodyDoc.LoadXml(new MemoryStream(Encoding.Unicode.GetBytes(body)));

【讨论】:

【参考方案9】:

我遇到了同样的问题,因为我上传的 XML 文件是使用 UTF-8-BOM(UTF-8 字节顺序标记)编码的。

在 Notepad++ 中将编码切换为 UTF-8,并且能够在代码中加载 XML 文件。

【讨论】:

以上是关于为啥包含 XML 标头时 C# XmlDocument.LoadXml(string) 会失败?的主要内容,如果未能解决你的问题,请参考以下文章

从 dbus xml 生成文件时,如何告诉 qmake 包含标头?

为啥Kong在请求时需要标头主机?

在 C# 中将 HTTP Accept 和 Content-Type 标头都设置为“application/xml”

为啥请求中不包含授权标头? - 认证0

为啥我不应该包含 cpp 文件而使用标头?

为啥 Xcode 4 在每个标头中都包含 iostream?