4

xml ドキュメントをコンパクトな形式でディスクに書き込みたいと考えています。この目的のために、私はネット フレームワーク メソッドを使用します。XmlDictionaryWriter.CreateBinaryWriter(Stream stream,IXmlDictionary dictionary)

このメソッドは、カスタム コンパクト バイナリ xml 表現を書き込みます。これは後で で読み取ることができますXmlDictionaryWriter.CreateBinaryReader。このメソッドは、共通の文字列を含むことができる を受け入れるXmlDictionaryため、それらの文字列を毎回出力に出力する必要はありません。文字列の代わりに、辞書のインデックスがファイルに出力されます。CreateBinaryReader後で同じ辞書を使用してプロセスを逆にすることができます。

しかし、私が渡す辞書は明らかに使用されていません。次のコードを検討してください。

using System.IO;
using System.Xml;
using System.Xml.Linq;

class Program
{
    public static void Main()
    {
        XmlDictionary dict = new XmlDictionary();
        dict.Add("myLongRoot");
        dict.Add("myLongAttribute");
        dict.Add("myLongValue");
        dict.Add("myLongChild");
        dict.Add("myLongText");

        XDocument xdoc = new XDocument();
        xdoc.Add(new XElement("myLongRoot",
                                new XAttribute("myLongAttribute", "myLongValue"),
                                new XElement("myLongChild", "myLongText"),
                                new XElement("myLongChild", "myLongText"),
                                new XElement("myLongChild", "myLongText")
                                ));

        using (Stream stream = File.Create("binaryXml.txt"))
        using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, dict))
        {
            xdoc.WriteTo(writer);
        }
    }
}

生成される出力は次のとおりです (バイナリ制御文字は示されていません)

@
myLongRootmyLongAttribute˜myLongValue@myLongChild™
myLongText@myLongChild™
myLongText@myLongChild™
myLongText

したがって、明らかに XmlDictionary は使用されていません。すべての文字列は、複数回であっても、そのまま出力に表示されます。

これは XDocument に限った問題ではありません。上記の最小限の例では、問題を示すために XDocument を使用しましたが、最初は XmlDictionaryWriter を DataContractSerializer と組み合わせて使用​​しているときに、一般的に使用されているため、これに遭遇しました。結果は同じでした:

[Serializable]
public class myLongChild
{
    public double myLongText = 0;
}
...
using (Stream stream = File.Create("binaryXml.txt"))
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, dict))
{
    var dcs = new DataContractSerializer(typeof(myLongChild));
    dcs.WriteObject(writer, new myLongChild());
}

結果の出力は、私の XmlDictionary を使用しませんでした。

提供された XmlDictionary を使用するように XmlDictionaryWriter を取得するにはどうすればよいですか?

または、これがどのように機能するかを誤解していますか?

DataContractSerializer アプローチで、ネット フレームワーク コードのデバッグを試みました ( Visual Studio/options/debugging/enable net.framework source stepping )。どうやら Writer は、予想どおり、上記の各文字列を辞書で検索しようとします。ただし、XmlbinaryWriter.cs の 356 行目でルックアップが失敗します。その理由は明らかではありません。

私が検討した代替案:

  • XmlDictionaryWriter.CreatebinaryWriter のオーバーロードがあり、XmlBinaryWriterSession も受け入れます。次に、ライターは、検出した新しい文字列をセッション ディクショナリに追加します。ただし、事前にわかっている読み書き用の静的辞書のみを使用したい

  • 全体を にラップしてGzipStream、圧縮で文字列の複数のインスタンスを処理できるようにすることができます。ただし、これは各文字列の最初のインスタンスを圧縮しないため、全体的に扱いにくい回避策のように思えます。

4

1 に答える 1

3

はい、誤解があります。XmlDictionaryWriterは主にオブジェクトのシリアル化に使用され、 の子クラスですXmlWriter。を引数としてXDocument.WriteTo(XmlWriter something)取ります。XmlWriterこの呼び出しは、内部的XmlDictionaryWriter.CreateBinaryWriterに のインスタンスを作成します。System.Xml.XmlBinaryNodeWriterこのクラスには、「通常の」書き込みのための両方のメソッドがあります。

// override of XmlWriter
public override void WriteStartElement(string prefix, string localName)
{
  // plain old "xml" for me please
}

および辞書ベースのアプローチの場合:

// override of XmlDictionaryWriter
public override void WriteStartElement(string prefix, XmlDictionaryString localName)
{
  // I will use dictionary to hash element names to get shorter output
}

後者は、 を介してオブジェクトをシリアル化する場合に主に使用されます(そのメソッドはとタイプの両方の引数を取ることに注意DataContractSerializerしてください) 。WriteObjectXmlDictionaryWriterXmlWriterXDocumentXmlWriter

あなたの問題について - もし私があなただったら、私は自分で作るでしょうXmlWriter

class CustomXmlWriter : XmlWriter
{
  private readonly XmlDictionaryWriter _writer;
  public CustomXmlWriter(XmlDictionaryWriter writer)
  {
    _writer = writer;
  }
  // override XmlWriter methods to use the dictionary-based approach instead
}

更新(コメントに基づく)

実際に使用している場合DataContractSerializer、コードに間違いはほとんどありません。

1) POC クラスは[DataContract][DataMember]属性で装飾する必要があります。シリアル化された値は、フィールドではなくプロパティにする必要があります。また、名前空間を空の値に設定するか、辞書内の名​​前空間も処理する必要があります。お気に入り:

namespace  XmlStuff {
  [DataContract(Namespace = "")]
  public class myLongChild
  {
    [DataMember]
    public double myLongText { get; set; }
  }

  [DataContract(Namespace = "")]
  public class myLongRoot
  {
    [DataMember]
    public IList<myLongChild> Items { get; set; }
  }
}

2) セッションのインスタンスも提供します。null セッションの場合、ディクショナリ ライターはデフォルトの ( XmlWriter-like) 実装を使用します。

// order matters - add new items only at the bottom
static readonly string[] s_Terms = new string[]
{
    "myLongRoot", "myLongChild", "myLongText", 
    "http://www.w3.org/2001/XMLSchema-instance", "Items"
};

public class CustomXmlBinaryWriterSession : XmlBinaryWriterSession
{
  private bool m_Lock;
  public void Lock() { m_Lock = true; }

  public override bool TryAdd(XmlDictionaryString value, out int key)
  {
    if (m_Lock)
    {
      key = -1;
      return false;
    }

    return base.TryAdd(value, out key);
  }
}

static void InitializeWriter(out XmlDictionary dict, out XmlBinaryWriterSession session)
{
  dict = new XmlDictionary();
  var result = new CustomXmlBinaryWriterSession();
  var key = 0;
  foreach(var term in s_Terms)
  {
    result.TryAdd(dict.Add(term), out key);
  }
  result.Lock();
  session = result;
}

static void InitializeReader(out XmlDictionary dict, out XmlBinaryReaderSession session)
{
  dict = new XmlDictionary();
  var result = new XmlBinaryReaderSession();
  for (var i = 0; i < s_Terms.Length; i++)
  {
    result.Add(i, s_Terms[i]);
  }
  session = result;
}

static void Main(string[] args)
{
  XmlDictionary dict;
  XmlBinaryWriterSession session;
  InitializeWriter(out dict, out session);

  var root = new myLongRoot { Items = new List<myLongChild>() };
  root.Items.Add(new myLongChild { myLongText = 24 });
  root.Items.Add(new myLongChild { myLongText = 25 });
  root.Items.Add(new myLongChild { myLongText = 27 });

  byte[] buffer;
  using (var stream = new MemoryStream())
  {
    using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, dict, session))
    {
      var dcs = new DataContractSerializer(typeof(myLongRoot));
      dcs.WriteObject(writer, root);
    }
    buffer = stream.ToArray();
  }


  XmlBinaryReaderSession readerSession;
  InitializeReader(out dict, out readerSession);
  using (var stream = new MemoryStream(buffer, false))
  {
    using (var reader = XmlDictionaryReader.CreateBinaryReader(stream, dict, new XmlDictionaryReaderQuotas(), readerSession))
    {
      var dcs = new DataContractSerializer(typeof(myLongRoot));
      var rootCopy = dcs.ReadObject(reader);
    }
  }
}    
于 2016-02-08T17:15:03.423 に答える