web-dev-qa-db-ja.com

XMLヘッダーが含まれているときにC#XmlDocument.LoadXml(string)が失敗するのはなぜですか?

次のコードサンプルがXmlException "ルートレベルのデータが無効です。行1、位置1"で失敗する理由は誰にもわかりません。

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

バックグラウンド

質問のエンコーディングはUTF-16に設定されていますが、文字列が適切にエスケープされていないため、実際に文字列を質問に正確に転置したかどうかはわかりませんでした。

私は同じ例外に遭遇しました:

System.Xml.XmlException:ルートレベルのデータが無効です。行1、位置1。

ただし、私のコードは次のようになりました。

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

問題

問題は、文字列が.NETでUTF-16として内部に保存されることですが、XMLドキュメントヘッダーで指定されたエンコードが異なる場合があります。例えば。:

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

String here のMSDNドキュメントから:

文字列内の各Unicode文字は、UnicodeコードポイントまたはUnicode文字の序数(数値)値とも呼ばれるUnicodeスカラー値によって定義されます。各コードポイントはUTF-16エンコードを使用してエンコードされ、エンコードの各要素の数値はCharオブジェクトで表されます。

これは、XmlDocument.LoadXml()にXMLヘッダー付きの文字列を渡すとき、エンコードがUTF-16であると言う必要があることを意味します。そうしないと、実際の基礎となるエンコードがヘッダーで報告されたエンコードと一致せず、XmlExceptionがスローされます。

ソリューション

この問題の解決策は、LoadメソッドまたはLoadXmlメソッドを渡す際に使用されるエンコードが、XMLヘッダーで指定されているものと一致するようにすることです。上記の私の例では、XMLヘッダーをUTF-16状態に変更するか、入力をUTF-8でエンコードして、 XmlDocument.Loadメソッド のいずれかを使用します。

以下は、MemoryStreamを使用して、UTF-8エンコードXMLドキュメントを定義する文字列を使用してXmlDocumentを構築する方法を示すサンプルコードです(もちろん、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);
116
Zach Burlingame

シンプルで効果的なソリューション:LoadXml()メソッドを使用する代わりに、Load()メソッドを使用します

例えば:

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

これを試して:

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

私はそれを考え出した。 MSDNドキュメントを読んで、文字列から読み取るときにLoadXmlではなく.Loadを使用するように指示されています。これが100%の時間で機能することがわかりました。奇妙なことに、StringReaderを使用すると問題が発生します。主な理由は、これがUnicodeエンコードされた文字列であり、StringReaderが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

これは私のために働いた:

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

これは本当に私の時間を節約しました。

Zachの答えに基づいて拡張メソッドを作成しました。また、エンコードをパラメーターとして使用するように拡張し、UTF-8以外の異なるエンコードを使用できるようにしました。また、MemoryStreamを「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

Xmlファイルの絶対パスから相対パスに切り替えると、同じ問題が発生しました。以下は、相対ソースパスの問題の読み込みと使用の両方を解決します。 xamlで定義されているXmlDataProviderを使用します(コードでも可能です):

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

ソースが設定されると、データプロバイダーは自動的にドキュメントを読み込みます。コードは次のとおりです。

        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

アップロードしたXMLファイルがUTF-8-BOM(UTF-8バイトオーダーマーク)を使用してエンコードされていたため、同じ問題が発生しました。

Notepad ++でエンコーディングをUTF-8に切り替え、XMLファイルをコードでロードできるようになりました。

0
Hugh

シンプルなライン:

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

0
xadriel