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




serialization read (9)

J'ai eu le même problème parce que le fichier XML que je téléchargeais était encodé en utilisant UTF-8-BOM (marque de commande d'octets UTF-8).

Commuté l'encodage en UTF-8 dans Notepad ++ et était capable de charger le fichier XML dans le code.

Est-ce que quelqu'un a une idée pourquoi l'exemple de code suivant échoue avec une exception XmlException "Les données au niveau de la racine est invalide Ligne 1, position 1."

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

Je l'ai compris. Lisez la documentation de MSDN et il est indiqué d'utiliser .Load au lieu de LoadXml lors de la lecture de chaînes. Trouvé sur ce travail 100% du temps. Bizarrement, utiliser StringReader cause des problèmes. Je pense que la raison principale est que c'est une chaîne codée Unicode et cela pourrait causer des problèmes parce que StringReader est UTF-8 seulement.

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

Essaye ça:

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

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

Contexte

Bien que l'encodage de votre question soit UTF-16, vous n'avez pas la chaîne correctement échappée, donc je n'étais pas sûr si vous avez, en fait, transposé avec précision la chaîne dans votre question.

J'ai rencontré la même exception:

System.Xml.XmlException: les données au niveau racine ne sont pas valides. 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 comme UTF-16 dans .NET, mais le codage spécifié dans l'en-tête de document XML peut être différent. Par exemple:

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

De la documentation de MSDN pour la chaîne here :

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

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

La solution

La solution à ce problème consiste à s'assurer que l'encodage utilisé dans tout ce que vous passez la méthode Load ou LoadXml correspond à ce que vous dites dans l'en-tête XML. Dans mon exemple ci-dessus, modifiez votre en-tête XML pour afficher UTF-16 ou pour encoder l'entrée dans UTF-8 et utiliser l'une des méthodes XmlDocument.Load .

Voici un exemple de code montrant comment utiliser un MemoryStream pour construire un XmlDocument en utilisant une chaîne qui définit un document XML de codage UTF-8 (mais bien sûr, est stocké une chaîne 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);

J'ai eu le même problème en passant du chemin absolu au chemin relatif pour mon fichier xml. Ce qui suit résout à la fois le chargement et l'utilisation de problèmes de chemin source relatifs. Utiliser un XmlDataProvider, qui est défini dans xaml (devrait aussi être possible dans le code):

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

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 aussi étendue pour utiliser l'encodage comme paramètre, permettant d'utiliser différents codages à côté de 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);
        }
    }
}

Cela a fonctionné pour moi:

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

Vous pouvez utiliser InvalidOperationException. C'est un compromis. Je ne voudrais pas utiliser une ArgumentException non plus.







c# .net xml