XmlException при десериализации XML-файла в формате кодировки UTF-16
Использование C# XmlSerializer.
В процессе десериализации всех файлов XML в данной папке я вижу исключение XmlException "There is an error in XML document (0, 0)".
и InnerException "There is no Unicode byte order mark. Cannot switch to Unicode".
Все xmls в каталоге имеют кодировку "UTF-16". Разница лишь в том, что в некоторых XML-файлах отсутствуют элементы, определенные в классе, чей объект я использую при десериализации.
Например, предположим, у меня есть 3 разных типа xmls в моей папке:
file1.xml
<?xml version="1.0" encoding="utf-16"?>
<ns0:PaymentStatus xmlns:ns0="http://my.PaymentStatus">
</ns0:PaymentStatus>
file2.xml
<?xml version="1.0" encoding="utf-16"?>
<ns0:PaymentStatus xmlns:ns0="http://my.PaymentStatus">
<PaymentStatus2 RowNum="1" FeedID="38" />
</ns0:PaymentStatus>
file3.xml
<?xml version="1.0" encoding="utf-16"?>
<ns0:PaymentStatus xmlns:ns0="http://my.PaymentStatus">
<PaymentStatus2 RowNum="1" FeedID="38" />
<PaymentStatus2 RowNum="2" FeedID="39" Amt="26.0000" />
</ns0:PaymentStatus>
У меня есть класс для представления вышеупомянутого XML:
[XmlTypeAttribute(AnonymousType = true, Namespace = "http://my.PaymentStatus")]
[XmlRootAttribute("PaymentStatus", Namespace = "http://http://my.PaymentStatus", IsNullable = true)]
public class PaymentStatus
{
private PaymentStatus2[] PaymentStatus2Field;
[XmlElementAttribute("PaymentStatus2", Namespace = "")]
public PaymentStatus2[] PaymentStatus2 { get; set; }
public PaymentStatus()
{
PaymentStatus2Field = null;
}
}
[XmlTypeAttribute(AnonymousType = true)]
[XmlRootAttribute(Namespace = "", IsNullable = true)]
public class PaymentStatus2
{
private byte rowNumField;
private byte feedIDField;
private decimal AmtField;
public PaymentStatus2()
{
rowNumField = 0;
feedIDField = 0;
AmtField = 0.0M;
}
[XmlAttributeAttribute()]
public byte RowNum { get; set; }
[XmlAttributeAttribute()]
public byte FeedID { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public decimal Amt { get; set; }
}
Следующий фрагмент выполняет десериализацию для меня:
foreach (string f in filePaths)
{
XmlSerializer xsw = new XmlSerializer(typeof(PaymentStatus));
FileStream fs = new FileStream(f, FileMode.Open);
PaymentStatus config = (PaymentStatus)xsw.Deserialize(new XmlTextReader(fs));
}
Я что-то пропустил? Это должно быть что-то с форматом кодирования, потому что, когда я пытаюсь вручную заменить UTF-16 на UTF-8, и это, кажется, работает просто отлично.
3 ответа
Я столкнулся с той же самой ошибкой сегодня, работая со сторонним веб-сервисом.
Я последовал совету Алексея, используя StreamReader и установив кодировку. После этого StreamReader можно использовать в конструкторе XmlTextReader. Вот реализация этого с использованием кода из исходного вопроса:
foreach (string f in filePaths)
{
XmlSerializer xsw = new XmlSerializer(typeof(PaymentStatus));
FileStream fs = new FileStream(f, FileMode.Open);
StreamReader stream = new StreamReader(fs, Encoding.UTF8);
PaymentStatus config = (PaymentStatus)xsw.Deserialize(new XmlTextReader(stream));
}
Более вероятный encoding="utf-16"
не имеет отношения к кодированию сохраненных XML-данных и, таким образом, приводит к тому, что синтаксический анализатор не может читать поток в виде текста UTF-16.
Поскольку у вас есть комментарий, что изменение параметра "encoding" на "utf-8" позволит вам прочитать текст, который, как я полагаю, на самом деле имеет формат UTF8. Вы можете легко проверить это, открыв файлы в двоичном виде вместо текста в выбранном вами редакторе (например, Visual Studio).
Наиболее вероятная причина такого несоответствия - сохранить XML как writer.Write(document.OuterXml)
(сначала получить строковое представление, которое помещает "utf-16", но затем записать строку в поток с кодировкой utf-8 по умолчанию).
Возможный обходной путь - читать XML способом, симметричным для написания кода, - читать как строку и затем загружать XML из строки.
Правильное исправление - убедитесь, что XML хранится правильно.
Я не знаю, является ли это лучшим способом, но если мой входной поток не содержит спецификацию, я просто использую XDocument для обработки различных кодировок... например:
public static T DeserializeFromString<T>(String xml) where T : class
{
try
{
var xDoc = XDocument.Parse(xml);
using (var xmlReader = xDoc.Root.CreateReader())
{
return new XmlSerializer(typeof(T)).Deserialize(xmlReader) as T;
}
}
catch ()
{
return default(T);
}
}
Конечно, вы, вероятно, захотите отбросить любое исключение, но в случае с кодом, который я скопировал, мне не нужно было знать, если или почему это не удалось... поэтому я просто съел исключение.