.NET двоичный XML с предварительным общим словарем - PullRequest
8 голосов
/ 14 марта 2011

Я использую XmlDictionaryWriter для сериализации объектов в базу данных с помощью сериализатора контракта данных. Он отлично работает, размер и скорость в 2 раза лучше, чем при использовании text / xml.

Однако мне придется иметь дело с огромным количеством записей в моей базе данных, где любые дополнительные байты напрямую переводятся в гигабайты размера БД. Вот почему я бы хотел еще больше уменьшить размер, используя словарь XML.

Как мне это сделать?

Я вижу, что статический метод XmlDictionaryWriter.CreateBinaryWriter принимает 2-й параметр типа IXmlDictionary. В MSDN написано «XmlDictionary для использования в качестве общего словаря».

Сначала я попытался использовать системную реализацию:

XmlDictionary dict = new XmlDictionary();
string[] dictEntries = new string[]
{
    "http://schemas.datacontract.org/2004/07/MyContracts",
    "http://www.w3.org/2001/XMLSchema-instance",
    "MyElementName1",
    "MyElementName2",
    "MyElementName3",
};
foreach ( string s in dictEntries )
        dict.Add( s );

В результате .NET Framework полностью игнорирует словарь и по-прежнему вставляет вышеуказанные строки в виде обычного текста, а не просто ссылается на соответствующую запись словаря.

Затем я создал собственную реализацию IXmlDictionary:

class MyDictionary : IXmlDictionary
{
    Dictionary<int, string> values = new Dictionary<int, string>();
    Dictionary<string, int> keys = new Dictionary<string, int>();

    MyDictionary()
    {
        string[] dictEntries = new string[]
        {
            "http://schemas.datacontract.org/2004/07/MyContracts",
            "http://www.w3.org/2001/XMLSchema-instance",
            "MyElementName1",
            "MyElementName2",
            "MyElementName3",
        };

        foreach ( var s in dictEntries )
            this.Add( s );
    }

    static IXmlDictionary s_instance = new MyDictionary();
    public static IXmlDictionary instance { get { return s_instance; } }

    void Add( string val )
    {
        if ( keys.ContainsKey( val ) )
            return;
        int id = values.Count + 1;
        values.Add( id, val );
        keys.Add( val, id );
    }

    bool IXmlDictionary.TryLookup( XmlDictionaryString value, out XmlDictionaryString result )
    {
        if ( value.Dictionary == this )
        {
            result = value;
            return true;
        }
        return this.TryLookup( value.Value, out result );
    }

    bool IXmlDictionary.TryLookup( int key, out XmlDictionaryString result )
    {
        string res;
        if ( !values.TryGetValue( key, out res ) )
        {
            result = null;
            return false;
        }
        result = new XmlDictionaryString( this, res, key );
        return true;
    }

    public bool /* IXmlDictionary. */ TryLookup( string value, out XmlDictionaryString result )
    {
        int key;
        if ( !keys.TryGetValue( value, out key ) )
        {
            result = null;
            return false;
        }

        result = new XmlDictionaryString( this, value, key );
        return true;
    }
}

Результат - мои методы TryLookup называются OK, однако DataContractSerializer.WriteObject создает пустой документ.

Как использовать общедоступный словарь?

Заранее спасибо!

P.S. Я не хочу связываться с XmlBinaryReaderSession / XmlBinaryWriterSession: у меня нет "сессий", вместо этого у меня есть база данных объемом 10 ГБ +, к которой одновременно обращается множество потоков То, что я хочу, это просто статический предопределенный словарь.

Обновление: ОК Я понял, что мне просто нужно вызвать "XmlDictionaryWriter.Flush". Единственный оставшийся вопрос - почему предоставляемая системой реализация IXmlDictionary не работает должным образом?

Ответы [ 2 ]

0 голосов
/ 06 марта 2016

Единственный способ воспроизвести проблему с неиспользованным IXmlDictionary - это когда мой класс не был украшен атрибутом DataContract. Следующее приложение отображает разницу в размерах с декорированными и неокрашенными классами.

using System;
using System.Runtime.Serialization;
using System.Xml;

namespace XmlPresharedDictionary
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Serialized sizes");
            Console.WriteLine("-------------------------");
            TestSerialization<MyXmlClassUndecorated>("Undecorated: ");
            TestSerialization<MyXmlClassDecorated>("Decorated:   ");
            Console.ReadLine();
        }

        private static void TestSerialization<T>(string lineComment) where T : new()
        {
            XmlDictionary xmlDict = new XmlDictionary();
            xmlDict.Add("MyElementName1");

            DataContractSerializer serializer = new DataContractSerializer(typeof(T));

            using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
            using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, xmlDict))
            {
                serializer.WriteObject(writer, new T());
                writer.Flush();
                Console.WriteLine(lineComment + stream.Length.ToString());
            }
        }
    }

    //[DataContract]
    public class MyXmlClassUndecorated
    {
        public MyElementName1[] MyElementName1 { get; set; }

        public MyXmlClassUndecorated()
        {
            MyElementName1 = new MyElementName1[] { new MyElementName1("A A A A A"), new MyElementName1("A A A A A") };
        }
    }

    [DataContract]
    public class MyXmlClassDecorated
    {
        public MyElementName1[] MyElementName1 { get; set; }

        public MyXmlClassDecorated()
        {
            MyElementName1 = new MyElementName1[] { new MyElementName1("A A A A A"), new MyElementName1("A A A A A") };
        }
    }

    [DataContract]
    public class MyElementName1
    {
        [DataMember]
        public string Value { get; set; }

        public MyElementName1(string value) { Value = value; }
    }
}
0 голосов
/ 21 марта 2011

для XmlDictionaryWriter вам нужно использовать сессию.
пример :

   private static Stream SerializeBinaryWithDictionary(Person person,DataContractSerializer serializer)
    {
        var stream = new MemoryStream();
        var dictionary = new XmlDictionary();
        var session = new XmlBinaryWriterSession();
        var key = 0;
        session.TryAdd(dictionary.Add("FirstName"), out key);
        session.TryAdd(dictionary.Add("LastName"), out key);
        session.TryAdd(dictionary.Add("Birthday"), out key);
        session.TryAdd(dictionary.Add("Person"), out key);
        session.TryAdd(dictionary.Add("http://www.friseton.com/Name/2010/06"),out key);
        session.TryAdd(dictionary.Add("http://www.w3.org/2001/XMLSchema-instance"),out key);

        var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, dictionary, session);
        serializer.WriteObject(writer, person);
        writer.Flush();
        return stream;
    }
...