【问题标题】:NetDataContractSerializer Deserialization With New Property具有新属性的 NetDataContractSerializer 反序列化
【发布时间】:2015-11-19 20:16:03
【问题描述】:

由于缺乏任何真正的远见,我使用 NetDataContractSerializer 序列化了大量仅使用 Serializable 修饰的数据,现在我想添加一个新字段。我有哪些选择?

原始类看起来像这样(有几个继承级别和很多字段):

[Serializable]
public class InheritedClass : BaseClass
{
    public string StringId { get; set; }
}

现在我想添加另一个属性,比如:

[Serializable]
public class InheritedClass : BaseClass
{
    public string StringId { get; set; }
    public int IntId { get; set; }
}

现在当我更新类并去反序列化时,我收到一个异常,因为新字段不存在,类似于:

Exception thrown: 'System.Runtime.Serialization.SerializationException' in System.Runtime.Serialization.dll

Additional information: Error in line 1 position 601. 'Element' '_x003C_StringId_x003E_k__BackingField' from namespace 'http://schemas.datacontract.org/2004/07/QT' is not expected. Expecting element '_x003C_IntId_x003E_k__BackingField'.

好的,这是有道理的,因为 NetDataContractSerializer 需要相同的类。我可以使用 DataMember 属性解决这个问题,例如:

[DataMember(IsRequired = false)]

然后的问题是切换到 DataMember(正如我应该预先做的那样,或者使用不同的序列化程序)会改变隐式的字母顺序,然后我的大部分字段将不会像众所周知的那样默默地反序列化。

我尝试手动添加与磁盘上的排序一致的排序(通过属性上的Order 属性),但这似乎也没有得到尊重。 (我也没有在原始 xml 中看到可以匹配的订单值。)

除了写一些东西来加载 xml 并插入丢失的节点之外,还有其他选择吗? (或者等效地设置一个并行类型并从一个重新序列化到另一个反序列化?)如果没有,我可能只是加载当前类型并反序列化到 JsonNet 或 protobuf,但我是否缺少使用 DataMember/ 更直接的东西等等?

【问题讨论】:

    标签: c# xml serialization netdatacontractserializer


    【解决方案1】:

    [Serializable] 标记一个类型意味着可以通过序列化它的public and private fields 来序列化该类型——而不是它的属性。 NetDataContractSerializer 在存在时尊重此属性,并按照指示序列化字段。对于auto-implemented property,秘密支持字段是实际序列化的内容。

    添加新字段时,处理遗留数据的一般做法是用[OptionalField] 标记它,以表明它不会总是出现在序列化流中。在 c# 7.3 及更高版本中,可以使用 field-targeted attribute 对自动实现的属性的秘密支持字段执行此操作:

    [Serializable]
    public class InheritedClass : BaseClass
    {
        public string StringId { get; set; }
    
        [field: OptionalField]
        public int IntId { get; set; }
    }    
    

    在 c# 7.3 之前,无法将属性应用于自动实现属性的支持字段。因此,您需要明确支持字段并向其添加属性:

    [Serializable]
    public class InheritedClass : BaseClass
    {
        public string StringId { get; set; }
    
        [OptionalField]
        int intId;
    
        public int IntId { get { return intId; } set { intId = value; } }
    }    
    

    注意事项:

    • 如问题中所述,如果类型标记为 data contract attributes,则 NetDataContractSerializer 将优先使用默认的 [Serializable] 合同,并允许您明确指示要序列化的属性(并提供比秘密支持字段名称)。

      不幸的是,将数据协定属性添加到旧类型并不总是可行的。

    • NetDataContractSerializer has not been ported to .NET Core / .NET 5 可能还有never will be

    【讨论】:

    • 我相信您实际上可以使用语法[field: OptionalField] 使用自动属性做到这一点。
    • @SodAlmighty 嗯,看起来是正确的。但是field-targeted attributes 是在我写这篇文章近 2.5 年后在 c# 7.3 中引入的。将检查和更新。
    猜你喜欢
    • 2010-12-25
    • 2012-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多