web-dev-qa-db-fra.com

Pourquoi C # XmlDocument.LoadXml (chaîne) échoue-t-il lorsqu'un en-tête XML est inclus?

Quelqu'un at-il une idée de la raison pour laquelle l'exemple de code suivant échoue avec une exception XmlException "Les données au niveau racine sont non valides. Ligne 1, position 1."

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

Contexte

Bien que le codage UTF-16 soit défini dans votre question, la chaîne n’est pas échappée correctement. Je ne savais pas si vous aviez effectivement transposé la chaîne dans votre question.

J'ai rencontré la même exception: 

System.Xml.XmlException: données sur le fichier le niveau racine est invalide. Ligne 1, position 1.

Cependant, mon code ressemblait à ceci:

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

Le problème

Le problème est que les chaînes sont stockées en interne en tant que UTF-16 dans .NET, mais le codage spécifié dans l'en-tête du document XML peut être différent. Par exemple.:

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

De la documentation MSDN pour String here :

Chaque caractère Unicode d'une chaîne est défini par une valeur scalaire Unicode, également appelé point de code Unicode ou la valeur ordinale (numérique) de Caractère Unicode. Chaque point de code est codé en utilisant le codage UTF-16, et le valeur numérique de chaque élément du fichier le codage est représenté par un caractère objet.

Cela signifie que lorsque vous transmettez à XmlDocument.LoadXml () votre chaîne avec un en-tête XML, il doit indiquer que le codage est UTF-16. Sinon, l'encodage sous-jacent réel ne correspondra pas à l'encodage indiqué dans l'en-tête et donnera lieu à une exception XmlException.

La solution

La solution à ce problème consiste à vérifier que le codage utilisé dans la méthode Load ou LoadXml correspond à ce que vous dites dans l'en-tête XML. Dans l'exemple ci-dessus, modifiez l'en-tête XML en indiquant l'état UTF-16 ou en codant l'entrée en UTF-8 et utilisez l'une des méthodes XmlDocument.Load .

Vous trouverez ci-dessous un exemple de code montrant comment utiliser un MemoryStream pour créer un document XmlDocument à l'aide d'une chaîne définissant un document XML à codage UTF-8 (mais, bien sûr, une chaîne .NET UTF-16 est stockée).

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);
114
Zach Burlingame

Solution simple et efficace: au lieu d'utiliser la méthode LoadXml(), utilisez la méthode Load()

Par exemple:

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

Essaye ça:

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

Je l'ai compris. Lisez la documentation MSDN et vous verrez utiliser .Load au lieu de LoadXml lors de la lecture de chaînes. Découvert que cela fonctionne 100% du temps. Curieusement, l'utilisation de StringReader pose des problèmes. Je pense que la raison principale est qu'il s'agit d'une chaîne codée Unicode et que cela pourrait poser problème, car StringReader est uniquement 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);
6
Gabe

Cela a fonctionné pour moi:

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

Cela a vraiment sauvé ma journée. 

J'ai écrit une méthode d'extension basée sur la réponse de Zach. Je l'ai également étendue pour utiliser l'encodage en tant que paramètre, ce qui permet d'utiliser différents encodages à partir d'UTF-8, et j'ai enveloppé le MemoryStream dans une instruction 'using'.

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

J'ai eu le même problème lorsque je passais d'un chemin absolu à relatif pour mon fichier xml . Ce qui suit résout les problèmes de chargement et d'utilisation relatifs du chemin source . Utiliser un XmlDataProvider, défini dans xaml aussi) :

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

Le fournisseur de données charge automatiquement le document une fois la source définie. Voici le code:

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

J'ai eu le même problème parce que le fichier XML que je téléchargeais était codé à l'aide de UTF-8-BOM (marque d'ordre UTF-8).

Basculé l'encodage sur UTF-8 dans Notepad ++ et parvenant à charger le fichier XML dans le code. 

0
Hugh