¿Por qué falla C # XmlDocument.LoadXml (cadena) cuando se incluye un encabezado XML?

¿Alguien tiene alguna idea de por qué el siguiente ejemplo de código falla con XmlException? “Los datos en el nivel raíz no son válidos. Línea 1, posición 1.”

var body = " ......" XmlDocument bodyDoc = new XmlDocument(); bodyDoc.LoadXml(body); 

Fondo

Aunque su pregunta tiene el conjunto de encoding como UTF-16, no tiene la cadena escapada correctamente, así que no estaba seguro si, de hecho, transpuso con precisión la cadena en su pregunta.

Me encontré con la misma excepción:

System.Xml.XmlException: los datos en el nivel raíz no son válidos. Línea 1, posición 1.

Sin embargo, mi código se veía así:

 string xml = "\nThis is a Test"; XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xml); 

El problema

El problema es que las cadenas se almacenan internamente como UTF-16 en .NET, pero la encoding especificada en el encabezado del documento XML puede ser diferente. P.ej:

  

De la documentación de MSDN para Cadena aquí :

Cada carácter Unicode en una cadena está definido por un valor escalar Unicode, también llamado punto de código Unicode o el valor ordinal (numérico) del carácter Unicode. Cada punto de código se codifica utilizando la encoding UTF-16, y el valor numérico de cada elemento de la encoding se representa mediante un objeto Char.

Esto significa que cuando pasa XmlDocument.LoadXml () su cadena con un encabezado XML, debe decir que la encoding es UTF-16. De lo contrario, la encoding subyacente real no coincidirá con la encoding informada en el encabezado y dará lugar a que se genere una XmlException.

La solución

La solución para este problema es asegurarse de que la encoding utilizada en lo que pase el método Load o LoadXml coincida con lo que usted dice que está en el encabezado XML. En mi ejemplo anterior, cambie su encabezado XML para indicar UTF-16 o codifique la entrada en UTF-8 y use uno de los métodos XmlDocument.Load .

A continuación se muestra el código de ejemplo que demuestra cómo usar un MemoryStream para construir un XmlDocument utilizando una cadena que define un documento XML con encoding UTF-8 (pero, por supuesto, se almacena una cadena .NET UTF-16).

 string xml = "\nThis is a Test"; // 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); 

Solución simple y efectiva: en lugar de usar el método LoadXml() use el método Load()

Por ejemplo:

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

Prueba esto:

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

Me lo imaginé. Lea la documentación de MSDN y dice usar .Load en lugar de LoadXml al leer cadenas. Descubrí que esto funciona el 100% del tiempo. Curiosamente, usar StringReader causa problemas. Creo que la razón principal es que esta es una cadena codificada en Unicode y que podría causar problemas porque StringReader es solo 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); 

Esto funcionó para mí:

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

Esto realmente me salvó el día.

Escribí un método de extensión basado en la respuesta de Zach, también lo extendí para usar la encoding como parámetro, permitiendo que se usen diferentes codificaciones además de UTF-8, y envolví el MemoryStream en una statement ‘using’.

 public static class XmlHelperExtentions { ///  /// Loads a string through .Load() instead of .LoadXml() /// This prevents character encoding problems. ///  ///  ///  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); } } } 

Tuve el mismo problema al cambiar de ruta absoluta a relativa para mi archivo xml. A continuación se resuelven tanto la carga como el uso de los problemas relativos de la ruta de origen. Usar un XmlDataProvider, que se define en xaml (también debería ser posible en el código):

     

El proveedor de datos carga automáticamente el documento una vez que se establece la fuente. Aquí está el código:

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

Línea simple:

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

Tuve el mismo problema porque el archivo XML que estaba cargando estaba codificado con UTF-8-BOM (marca de orden de bytes UTF-8).

Cambió la encoding a UTF-8 en Notepad ++ y fue capaz de cargar el archivo XML en código.