1

私はこれに髪を引っ張っています。私は XmlReader を使用して手動で逆シリアル化を行います - 深刻なことは何もありません。しかし、これは私が理解できないものです。

これはサンプルの xml ファイルです

<?xml version="1.0" encoding="utf-8"?>
<Theme name="something" version="1.0.0.0">
  <Thumbnail length="1102">[some base64 encoded data]
</Thumbnail>
  <Backgrounds>
    <string>Themes\something\Backgrounds\file1</string>
    <string>Themes\something\Backgrounds\file2</string>
    <string>Themes\something\Backgrounds\file3</string>
  </Backgrounds>
  <Stickers>
    <string>Themes\something\Stickers\stick1</string>
    <string>Themes\something\Stickers\stick1</string>
    <string>Themes\something\Stickers\stick1</string>
  </Stickers>
  <PreviewImages>
    <string>Themes\something\Preview\rh_01.jpg</string>
    <string>Themes\something\Preview\rh_02.jpg</string>
    <string>Themes\something\Preview\rh_03.jpg</string>
  </PreviewImages>
</Theme>

これは逆シリアル化コードです (少し簡略化されています):

public void ReadXml(System.Xml.XmlReader reader)
{       
    /* Read attributes - not important here */

    while (reader.Read())
    {
        Console.WriteLine("Main: {0} {1}", reader.NodeType, reader.Name);
        switch (reader.Name)
        {
            case Xml.Elements.Thumbnail:
                this._thumbnail = Xml.DeserializeBitmap(reader);
                Console.WriteLine("Inner: {0} {1}", reader.NodeType, reader.Name);
                break;
            case Xml.Elements.Backgrounds:
                this._backgrounds = Xml.DeserializeListOfStrings(reader);
                break;
            case Xml.Elements.Stickers:
                this._stickers = Xml.DeserializeListOfStrings(reader);
                break;
            case Xml.Elements.PreviewImages:
                this._previewImages = Xml.DeserializeListOfStrings(reader);
                break;
        }

        if (reader.NodeType == System.Xml.XmlNodeType.EndElement
                && reader.Name == Xml.Root)
            break;
    }
}

問題:

this._thumbnailが逆シリアル化された後、 はThumbnailノードreaderの終了要素に配置されます。次に、ループの先頭で...が呼び出され、文字列ノードの開始要素に配置されます。Backgrounds要素はスキップされます。なんで?reader.Read()whilereader

これは、 が で、そのプロパティが または に設定されている場合readerXmlTextReader発生WhitespaceHandlingWhitespaceHandling.NoneますWhitespaceHandling.Significant

WhitespaceHandling.Allすべてに設定されている場合は、期待どおりに機能します。を呼び出しreader.Read()た後、 Backgroundsノードreaderの開始要素に配置されます。


[編集]サンプル コードに 2 つのデバッグ行を追加しました。

私はこれWhitespaceHandling.Allを得る:

Main: Whitespace 
Main: Element Thumbnail
Inner: EndElement Thumbnail
Main: Element Backgrounds
Main: Whitespace 
Main: Element Stickers
Main: Whitespace 
Main: Element PreviewImages
Main: Whitespace 
Main: EndElement Theme

私はこれWhitespaceHandling.Significantを得る:

Main: Element Thumbnail
Inner: EndElement Thumbnail
Main: Element string
Main: Text 
Main: EndElement string
Main: Element string
Main: Text 
Main: EndElement string
Main: Element string
Main: Text 
Main: EndElement string
Main: EndElement Backgrounds

[編集 2]デバッグ出力を少し読みやすく調整しました。

ご覧のとおり、 のデバッグ出力は でWhitespaceHandling.Significant終了し</Backgrounds>ます。これXml.DeserializeListOfStringsは、ドキュメントが正しく配置されているかどうかをまだ確認しておらず、「誤って」ドキュメントを最後まで読み取っているためです。しかし、それはこの質問の範囲ではありません。

4

1 に答える 1

0

私の頭痛の原因は、ノードXmlReader.ReadElementContentAsBase64を逆シリアル化するために使用する方法です。<Thumbnail>私はループでそれを実験していました:

private static byte[] ReadBytes(System.Xml.XmlReader reader)
{
    byte[] buffer = new byte[128];
    int length = XmlConvert.ToInt32(reader[Xml.Attributes.Length]);

    using (MemoryStream ms = new MemoryStream(length))
    {
        int count = 0;

        do
        {
            count = reader.ReadElementContentAsBase64(buffer, 0, buffer.Length);
            ms.Write(buffer, 0, count);

        } while (ms.Length < length);

        return ms.GetBuffer();
    }
}

ただし、MSDNは次のように述べています。

カウント値がドキュメントのバイト数よりも大きい場合、またはドキュメントのバイト数と等しい場合、XmlNodeReaderはドキュメントの残りのすべてのバイトを読み取り、読み取ったバイト数を返します。次のReadElementContentAsBase64メソッド呼び出しはゼロを返し、リーダーをEndElementノードに続くノードに移動します。

すべての要素コンテンツが消費される前にReadを呼び出すと、リーダーは最初のコンテンツが消費されてからReadメソッドが呼び出されたかのように動作する場合があります。これは、読者がend要素に遭遇するまですべてのテキストを読むことを意味します。次に、終了タグノードを読み取り、次のノードを読み取り、次の後続ノードに自分自身を配置します。

要素のコンテンツを最後まで読んだにもかかわらず(データの長さを知っているので、理論的にはそれができる)、XmlReader要素のコンテンツをすべて「消費」したとは考えていなかったようです。これにより、MSDNで説明されている予期しない動作が発生しました。

ととXmlReader同じように動作しました。を最後に呼び出した後、重要でない空白をスキップしていたため、私のコードは機能しました。ソースxmlファイルに改行とタブが含まれていない場合、私のコードも失敗します。WhietespaceHandling.AllWhietespaceHandling.SignificantWhietespaceHandling.AllXmlReader.ReadElementContentAsBase64readerWhietespaceHandling.All

解決策は、whileループを変更してXmlReader.ReadElementContentAsBase64、すべてのバイトが赤くなった後に1つの追加呼び出しを行うことです。このアプローチの欠点は、その追加の呼び出しの後、EndElementノードに続くノードにreader移動されることです。

do
{
    count = reader.ReadElementContentAsBase64(buffer, 0, buffer.Length);
    if (count > 0)
        ms.Write(buffer, 0, count);

} while (count > 0);

XmlTextReader.ReadBase64メソッドを使用して要素のコンテンツ全体を一度に読み取ることもできますがXmlReader、クラスがIXmlSerializableを実装しているため、baseのみを使用する必要があるため、このメソッドは使用できません。

于 2012-04-05T23:05:47.897 に答える