Почему С# XmlDocument.LoadXml(строка) терпит неудачу при включении заголовка XML?

Кто-нибудь может понять, почему следующий пример кода завершился с ошибкой XmlException. "Данные на корневом уровне недействительны. Строка 1, позиция 1."

var body = "<?xml version="1.0" encoding="utf-16"?><Report> ......"
XmlDocument bodyDoc = new XmlDocument();            
bodyDoc.LoadXml(body);

Ответ 1

Фон

Несмотря на то, что у вашего вопроса есть кодировка, установленная как UTF-16, у вас нет строки, которая правильно экранирована, поэтому я не был уверен, что вы действительно переставляете строку в свой вопрос.

Я столкнулся с тем же исключением:

System.Xml.XmlException: данные на root недопустим. Линия 1, положение 1.

Однако мой код выглядел так:

string xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<event>This is a Test</event>";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);

Проблема

Проблема состоит в том, что строки хранятся внутри UTF-16 в .NET, однако кодировка, указанная в заголовке документа XML, может быть разной. Например:.

<?xml version="1.0" encoding="utf-8"?>

Из документации MSDN для String здесь:

Каждый символ Юникода в строке определяемый скалярным значением Unicode, также называемый кодовой точкой Unicode или порядковое (числовое) значение Юникод. Каждая кодовая точка кодируется с использованием кодировки UTF-16, и числовое значение каждого элемента кодирование представлено Charобъект.

Это означает, что когда вы передаете XmlDocument.LoadXml() свою строку с заголовком XML, она должна сказать, что кодировка UTF-16. В противном случае фактическое базовое кодирование не будет соответствовать кодировке, указанной в заголовке, и приведет к тому, что будет выведено исключение XmlException.

Решение

Решение этой проблемы состоит в том, чтобы убедиться, что кодировка, используемая во всем, что вы передаете методу Load или LoadXml, соответствует тому, что вы говорите в заголовке XML. В моем примере выше либо измените свой XML-заголовок на состояние UTF-16, либо для кодирования ввода в UTF-8 и используйте один из методов XmlDocument.Load.

Ниже приведен пример кода, демонстрирующего, как использовать MemoryStream для создания XmlDocument с использованием строки, которая определяет XML-документ с кодировкой UTF-8 (но, конечно, хранится строка UTF-16.NET).

string xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<event>This is a Test</event>";

// Encode the XML string in a UTF-8 byte array
byte[] encodedString = Encoding.UTF8.GetBytes(xml);

// Put the byte array into a stream and rewind it to the beginning
MemoryStream ms = new MemoryStream(encodedString);
ms.Flush();
ms.Position = 0;

// Build the XmlDocument from the MemorySteam of UTF-8 encoded bytes
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(ms);

Ответ 2

Простое и эффективное решение: вместо использования метода LoadXml() используйте метод Load()

Например:

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("sample.xml");

Ответ 3

Попробуйте следующее:

XmlDocument bodyDoc = new XmlDocument();
bodyDoc.XMLResolver = null;
bodyDoc.Load(body);

Ответ 4

Я понял это. Прочтите документацию MSDN, и он говорит, что следует использовать .Load вместо LoadXml при чтении из строк. Обнаружено, что это работает 100% времени. Как ни странно, использование StringReader вызывает проблемы. Я думаю, что основная причина заключается в том, что это строковая кодировка Unicode, которая может вызвать проблемы, поскольку StringReader является только UTF-8.

MemoryStream stream = new MemoryStream();
            byte[] data = body.PayloadEncoding.GetBytes(body.Payload);
            stream.Write(data, 0, data.Length);
            stream.Seek(0, SeekOrigin.Begin);

            XmlTextReader reader = new XmlTextReader(stream);

            // MSDN reccomends we use Load instead of LoadXml when using in memory XML payloads
            bodyDoc.Load(reader);

Ответ 5

Это сработало для меня:

var xdoc = new XmlDocument { XmlResolver = null };  
xdoc.LoadXml(xmlFragment);

Ответ 6

Это действительно спасло мой день.

Я написал метод расширения на основе ответа Zach, также я расширил его, чтобы использовать кодировку в качестве параметра, позволяя использовать разные кодировки рядом с UTF-8, и я завернул MemoryStream в "использование", утверждение.

public static class XmlHelperExtentions
{
    /// <summary>
    /// Loads a string through .Load() instead of .LoadXml()
    /// This prevents character encoding problems.
    /// </summary>
    /// <param name="xmlDocument"></param>
    /// <param name="xmlString"></param>
    public static void LoadString(this XmlDocument xmlDocument, string xmlString, Encoding encoding = null) {

        if (encoding == null) {
            encoding = Encoding.UTF8;
        }

        // Encode the XML string in a byte array
        byte[] encodedString = encoding.GetBytes(xmlString);

        // Put the byte array into a stream and rewind it to the beginning
        using (var ms = new MemoryStream(encodedString)) {
            ms.Flush();
            ms.Position = 0;

            // Build the XmlDocument from the MemorySteam of UTF-8 encoded bytes
            xmlDocument.Load(ms);
        }
    }
}

Ответ 7

У меня была такая же проблема при переключении с абсолютного на относительный путь для моего xml файла. Следующее решает как загрузку, так и использование относительных проблем с источником. Использование XmlDataProvider, которое определено в xaml (должно быть возможно и в коде):

    <Window.Resources>
    <XmlDataProvider 
        x:Name="myDP"
        x:Key="MyData"
        Source=""
        XPath="/RootElement/Element"
        IsAsynchronous="False"
        IsInitialLoadEnabled="True"                         
        debug:PresentationTraceSources.TraceLevel="High"  /> </Window.Resources>

Поставщик данных автоматически загружает документ после установки источника. Здесь код:

        m_DataProvider = this.FindResource("MyData") as XmlDataProvider;
        FileInfo file = new FileInfo("MyXmlFile.xml");

        m_DataProvider.Document = new XmlDocument();
        m_DataProvider.Source = new Uri(file.FullName);

Ответ 8

Простая строка:

bodyDoc.LoadXml(new MemoryStream(Encoding.Unicode.GetBytes(body)));