Серіалізація даних приватних членів


74

Я намагаюся серіалізувати об'єкт до XML, що має ряд властивостей, деякі з яких лише для читання.

public Guid Id { get; private set; }

Я позначив клас [Serializable] та реалізував інтерфейс ISerializable.

Нижче наведено код, який я використовую для серіалізації мого об’єкта.

public void SaveMyObject(MyObject obj)
{
    XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
    TextWriter tw = new StreamWriter(_location);
    serializer.Serialize(tw, obj);
    tw.Close();
}

На жаль, він потрапляє на перший рядок з цим повідомленням.

Не оброблено InvalidOperationException: неможливо створити тимчасовий клас (результат = 1). помилка CS0200: Властивість або індексатор 'MyObject.Id' неможливо призначити - він лише для читання

Якщо я встановив для властивості Id загальнодоступне, це буде добре. Хтось може сказати мені, чи я щось роблю, або, принаймні, якщо це можливо?

Відповіді:


62

Ви можете використовувати DataContractSerializer(але зверніть увагу, що ви не можете використовувати атрибути xml - лише елементи xml):

using System;
using System.Runtime.Serialization;
using System.Xml;
[DataContract]
class MyObject {
    public MyObject(Guid id) { this.id = id; }
    [DataMember(Name="Id")]
    private Guid id;
    public Guid Id { get {return id;}}
}
static class Program {
    static void Main() {
        var ser = new DataContractSerializer(typeof(MyObject));
        var obj = new MyObject(Guid.NewGuid());
        using(XmlWriter xw = XmlWriter.Create(Console.Out)) {
            ser.WriteObject(xw, obj);
        }
    }
}

Крім того, ви можете реалізувати IXmlSerializableі зробити все самостійно, але це XmlSerializerпринаймні працює.


Я змінив свій код, щоб використовувати DataContractSerializer, і я помітив, що в ньому все ще працює метод GetObjectData. Я правий, думаючи, що можу або розмістити атрибути у своїх властивостях, щоб їх серіалізувати, або я можу реалізувати інтерфейс ISerializable?
Джон Мітчелл,

Якщо ви впроваджуєте ISerializable (або це IXmlSeializable?), Ви в основному виконуєте всю роботу самостійно ...
Marc Gravell

4
Це спрацювало для мене, але пізніше я виявив, що серіалізація приватних членів за допомогою DataMemberAttribute працює лише при запуску в повному довірі, а не в частковому. Рішенням є створення учасника внутрішнім, а не приватним. Детальніше див. Blog.walteralmeida.com/2010/05/… .
Пеладао,

6

Ви можете використовувати System.Runtime.Serialization.NetDataContractSerializer. Він є потужнішим і вирішує деякі проблеми класичного серіалізатора Xml.

Зверніть увагу, що для цього існують різні атрибути.

[DataContract]
public class X
{
  [DataMember]
  public Guid Id { get; private set; }
}


NetDataContractSerializer serializer = new NetDataContractSerializer();
TextWriter tw = new StreamWriter(_location);
serializer.Serialize(tw, obj);

Редагувати:

Оновлення на основі коментаря Марка: Ви, мабуть, повинні використовувати System.Runtime.Serialization.DataContractSerializerдля своєї справи отримання чистого XML. Решта коду однакова.


NetDataContractSerializer не пише xml ... - точніше, це не чистий xml, придатний для зовнішнього споживання - у ньому є метадані збірки.
Марк Гравелл

@Marc: Дякую за підказку. Це завжди залежить від того, чого хочеться досягти. DataContractSerializer - це, мабуть, те, що тут очікують.
Стефан Штайнеггер,

2

Поля лише для читання не будуть серіалізовані за допомогою XmlSerializer, це пов'язано з природою readonlyключового слова

З MSDN:

Читання ключових слів є модифікатором , який можна використовувати на полях. Коли декларація поля включає модифікатор лише для читання, присвоєння полям, введеним декларацією, може відбуватися лише як частина декларації або в конструкторі того самого класу.

Отже ... вам майже потрібно було б встановити значення полів у конструкторі за замовчуванням ...


Я думав, що оскільки я реалізував метод ISerializable.GetObjectData, XmlSerializer буде використовувати це для отримання інформації, яку я хотів серіалізувати, а не намагатися отримати доступ до моїх властивостей лише для читання.
Джон Мітчелл,

XmlSerializer не дбає про ISerializable - лише IXmlSerializable
Marc Gravell

0

Це неможливо з цим конкретним режимом серіалізації (див. Інші коментарі щодо обхідних шляхів). Якщо ви дійсно хочете залишити режим серіалізації як є, вам доведеться обійти рамкові обмеження цього. Див. Цей приклад

По суті, позначте властивість public, але видайте виняток, якщо до нього звертаються в будь-який час, крім десеріалізації.


5
"але кинь виняток" - оскільки XmlSerializer не підтримує зворотних викликів серіалізації, ти не знаєш про це ...
Марк Гравелл

1
Ви можете використати, System.Diagnostics.StackTraceщоб дізнатись, що викликає вашу власність, але я б не рекомендував такого рішення :-)
Луї Сомерс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.