【问题标题】:Deserializing UTF-8 encoded XML fails when BOM is present存在 BOM 时,反序列化 UTF-8 编码的 XML 失败
【发布时间】:2017-06-18 16:36:24
【问题描述】:

我在这里遇到了一个非常奇怪的问题:我正在构建一个第三方系统的接口,该系统通过 SFTP 服务器提供 XML 文件(使用 UTF-8 编码)。

我在我的 C# 代码中下载这些文件,然后尝试将它们反序列化为 C# 对象。对于大多数文件来说,这很好用,但对于某些文件来说,它只是不断轰炸......

想象一下这样的 DTO 类:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

还有这样的 XML:

<?xml version="1.0" encoding="utf-8"?>
<Person>
    <FirstName>John</FirstName>
    <LastName>Doe</LastName>
    <Age>42</Age>
</Person>

我在 C# 代码中所做的是:

  • 从 SFTP 服务器下载文件内容为字节数组
  • 从该二进制数据中提取一个 UTF-8 编码的字符串
  • 在反序列化过程中使用该字符串表示

类似这样的:

// get bytes from SFTP server
byte[] content = _sftpClient.Download(fileName);

// convert content to a UTF-8 string
string contentAsString = Encoding.UTF8.GetString(content);

try
{
    // deserialize that string into a "Person" instance
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.IgnoreComments = true;
    settings.IgnoreProcessingInstructions = true;
    settings.IgnoreWhitespace = true;
    settings.CheckCharacters = false;

    using (StringReader str = new StringReader(contentAsString))
    using (XmlReader xr = XmlReader.Create(str, settings))
    {
        XmlSerializer ser = new XmlSerializer(typeof(Person));

        if (ser.CanDeserialize(xr))
        {
            Person person = ser.Deserialize(xr) as Person;
        }
    }
}
catch (Exception exc)
{
    Console.WriteLine("ERROR: {0} - {1}", exc.GetType().Name, exc.Message);
}

现在我分析了有效的文件和无效的文件 - 区别在于二进制数据 (0xEF 0xBB 0xBF) 中的三字节前缀 - “Unicode BOM”(字节顺序标记)。

我知道 BOM,这就是我不使用直接从 SFTP 服务器获取的 二进制 数据的原因。当我将这些类型的文件转换为 XML 字符串 contentAsString 时,这个字符串 似乎 是相同的 - 至少我看不出有任何区别。

但是以 3 字节 BOM 开头的文件(在二进制数据中)导致反序列化在这一行失败

if (ser.CanDeserialize(xr))

出现错误:

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

但是 string 究竟是如何知道/“保存”关于 3 字节 BOM 的信息的呢?我期待通过将字节数组转换为 UTF-8 编码字符串,任何差异都会消失,BOM 应该不再相关......

关于如何可靠地处理 with没有 3 字节 BOM 的文件的任何想法?

【问题讨论】:

  • 可能重复 - 看看这个答案:stackoverflow.com/questions/3104158/…
  • @hoodaticus 这不是完全重复的 - 是的,情况似乎几乎相同 - 但接受的响应实际上是我已经在做的事情,而且它不起作用 对我来说(如果存在 BOM,则会导致异常)。不幸的是,该提议的解决方案并没有解决我的问题.....
  • BOMbing out 我知道你在那里做了什么:-D

标签: c# xml utf-8 deserialization


【解决方案1】:

不要从 byte[] 创建 string 并将其用作 XmlReader 的输入,而是使用 MemoryStream

        // get bytes from SFTP server
        byte[] content = _sftpClient.Download(fileName);

        try
        {
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.IgnoreComments = true;
            settings.IgnoreProcessingInstructions = true;
            settings.IgnoreWhitespace = true;
            settings.CheckCharacters = false;

            using(var memoryStream = new MemoryStream(content))
            using (XmlReader xr = XmlReader.Create(memoryStream, settings))
            {
                XmlSerializer ser = new XmlSerializer(typeof(Person));

                if (ser.CanDeserialize(xr))
                {
                    Person person = ser.Deserialize(xr) as Person;
                }
            }
        }
        catch (Exception exc)
        {
            Console.WriteLine("ERROR: {0} - {1}", exc.GetType().Name, exc.Message);
        }

【讨论】:

  • 谢谢 - 我很惊讶它的工作原理 - 我确信我需要将字节数组转换为字符串以消除有或没有 BOM 的差异 - 但是最后,它实际上是相反的 - 直接使用字节流,使用“中间”字符串不......但它工作 - 所以感谢一堆!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-25
  • 2014-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多