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);
        }
    }

Конечно, вы, вероятно, захотите отбросить любое исключение, но в случае с кодом, который я скопировал, мне не нужно было знать, если или почему это не удалось... поэтому я просто съел исключение.

Другие вопросы по тегам