locked
Cериализация ObservableCollection<Т> RRS feed

  • Вопрос

  • Здравствуйте, коллеги,

    появилась необходимость сохранить изменяемую пользователем коллекцию объектов и загружать список объектов обратно в коллекцию при запуске приложения (открытия страницы), в книгах по Win8 я подобного не встретил, в примерах на форумах у каждого своя задача и способ сериализации без объяснений происходящего, хотелось бы использовать максимально простой способ для данной простой задачи с обработкой возможных исключений.

    Например, имеем следующее:

    ObservableCollection<Person> myCollection,

    public const string XMLSerializationFile = "myfile.xml";

    StorageFile XMLStorageFile;

    StorageFolder storageFolder = ApplicationData.Current.LocalFolder;

    получаем файл с проверкой на существование, если есть, то перезаписываем при сохранении

    XMLStorageFile = await storageFolder.CreateFileAsync(XMLSerializationFile, CreationCollisionOption.ReplaceExisting);

    какие дальнейшие действия?

    18 августа 2013 г. 13:17

Ответы

  • Сериализуйте не ObservableCollection, а просто массив Person[]. Помимо стандартного DataContractSerializer'a есть так же DataContractJsonSerializer.

    Если у вас минимальные данные для сериализации, то можно обойтись и этими классами. По поводу Json.NET - штука хорошая (хотя на моих данных DataContractJsonSerializer был быстрее), но если не хотите подключать сторонние сборки и зависеть от них, то лучше обойтись стандартными средствами.

    • Помечено в качестве ответа Andev 19 августа 2013 г. 15:56
    18 августа 2013 г. 20:54
  • Сериализуй через сторонюю библиотеку JSON.NET (nuget). Там все норм будет серилизоваться. Сам с таким сталкивался. Только все еще зависит от твоего объекта ObservableCollection
    • Помечено в качестве ответа Andev 19 августа 2013 г. 15:57
    18 августа 2013 г. 13:31
  • Ну вот небольшой пример десериализации:

    private static T Deserialize<T>(String content, DataContractJsonSerializerSettings settings = null)
            {
                var serializer = settings == null ? new DataContractJsonSerializer(typeof(T)) : new DataContractJsonSerializer(typeof(T), settings);
                T response;
                using (var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(content)))
                {
                    response = (T)serializer.ReadObject(memoryStream);
                }
                return response;
            }

    • Помечено в качестве ответа Andev 19 августа 2013 г. 15:57
    19 августа 2013 г. 8:48
  • У меня следующий код отлично работает:

            IList<Person> persons = new List<Person>();
            persons.Add(new Person());
            persons.Add(new Person());
            persons.Add(new Person());
    
            var json = Serialize(persons.ToArray());
            SaveToFile("Persons.dat", json);
    
            public static String Serialize(Object value, DataContractJsonSerializerSettings settings = null)
            {
                var serializer = settings == null ? new DataContractJsonSerializer(value.GetType()) : new DataContractJsonSerializer(value.GetType(), settings);
                using (var memoryStream = new MemoryStream())
                {
                    serializer.WriteObject(memoryStream, value);
                    return Encoding.UTF8.GetString(memoryStream.ToArray(), 0, (int)memoryStream.Length);
                }
            }
    
            private static async void SaveToFile(String fileName, String content)
            {
                var storageFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
                await FileIO.WriteTextAsync(storageFile, content);
            }

    • Помечено в качестве ответа Andev 20 августа 2013 г. 6:47
    19 августа 2013 г. 20:20

Все ответы

  • Сериализуй через сторонюю библиотеку JSON.NET (nuget). Там все норм будет серилизоваться. Сам с таким сталкивался. Только все еще зависит от твоего объекта ObservableCollection
    • Помечено в качестве ответа Andev 19 августа 2013 г. 15:57
    18 августа 2013 г. 13:31
  • Сериализуй через сторонюю библиотеку JSON.NET (nuget). Там все норм будет серилизоваться. Сам с таким сталкивался. Только все еще зависит от твоего объекта ObservableCollection

    а DataContractSerializer вы рассматривали? http://stackoverflow.com/questions/15413468/deserializing-xml-with-datacontractserializer 

    почему вы решили использовать стороннюю библиотеку?

    18 августа 2013 г. 13:58
  • Если честно, то JSON.NET быстрее сериализует и десериализует чем стандартные. А еще, у меня почему-то всегда ошибку выдавало при сериализации, и решил попробовать сторонюю библиотеку, а в ней такая же ошибка. А потом я нашел ошибку в коде, которая все исправила, и так если честно и осталась сторонняя библиотека. Хотя тоже самое можно провернуть и со стандартными мне кажется. Если нужно поподробнее что-то, попробуйте прислать кусок кода, посмотрю, помогу.
    18 августа 2013 г. 19:31
  • Сериализуйте не ObservableCollection, а просто массив Person[]. Помимо стандартного DataContractSerializer'a есть так же DataContractJsonSerializer.

    Если у вас минимальные данные для сериализации, то можно обойтись и этими классами. По поводу Json.NET - штука хорошая (хотя на моих данных DataContractJsonSerializer был быстрее), но если не хотите подключать сторонние сборки и зависеть от них, то лучше обойтись стандартными средствами.

    • Помечено в качестве ответа Andev 19 августа 2013 г. 15:56
    18 августа 2013 г. 20:54
  • Кирилл, спасибо за рекомендацию. Можете привести пример кода с сериализацией и десериализацией с использованием DataContractJsonSerializer, как это у вас реализвано?

    19 августа 2013 г. 8:44
  • Ну вот небольшой пример десериализации:

    private static T Deserialize<T>(String content, DataContractJsonSerializerSettings settings = null)
            {
                var serializer = settings == null ? new DataContractJsonSerializer(typeof(T)) : new DataContractJsonSerializer(typeof(T), settings);
                T response;
                using (var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(content)))
                {
                    response = (T)serializer.ReadObject(memoryStream);
                }
                return response;
            }

    • Помечено в качестве ответа Andev 19 августа 2013 г. 15:57
    19 августа 2013 г. 8:48
  • здесь еще пример нашел http://msdn.microsoft.com/ru-ru/library/bb412179.aspx
    19 августа 2013 г. 16:32
  • видимо функция для сериализации должна выглядеть так

            public async static void Serialize<Person>(Person[] collection, String content, DataContractJsonSerializerSettings settings = null)
            {
    
                var serializer = settings == null ? new DataContractJsonSerializer(typeof(Person)) : new DataContractJsonSerializer(typeof(Person), settings);
    
                using (var memoryStream = new MemoryStream())
                {
                    serializer.WriteObject(memoryStream, collection);
                    StreamWriter sr = new StreamWriter(memoryStream);
                    await sr.WriteAsync(content);
                }
            }

    функцию в AddPerson() для сохранения списка персон вызываю так (вызывается из класса страницы-настроек):

    MainPage.Serialize<MainPage.Person>(array, XMLFileForSerialization);

    До вызова функции сериализации массив array создается из коллекции:

    array = PersonCollection.ToArray();

    и путь для сохранения, который передается параметру content:

    public const string XMLFileForSerialization = "ms-appdata:///local/mypersons.xml";

    сам класс имеет аттрибуты:

            [DataContract]
            public class Person
            {
                [DataMember]
                public string Name { get; set; }
                [DataMember]
                public string Image { get; set; }
            }

    вроде все правильно?

    но появляется исключение:

    Additional information: Type 'Project.MainPage+Person[]' with data contract name 'ArrayOfMainPage.Person:http://schemas.datacontract.org/2004/07/Project' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.

    какой KnownType в данном случае необходимо указать?


    • Изменено Andev 19 августа 2013 г. 18:04
    19 августа 2013 г. 18:03
  • У меня следующий код отлично работает:

            IList<Person> persons = new List<Person>();
            persons.Add(new Person());
            persons.Add(new Person());
            persons.Add(new Person());
    
            var json = Serialize(persons.ToArray());
            SaveToFile("Persons.dat", json);
    
            public static String Serialize(Object value, DataContractJsonSerializerSettings settings = null)
            {
                var serializer = settings == null ? new DataContractJsonSerializer(value.GetType()) : new DataContractJsonSerializer(value.GetType(), settings);
                using (var memoryStream = new MemoryStream())
                {
                    serializer.WriteObject(memoryStream, value);
                    return Encoding.UTF8.GetString(memoryStream.ToArray(), 0, (int)memoryStream.Length);
                }
            }
    
            private static async void SaveToFile(String fileName, String content)
            {
                var storageFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
                await FileIO.WriteTextAsync(storageFile, content);
            }

    • Помечено в качестве ответа Andev 20 августа 2013 г. 6:47
    19 августа 2013 г. 20:20
  • Кирилл, поясните, пожалуйста, последний момент,

    если мы сохранили несколько объектов, то в файле Persons.dat у нас все объекты в формате JSON в одну строку, мы считываем эту строку в файле в строку textData

            public static async Task<string> LoadFromFile(string file)
            {
                StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
                StorageFile storageFile = await storageFolder.GetFileAsync(file);
                string textData = await Windows.Storage.FileIO.ReadTextAsync(storageFile);
                return textData;
            }

    затем передаем в метод Deserialize<Person>() 

    что в таком случае возвращает этот метод, массив объектов или нужно циклом пройти? 

    20 августа 2013 г. 17:35
  • Если вы сохраняли массив Person[], то и восстанавливать надо массив:Deserialize<Person[]>() 
    20 августа 2013 г. 19:42