none
Пробллемма с XML сериализацией RRS feed

  • Вопрос

  • Необходимо сохранить список данных встал вопрос выбора сериализатора:

    1. XmlSerializer:

    - требует конструктора по умолчанию для всех вложенных типов (у меня например есть класс URI который такового не имеет)

    - сериализует только публичные свойства и поля, но у меня есть приватное поле которое хранит значение и выдает его через свойство с методом доступа только GET

    2. DataContractSerializer: вроде не имеет выше перечисленных недостатков. Но я не совсем понял как он создает объект? Точ то он не вызывает конструктора, то еще пол беды у так понял что и поля не инициализируются (за исключением статических) и к примеру все ссылочное равно null?

    но как тогда его использовать создавать метод где будут инициализироваться переменные? Но раз он будет вызываться после сериализации, то в методе не должно быть полей с атрибутом [DataMember]...

    Какой подход применить?

    10 декабря 2011 г. 11:05

Ответы

  • > сериализует только публичные свойства и поля, но у меня есть приватное поле которое хранит значение и выдает его через свойство с методом доступа только GET
     
     
    приватные поля можно сериализовать, если реализовать интерфейс ISerializable.
     
     
    using System;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Windows;
    
    namespace WpfApplication9
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                var t1 = new Test(12345);
                var xs = new NetDataContractSerializer(); //или new DataContractSerializer(typeof(Test));
                var ms = new MemoryStream();
                xs.Serialize(ms, t1); //или xs.WriteObject(ms, t1);
                ms.Flush();
                ms.Position = 0;
                var str = System.Text.Encoding.Default.GetString(ms.ToArray());
                System.Diagnostics.Trace.WriteLine(str);
    
                var t2 = (Test)xs.Deserialize(ms); //или (Test)xs.ReadObject(ms);
                System.Diagnostics.Trace.WriteLine(t2);
            }
        }
    
        [Serializable]
        public class Test : System.Runtime.Serialization.ISerializable
        {
            public int Data { get; private set; }
    
            public Test(int data)
            {
                Data = data;
            }
            protected Test(SerializationInfo info, StreamingContext context)
            {
                this.Data = info.GetInt32("Data");
            }
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.AddValue("Data", this.Data);
            }
            public override string ToString()
            {
                return "{" + this.Data + "}";
            }
        }
    }
    
      
     
    результат сериализации:
     
     
    <Test z:Id="1" 
       z:Type="WpfApplication9.Test" 
       z:Assembly="WpfApplication9, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 
       xmlns="http://schemas.datacontract.org/2004/07/WpfApplication9" 
       xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:x="http://www.w3.org/2001/XMLSchema" 
       xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
       <Data z:Id="2" z:Type="System.Int32" z:Assembly="0" xmlns="">12345</Data>
    </Test>
    
    
     
      
      или

     
    <Test
      xmlns="http://schemas.datacontract.org/2004/07/WpfApplication9"
      xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:x="http://www.w3.org/2001/XMLSchema">
      <Data i:type="x:int" xmlns="">12345</Data>
    </Test>
    
      
      
    • Предложено в качестве ответа Abolmasov Dmitry 20 декабря 2011 г. 14:54
    • Помечено в качестве ответа PhantomSL 16 января 2012 г. 10:39
    13 декабря 2011 г. 10:49
  • PhantomSL, здравствуйте.

    Просто не бросайте тему без ответа или какой-либо реакции на ответ. Вам ответили, но никакой ответной реакции не было, мы не знаем подошел ли вам ответ или по какой-либо причине - нет. Или может у вас что-то не вышло и требуется уточнить ответ.

    Спасибо.

    Вот немного расширенный ответ Malobukv, посмотрите, он вам подходит?:

                // где то в программе, например по клику кнопки
                var t1 = new Test(12345, "hello", new Uri("http://microsoft.com"));
                var xs = new DataContractSerializer(typeof(Test));
                var ms = new MemoryStream();
                xs.WriteObject(ms, t1);
                ms.Flush();
                ms.Position = 0;
                var str = System.Text.Encoding.Default.GetString(ms.ToArray());
                System.Diagnostics.Trace.WriteLine(str);
    
                var t2 = (Test)xs.ReadObject(ms);
                System.Diagnostics.Trace.WriteLine(t2);
    
        // класс, который сериализуем
        [Serializable]
        public class Test : System.Runtime.Serialization.ISerializable
        {
            public int Data { get; private set; }
            public string Str { get; set; }
            public Uri Adress { get; set; }
    
            public Test(int data, string str, Uri u)
            {
                Data = data;
                Str = str;
                Adress = u;
            }
            protected Test(SerializationInfo info, StreamingContext context)
            {
                this.Data = info.GetInt32("Data");
                this.Str = info.GetString("Str");
                this.Adress = (Uri)info.GetValue("Adress", typeof(Uri));
            }
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.AddValue("Data", this.Data);
                info.AddValue("Str", this.Str);
                info.AddValue("Adress", this.Adress, typeof(Uri));
            }
            public override string ToString()
            {
                return String.Format("{0}, {1}, {2}", this.Data, this.Str, this.Adress);
            }
        }
    
    

     


    Для связи [mail]
    • Помечено в качестве ответа PhantomSL 16 января 2012 г. 10:38
    21 декабря 2011 г. 6:39

Все ответы

  • > сериализует только публичные свойства и поля, но у меня есть приватное поле которое хранит значение и выдает его через свойство с методом доступа только GET
     
     
    приватные поля можно сериализовать, если реализовать интерфейс ISerializable.
     
     
    using System;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Windows;
    
    namespace WpfApplication9
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                var t1 = new Test(12345);
                var xs = new NetDataContractSerializer(); //или new DataContractSerializer(typeof(Test));
                var ms = new MemoryStream();
                xs.Serialize(ms, t1); //или xs.WriteObject(ms, t1);
                ms.Flush();
                ms.Position = 0;
                var str = System.Text.Encoding.Default.GetString(ms.ToArray());
                System.Diagnostics.Trace.WriteLine(str);
    
                var t2 = (Test)xs.Deserialize(ms); //или (Test)xs.ReadObject(ms);
                System.Diagnostics.Trace.WriteLine(t2);
            }
        }
    
        [Serializable]
        public class Test : System.Runtime.Serialization.ISerializable
        {
            public int Data { get; private set; }
    
            public Test(int data)
            {
                Data = data;
            }
            protected Test(SerializationInfo info, StreamingContext context)
            {
                this.Data = info.GetInt32("Data");
            }
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.AddValue("Data", this.Data);
            }
            public override string ToString()
            {
                return "{" + this.Data + "}";
            }
        }
    }
    
      
     
    результат сериализации:
     
     
    <Test z:Id="1" 
       z:Type="WpfApplication9.Test" 
       z:Assembly="WpfApplication9, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 
       xmlns="http://schemas.datacontract.org/2004/07/WpfApplication9" 
       xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:x="http://www.w3.org/2001/XMLSchema" 
       xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
       <Data z:Id="2" z:Type="System.Int32" z:Assembly="0" xmlns="">12345</Data>
    </Test>
    
    
     
      
      или

     
    <Test
      xmlns="http://schemas.datacontract.org/2004/07/WpfApplication9"
      xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:x="http://www.w3.org/2001/XMLSchema">
      <Data i:type="x:int" xmlns="">12345</Data>
    </Test>
    
      
      
    • Предложено в качестве ответа Abolmasov Dmitry 20 декабря 2011 г. 14:54
    • Помечено в качестве ответа PhantomSL 16 января 2012 г. 10:39
    13 декабря 2011 г. 10:49
  • Уважаемый пользователь,

    Пожалуйста, не забывайте отмечеть ответы, если они решают вашу проблему. Это можно сделать с помощью кнопки 'Пометить как ответ'.

    Спасибо.


    Для связи [mail]
    20 декабря 2011 г. 14:54
  • Уважаемый Abolmasov Dmitry, у меня зарадывается впечатление что это не форму Microsoft, а наследие совка. Вы действительно считаете что данный ответ я вляется ответом на мой вопрос. Или он просто портит статистику?

    В вопросе рассмотрено два варианта, в обоих указаны найденные мной недостатки. В ответе указано как устранить один недостаток одного из вариантов. Спасибо Malobukv за ответ и он возможно и помог бы решить проблеммму если бы я знал как "требует конструктора по умолчанию для всех вложенных типов (у меня например есть класс URI который такового не имеет)".

    Так что в моем случае был выбран второй вариант и я сериализую объекты с помощью DataContractSerializer, так и не поняв как все же он создает объект. Но если вы считаете что пост Malobukv является ответом я думаю вам хватит прав чтоб выполнить план:)

    20 декабря 2011 г. 15:32
  • PhantomSL, здравствуйте.

    Просто не бросайте тему без ответа или какой-либо реакции на ответ. Вам ответили, но никакой ответной реакции не было, мы не знаем подошел ли вам ответ или по какой-либо причине - нет. Или может у вас что-то не вышло и требуется уточнить ответ.

    Спасибо.

    Вот немного расширенный ответ Malobukv, посмотрите, он вам подходит?:

                // где то в программе, например по клику кнопки
                var t1 = new Test(12345, "hello", new Uri("http://microsoft.com"));
                var xs = new DataContractSerializer(typeof(Test));
                var ms = new MemoryStream();
                xs.WriteObject(ms, t1);
                ms.Flush();
                ms.Position = 0;
                var str = System.Text.Encoding.Default.GetString(ms.ToArray());
                System.Diagnostics.Trace.WriteLine(str);
    
                var t2 = (Test)xs.ReadObject(ms);
                System.Diagnostics.Trace.WriteLine(t2);
    
        // класс, который сериализуем
        [Serializable]
        public class Test : System.Runtime.Serialization.ISerializable
        {
            public int Data { get; private set; }
            public string Str { get; set; }
            public Uri Adress { get; set; }
    
            public Test(int data, string str, Uri u)
            {
                Data = data;
                Str = str;
                Adress = u;
            }
            protected Test(SerializationInfo info, StreamingContext context)
            {
                this.Data = info.GetInt32("Data");
                this.Str = info.GetString("Str");
                this.Adress = (Uri)info.GetValue("Adress", typeof(Uri));
            }
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.AddValue("Data", this.Data);
                info.AddValue("Str", this.Str);
                info.AddValue("Adress", this.Adress, typeof(Uri));
            }
            public override string ToString()
            {
                return String.Format("{0}, {1}, {2}", this.Data, this.Str, this.Adress);
            }
        }
    
    

     


    Для связи [mail]
    • Помечено в качестве ответа PhantomSL 16 января 2012 г. 10:38
    21 декабря 2011 г. 6:39
  • Как я уже говорил я пошел иным путем я использовал DataContractSerializer, но мне пришлось расчитывать что все поля не момеченные как [DataMember] или не являющиеся статическими (видимо статическая инициализация как и статические конструкторы все же срабатывают при первод обращении к свойству) получают значения по умолчанию (по этому и ссылочные были nil), потом в зависимости от обстоятельств я объявил метод занимающийся инициализацией который вызываю и в конструкторе или после восстановления объекта с помощью DataContractSerializer.

    Ваш (Malobukv) подход интересен тем что у вас все же сработает конструктор по умолчанию. Но тогда дополнительный вопрос, а при реализации интерфеса  ISerializable вручную необходимо создавать методы извлекания/сохранения для всех полей (которые хочу сохранить в поток) или только для тех с которыми не справится "cериализатор по умолчанию": с приватными полями и отсутствующими конструкторами?

    И так же интерисует ваше мнение на более общий вопрос учитывая их недостатки каким сериализатором удобнее пользоваться XmlSerializer или DataContractSerializer?

    21 декабря 2011 г. 10:12
  • Кстати, если уж начали смотреть различные сериализаторы, возможно стоит так же посмотреть protobuf-net и DataContractJsonSerializer. Это не ответ на ваш вопрос, это предложение посмотреть другие сериализаторы и знать из чего можно выбрать :)

    А по поводу того, что выбрать, то это вполне нормально иметь конструктор пустой для сериализации. Иметь лишние методы для полноценной инициализации элементов - не лучший вариант, ведь сохранять по идее нужно только данные, а не какие-то объекты, на которые накладывается дополнительная "умная" логика. Если же у вас элементы "умные", то возможно стоит создать отдельные простые классы для сериализации и потом по ним восстанавливать нужные вам объекты с помощью различных билдеров или просто передавая эти простые элементы в конструктор сложных.

    21 декабря 2011 г. 13:34
  • Я не против иметь пустой конструктор, его не имеет URI
    26 декабря 2011 г. 19:38
  • А при чем тут он? Если вы не можете сериализовать Uri класс, то это не проблема, сохраняйте его как строку.
    30 декабря 2011 г. 7:05