it-swarm.dev

Proč C # XmlDocument.LoadXml (řetězec) selže při zahrnutí záhlaví XML?

Má někdo nějaký nápad, proč následující ukázka kódu nezdaří s XmlException "Data na kořenové úrovni je neplatná. Řádek 1, pozice 1."

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

Pozadí

Ačkoli vaše otázka má kódování nastaveno jako UTF-16, nemáte řetězec správně unikl, takže jsem si nebyl jistý, zda jste ve skutečnosti přesně provedli řetězec do své otázky.

Narazil jsem na stejnou výjimku: 

System.Xml.XmlException: Data v kořenové úrovni Jsou neplatná. Poloha 1, Pozice 1.

Můj kód však vypadal takto:

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

Problém

Problém je, že řetězce jsou uloženy interně jako UTF-16 v .NET, ale kódování zadané v hlavičce dokumentu XML se může lišit. Např.:

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

Z dokumentace MSDN pro String zde :

Každý znak Unicode v řetězci je Definovaný skalární hodnotou Unicode, Také nazývaný kódový bod Unicode nebo Pořadová (numerická) hodnota Unicode charakter. Každý kódový bod je kódován pomocí kódování UTF-16 A číselná hodnota Každého prvku kódování Je reprezentována objektem Char .

To znamená, že když předáte řetězec XmlDocument.LoadXml () se záhlavím XML, musí se říci, že kódování je UTF-16. Jinak skutečné podkladové kódování neodpovídá kódování hlášené v záhlaví a bude mít za následek vyvolání XmlException.

Řešení

Řešením tohoto problému je zajistit, aby kódování použité v metodě Load nebo LoadXml odpovídalo tomu, co říkáte v záhlaví XML. Ve výše uvedeném příkladu buď změňte záhlaví XML na stav UTF-16 nebo kódujte vstup v UTF-8 a použijte jednu z metod XmlDocument.Load .

Níže je ukázkový kód demonstrující použití paměti MemoryStream k vytvoření XmlDocument pomocí řetězce, který definuje dokument XML kódování UTF-8 (ale samozřejmě je uložen řetězec 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);
114
Zach Burlingame

Jednoduché a efektivní řešení: Namísto použití metody LoadXml() použijte metodu Load()

Například:

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

Zkuste to:

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

Přišel jsem na to. Při čtení z řetězců si přečtěte dokumentaci MSDN a říká se, že se použije namísto LoadXml. Tohle funguje 100% času. Zvláště dost pomocí StringReaderu způsobuje problémy. Myslím, že hlavním důvodem je to, že se jedná o kódovaný řetězec Unicode, který by mohl způsobit problémy, protože StringReader je pouze 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

To fungovalo pro mě:

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

To mi opravdu zachránilo den. 

Napsal jsem metodu rozšíření založenou na Zachově odpovědi, také jsem ji rozšířil tak, aby používal kódování jako parametr, umožňující použití různých kódování vedle UTF-8 a zabalil jsem MemoryStream do '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

Měl jsem stejný problém při přechodu z absolutní na relativní cestu pro můj soubor xml. Následující řešení řeší jak načítání, tak použití relativních problémů se zdrojovou cestou. Pomocí XmlDataProvider, který je definován v xamlu možné i v kódu:

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

Jakmile je zdroj nastaven, poskytovatel dat automaticky načte dokument. Zde je kód:

        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

Měl jsem stejný problém, protože soubor XML, který jsem nahrál, byl zakódován pomocí UTF-8-BOM (UTF-8 byte-order mark).

Přepnutí kódování na UTF-8 v Notepad ++ a bylo možné načíst XML soubor v kódu. 

0
Hugh

Jednoduchá linka:

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

0
xadriel