重命名使用DataMemberAttribute标记的类属性

时间:2010-08-10 09:26:09

标签: c# .net-3.5 datacontractserializer datacontract

其实我有这样的课程

[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 个答案:

答案 0 :(得分:3)

如果您不介意在您的课程中使用一些过时的代码(无论如何代码将少于您建议的整个第二课程),那么您可以这样做:

[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,并且反序列化将正确分配给您的新属性。然后在序列化时,只有新属性将通过set访问器序列化为FullName。序列化时,旧属性始终返回null,并且不会将事件发送到序列化XML。

此外,您可以将过时的属性标记为私有,以便在类外部不可见。

未来的一些提示,我更喜欢不为DataMember属性使用自动属性。实际上,我通常将支持字段标记为DataMember。最好始终为Name属性包含可选的DataMember参数。我承认我并不总是这样做,但这是一个很好的做法,因为如果你重构,那么你不会意外地违反合同 - >在Visual Studio中重命名属性或字段。

更新

OP Oliver本人提出了另一种解决方案......对于那些感兴趣的人来说,这就是这样。

[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;
        }
    }
}

答案 1 :(得分:0)

您应该查看IExtensibleDataObject界面和相应的ExtensionDataObject字段。它需要一些自定义代码才能使其正常运行,但有助于解决您的问题。