【问题标题】:Parsing xml string to an xml document fails if the string begins with <?xml... ?> section如果字符串以 <?xml... ?> 部分开头,则将 xml 字符串解析为 xml 文档会失败
【发布时间】:2011-01-07 20:51:36
【问题描述】:

我有一个这样开头的 XML 文件:

<?xml version="1.0" encoding="utf-8"?>
<Report xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns="http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition">
  <DataSources>

当我运行以下代码时:

byte[] fileContent = //gets bytes
            string stringContent = Encoding.UTF8.GetString(fileContent);
            XDocument xml = XDocument.Parse(stringContent);

我得到以下 XmlException:

根级别的数据无效。 第 1 行,位置 1。

删除版本和编码节点可以解决问题。为什么?如何正确处理这个xml?

【问题讨论】:

    标签: c# .net xml


    【解决方案1】:

    我的第一个想法是从 .NET 字符串类型解析 XML 时编码是 Unicode。 看起来,虽然 XDocument 的解析对此非常宽容。

    问题实际上与 UTF8 前导码/字节顺序标记 (BOM) 有关,它是 UTF-8 流开头的三字节签名optionally present。这三个字节是关于流中使用的编码的提示。

    您可以通过在System.Text.Encoding 类的实例上调用GetPreamble 方法来确定编码的前导码。 例如:

    // returns { 0xEF, 0xBB, 0xBF }
    byte[] preamble = Encoding.UTF8.GetPreamble();
    

    XmlTextReader 应正确处理序言,因此只需从XmlTextReader 加载您的XDocument

    XDocument xml;
    using (var xmlStream = new MemoryStream(fileContent))
    using (var xmlReader = new XmlTextReader(xmlStream))
    {
        xml = XDocument.Load(xmlReader);
    }
    

    【讨论】:

    • 请注意,UTF-8“前导码”是微软的一项发明,与普通的 UTF-16 BOM 不同,它不受任何 Unicode 标准的认可。它永远不应该用于写作,尽管你必须在阅读时处理它,因为你经常会在野外遇到讨厌的枯萎病。
    • @bobince - 我同意(尽管 Unicode 标准允许使用它,但不鼓励使用它 - 请参阅 unicode.org/versions/Unicode5.0.0/ch02.pdf#G19273 的第 36 页了解更多信息)。
    • 我已经修改了答案 - 见最后一段。
    【解决方案2】:

    如果您只有字节,您可以将字节加载到流中:

    XmlDocument oXML;
    
    using (MemoryStream oStream = new MemoryStream(oBytes))
    {
      oXML = new XmlDocument();
      oXML.Load(oStream);
    }
    

    或者您可以在加载 XML 之前将字节转换为字符串(假设您知道编码):

    string sXml;
    XmlDocument oXml;
    
    sXml = Encoding.UTF8.GetString(oBytes);
    oXml = new XmlDocument();
    oXml.LoadXml(sXml);
    

    我已将我的示例显示为与 .NET 2.0 兼容,如果您使用的是 .NET 3.5,则可以使用 XDocument 而不是 XmlDocument

    将字节加载到流中:

    XDocument oXML;
    
    using (MemoryStream oStream = new MemoryStream(oBytes))
    using (XmlTextReader oReader = new XmlTextReader(oStream))
    {
      oXML = XDocument.Load(oReader);
    }
    

    将字节转换为字符串:

    string sXml;
    XDocument oXml;
    
    sXml = Encoding.UTF8.GetString(oBytes);
    oXml = XDocument.Parse(sXml);
    

    【讨论】:

    • 问题是我需要使用XDocument
    • @agnieszka - 我已经更新了我的答案,让您了解如何使用 XDocument。
    • 如果原始oBytes 包含字节顺序标记序列,则必须修改字符串。我不得不打电话给sXml = sXml.Substring(1);,否则XDocument.Parse 上会抛出错误Data at the root level is invalid. Line 1, position 1.。 BOM 字节不可见,因此可以使用.WriteLine("first char '{0}'", sXml[0]) 进行检查
    【解决方案3】:

    您的 XML 开头是否有 byte-order-mark (BOM),它是否与您的编码匹配?如果你砍掉你的标题,你也会砍掉 BOM,如果那不正确,那么后续的解析可能会起作用。

    您可能需要在字节级别检查文档以查看 BOM。

    【讨论】:

    • 什么是字节序标记...?以及如何找出文档的编码?我只是怀疑它是 utf-8(阅读文本是可读的)
    • 查看我发布的链接。它是一个字节序列之前作为文档编码的指令的标题。
    【解决方案4】:

    为什么要费心将文件作为字节序列读取,然后在它是 xml 文件时将其转换为字符串?只需让框架为您加载并处理编码:

    var xml = XDocument.Load("test.xml");
    

    【讨论】:

    • 因为我没有从路径中获取 xml。我只有字节内容
    • 这些字节是从哪里来的?数据库,网络流,...?
    【解决方案5】:

    试试这个:

    int startIndex = xmlString.IndexOf('<');
    if (startIndex > 0)
    {
        xmlString = xmlString.Remove(0, startIndex);
    }
    

    【讨论】:

    • 如果您解释说这是为了强制跳过序言/BOM,将会有所帮助。
    【解决方案6】:

    我也遇到了这个错误,因为源 XML 是一个字符串,它以某种方式获得了一些似乎破坏 XmlDocumentXDocument 解析的不可打印字符。剥离它们可以解决问题:

    string sanitized = Regex.Replace(part, @"\p{C}+", string.Empty);
    

    信用:C# regex to remove non - printable characters, and control characters, in a text that has a mix of many different languages, unicode letters

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多