我有一个非常奇怪的问题:我正在构建一个第三方系统的接口,该系统通过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#代码中所做的就是:
这样的事情:
// 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位。
但字符串如何知道/“保留”有关3字节BOM的信息呢?我期望通过将字节数组转换为UTF-8编码字符串,任何差异都会消失,而BOM应该不再相关......
关于如何在没有 3字节BOM的情况下可靠地处理文件 或的任何想法?
答案 0 :(得分:2)
不是从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);
}