webentwicklung-frage-antwort-db.com.de

Warum schlägt C # XmlDocument.LoadXml (Zeichenfolge) fehl, wenn ein XML-Header enthalten ist?

Hat jemand eine Idee, warum das folgende Codebeispiel mit einer XmlException fehlschlägt "Daten auf Stammebene sind ungültig. Zeile 1, Position 1."

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

Hintergrund

Obwohl für Ihre Frage die Kodierung als UTF-16 festgelegt wurde, ist die Zeichenfolge nicht ordnungsgemäß mit Escape-Zeichen versehen. Daher war ich mir nicht sicher, ob Sie die Zeichenfolge tatsächlich genau in Ihre Frage übernommen haben.

Ich bin in die gleiche Ausnahme geraten: 

System.Xml.XmlException: Daten am Stammebene ist ungültig. Linie 1, Position 1.

Mein Code sah jedoch so aus:

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

Das Problem

Das Problem ist, dass Zeichenfolgen intern als UTF-16 in .NET gespeichert werden. Die im XML-Dokumentkopf angegebene Codierung kann jedoch unterschiedlich sein. Z.B.:

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

Aus der MSDN-Dokumentation für String hier :

Jedes Unicode-Zeichen in einer Zeichenfolge lautet definiert durch einen Unicode-Skalarwert wird auch als Unicode-Codepunkt oder .__ bezeichnet. der Ordinalwert (numerisch) des Unicode-Zeichen Jeder Codepunkt lautet kodiert mit UTF-16-Kodierung und der numerischer Wert jedes Elements des Die Kodierung wird durch ein Zeichen dargestellt Objekt.

Das heißt, wenn Sie XmlDocument.LoadXml () Ihre Zeichenfolge mit einem XML-Header übergeben, muss die Kodierung UTF-16 sein. Andernfalls stimmt die eigentliche zugrunde liegende Kodierung nicht mit der im Header angegebenen Kodierung überein und führt dazu, dass eine XmlException ausgelöst wird.

Die Lösung

Die Lösung für dieses Problem besteht darin, sicherzustellen, dass die Codierung, die in der Übergabe der Load- oder LoadXml-Methode verwendet wird, mit den Angaben im XML-Header übereinstimmt. Ändern Sie in meinem obigen Beispiel entweder Ihren XML-Header in den Status UTF-16 oder verschlüsseln Sie die Eingabe in UTF-8 und verwenden Sie eine der XmlDocument.Load-Methoden .

Im Folgenden finden Sie Beispielcode, der zeigt, wie ein MemoryStream verwendet wird, um ein XmlDocument mithilfe einer Zeichenfolge zu erstellen, die ein UTF-8-XML-Kodierungsdokument definiert (es wird jedoch natürlich eine UTF-16-.NET-Zeichenfolge gespeichert).

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

Einfache und effektive Lösung: Verwenden Sie statt der LoadXml()-Methode die Load()-Methode

Zum Beispiel:

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

Versuche dies:

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

Ich habe es herausgefunden. Lesen Sie die MSDN-Dokumentation. Dort wird empfohlen, beim Lesen von Zeichenfolgen .Load anstelle von LoadXml zu verwenden. Ich habe herausgefunden, dass dies zu 100% funktioniert. Seltsamerweise verursacht die Verwendung von StringReader Probleme. Ich denke, der Hauptgrund ist, dass dies eine Unicode-codierte Zeichenfolge ist, die Probleme verursachen kann, da StringReader nur UTF-8 ist.

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

Das hat für mich funktioniert:

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

Das hat meinen Tag wirklich gerettet. 

Ich habe eine Erweiterungsmethode geschrieben, die auf Zachs Antwort basiert. Außerdem habe ich sie erweitert, um die Kodierung als Parameter zu verwenden, sodass neben UTF-8 auch andere Kodierungen verwendet werden können, und ich habe den MemoryStream in eine using-Anweisung verpackt.

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

Ich hatte das gleiche Problem, als ich von einem absoluten auf einen relativen Pfad für meine XML-Datei umstellte. Das Folgende löst sowohl das Laden als auch das Verwenden relativer Quellpfadprobleme. Verwenden eines XmlDataProvider, der in xaml definiert ist (sollte im Code möglich sein auch) :

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

Der Datenanbieter lädt das Dokument automatisch, sobald die Quelle festgelegt wurde. Hier ist der 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

Ich hatte das gleiche Problem, weil die XML-Datei, die ich hochlud, mit UTF-8-BOM (UTF-8-Bytereihenfolge) codiert wurde.

Die Codierung wurde in Notepad ++ auf UTF-8 umgestellt und konnte die XML-Datei im Code laden. 

0
Hugh

Einfache Linie:

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

0
xadriel