Переименование свойства класса, помеченного с помощью DataMemberAttribute - PullRequest
2 голосов
/ 10 августа 2010

На самом деле у меня есть такой класс

[DataContract]
public class Item : IExtensibleDataObject
{
    [Browsable(false)]
    public virtual ExtensionDataObject ExtensionData { get; set; }

    [DataMember]
    public string ID { get; set; }

    [DataMember]
    public string Name { get; set; }

    public Item()
        : this(String.Empty, String.Empty)
    { }

    public Item(string id, string name)
    {
        ID = id ?? String.Empty;
        Name = name ?? String.Empty;
    }
}

Вы можете легко сериализовать и десериализовать его. Но теперь самое сложное:

Мне нужно переименовать свойство Name в FullName, и каждый новый сериализованный файл xml должен выходить с FullName, тогда как в старых файлах все еще можно читать.

Вывести новое имя свойства в новый файл довольно просто. Просто переименуйте собственность, и все готово.

Для совместимости я бы пометил новое имя свойства [DataMember(Name="Name")], но в этом случае, если я запишу этот класс обратно в файл, я получу записанное старое имя.

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

[DataMember(Name="Name")]
[DataMember]
public string FullName { get; set}

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

Единственный способ, которым я могу представить, - это создать класс LegacyItem, который не имеет функций или чего-то подобного, но выполняет старый DataContract. Кроме того, какой-то неявный / явный оператор для преобразования моего LegacyItem в Item. И последнее, но не менее важное: при десериализации мне нужно сделать несколько тестов (может быть, просто try/catch), чтобы узнать, какой класс я должен сказать DataContractSerializer для чтения в файле.

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

Ответы [ 2 ]

3 голосов
/ 29 января 2014

Если вы не против иметь в своем классе немного устаревшего кода (который в любом случае будет меньше кода, чем весь предложенный вами второй класс), то вы можете сделать это:

[DataContract]
public class Item : IExtensibleDataObject
{
    public virtual ExtensionDataObject ExtensionData { get; set; }

    [DataMember]
    public string ID { get; set; }

    [DataMember(Name = "Name", EmitDefaultValue = false)]
    private string _obsoleteName { get { return null; } set { if(value != null) FullName = value; } }

    [DataMember]
    public string FullName {get; set;}

    public Item()
        : this(String.Empty, String.Empty)
    { }

    public Item(string id, string name)
    {
        ID = id ?? String.Empty;
        FullName = name ?? String.Empty;
    }
}

Старый XML можно прочитать, а при десериализации он будет правильно назначен вашему новому свойству. Затем при сериализации только новое свойство будет сериализовано как FullName через установленный метод доступа. При сериализации старое свойство всегда возвращает ноль и не передается событием в сериализованный XML.

Также вы можете пометить устаревшее свойство как частное, чтобы оно не было видно вне класса.

Несколько советов, я предпочитаю не использовать автоматическое свойство для DataMember свойств. На самом деле я обычно отмечаю поле поддержки как DataMember. Вероятно, лучше ВСЕГДА включать необязательный параметр Name для атрибута DataMember. Признаюсь, я не всегда делаю это сам, но это хорошая практика, потому что тогда вы случайно не нарушите свой контракт, если вы Refactor -> Rename свойство или поле в Visual Studio.

UPDATE

ОП Оливер сам предложил другое решение ... вот как это выглядит для интересующихся.

[DataContract]
public class Item : IExtensibleDataObject
{
    public virtual ExtensionDataObject ExtensionData { get; set; }

    [DataMember]
    public string ID { get; set; }

    [DataMember(Name = "Name", EmitDefaultValue = false)]
    private string _obsoleteName;

    [DataMember]
    public string FullName {get; set;}

    public Item()
        : this(String.Empty, String.Empty)
    { }

    public Item(string id, string name)
    {
        ID = id ?? String.Empty;
        FullName = name ?? String.Empty;
    }

    [OnDeserialized]
    void OnDeserialized(StreamingContext context)
    {
        if(_obsoleteName != null && FullName == null)
        {
            //upgrade old serialized object to new version
            //by copying serialized Name field to newer FullName
            FullName = _obsoleteName;

            //set _obsoleteName to null so that it stops serializing
            _obsoleteName = null;
        }
    }
}
0 голосов
/ 06 июня 2011

Вы должны взглянуть на интерфейс IExtensibleDataObject и соответствующее поле ExtensionDataObject.Для правильной работы потребуется некоторый пользовательский код, но он поможет решить вашу проблему.

...