如果在类中反序列化接口属性,如何定义哪个类?

时间:2010-05-17 11:13:21

标签: c# interface datacontractserializer

想象一下你有以下课程

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

    [DataMember]
    public IList<string> Items { get; private set; }

    public DumpList(string name)
    {
        Name = name;
        Items = new List<string>();
    }
}

如果将其序列化为文件,则很容易,因为IList后面的具体类是已知的并且可以序列化。

但是如果你试图将这个文件反序列化到内存中会发生什么呢? 它没有任何直接错误发生。

如果您尝试在列表中添加或删除某些内容,则会出现问题。在这种情况下,你会得到一个例外。此异常的根源于反序列化对象用作IList和Array的具体实现的情况。

在这个简单的例子中避免这个问题很容易。只是序列化具体的后备存储而不是公共属性,并在构造函数中进行更改:

[DataMember(Name = "Items")]
private List<string> _Items;

public IList<string> Items
{
    get
    {
        return _Items;
    }
}

public DumpList(string name)
{
    Name = name;
    _Items = new List<string>();
}

但更有趣的问题是:

  • 为什么选择Deserializer将Array类型作为IList接口的具体实现?
  • 是否可以更改每个界面应采用的类设置?
  • 如果我有一个自定义的接口和这个接口的几个实现,是否可以告诉Deserializer应该为给定的接口采用哪个具体类?

2 个答案:

答案 0 :(得分:3)

您可以使用DataContractSurrogate进行反序列化来解决此问题,将IList替换为List。

public class CustomDataContractSurrogate : IDataContractSurrogate
{
    // The only function you should care about here. The rest don't do anything, just default behavior.
    public Type GetDataContractType(Type type)
    {
        if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(ICollection<>)))
        {
            return (typeof(List<>).MakeGenericType(type.GetGenericArguments().Single()));
        }
        return type;
    }

    public object GetObjectToSerialize(object obj, Type targetType)
    {
        return obj;
    }

    public object GetDeserializedObject(object obj, Type targetType)
    {
        return obj;
    }

    public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
    {
        return null;
    }

    public object GetCustomDataToExport(Type clrType, Type dataContractType)
    {
        return null;
    }

    public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
    {
    }

    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
    {
        return null;
    }

    public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
    {
        return typeDeclaration;
    }
}

基本上就是这样,您只需要使用该代理创建DataContractSerializer实例并将其用于反序列化(对于序列化它无关紧要),例如:

var serializer = new DataContractSerializer(type, new Type[]{}, Int32.MaxValue, false, true, new CustomDataContractSurrogate());

或采取代理的任何其他构造函数。

,(作为答案的奖励)如果您正在使用app / web.config定义的服务,您可以定义一个自定义行为,使用上述内容创建数据协定序列化程序替代:

public class CustomDataContractSerializerBehavior : DataContractSerializerOperationBehavior
{
    public CustomDataContractSerializerBehavior(OperationDescription operation)
        : base(operation)
    {
    }

    public CustomDataContractSerializerBehavior(OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute)
        : base(operation, dataContractFormatAttribute)
    {
    }

    public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns,
        IList<Type> knownTypes)
    {
        return new DataContractSerializer(type, knownTypes, Int32.MaxValue, false, true, new CustomDataContractSurrogate());
    }

    public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name,
        XmlDictionaryString ns, IList<Type> knownTypes)
    {
        return new DataContractSerializer(type, knownTypes, Int32.MaxValue, false, true, new CustomDataContractSurrogate());
    }

}

最后您可以使用此行为:

public static IMyDataServiceContract CreateService()
{
    var factory = new ChannelFactory<IMyDataServiceContract>("MyServiceName");
    SetDataContractSerializerBehavior(factory.Endpoint.Contract);
    return factory.CreateChannel();
}

private static void SetDataContractSerializerBehavior(ContractDescription contractDescription)
{
    foreach (OperationDescription operation in contractDescription.Operations)
    {
        ReplaceDataContractSerializerOperationBehavior(operation);
    }
}

private static void ReplaceDataContractSerializerOperationBehavior(OperationDescription description)
{
    DataContractSerializerOperationBehavior dcsOperationBehavior =
    description.Behaviors.Find<DataContractSerializerOperationBehavior>();

    if (dcsOperationBehavior != null)
    {
        description.Behaviors.Remove(dcsOperationBehavior);
        description.Behaviors.Add(new CustomDataContractSerializerBehavior(description));
    }
}

要完成作业,请在某处调用上面的CreateService来创建频道。

答案 1 :(得分:1)

如果使用存储类型信息的NetDataContractSerializer以及序列化对象,则应解决问题。但是,它同时减少了与非.NET客户端的互操作性。