“Type”属性的DataContract序列化

Posted

技术标签:

【中文标题】“Type”属性的DataContract序列化【英文标题】:DataContract serialization of property of "Type" 【发布时间】:2011-11-21 06:08:17 【问题描述】:

如何在我的 DataContract 属性类中有效地序列化类型为“Type”的属性?我假设 Type 是一个不可序列化的类型(哇,这听起来很愚蠢。)我确信有一种方法可以满足我的需求。基本上我需要序列化一个类型的名称,以便工厂方法有效地构造,但我不想将它公开为字符串,我想要一个类型。

我知道有很多方法可以做到这一点,我很好奇目前还知道哪些其他方法。

编辑: 我刚刚意识到这可能是其他原因造成的,但这是错误,下面我有类定义。

不应使用数据合同名称“RuntimeType:http://schemas.datacontract.org/2004/07/System”键入“System.RuntimeType”。考虑使用 DataContractResolver 或将任何静态未知的类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 属性或将它们添加到传递给 DataContractSerializer 的已知类型列表中。

[DataContract]
public class PlottingDeviceInfo : ObservableObject

    private string _deviceName;
    [DataMember]
    public string DeviceName
    
        get
        
            return _deviceName;
        
        set
        
            Set(() => DeviceName, ref _deviceName, value);
        
    

    private Type _deviceType;
    [DataMember]
    public Type DeviceType
    
        get
        
            return _deviceType;
        
        set
        
            Set(() => DeviceType, ref _deviceType, value);
        
    

    private DeviceSettingsInfo _settings;
    [DataMember]
    public DeviceSettingsInfo Settings
    
        get
        
            return _settings;
        
        set
        
            Set(() => Settings, ref _settings, value);
        
    

    private DeviceChannelInfo _channel;
    [DataMember]
    public DeviceChannelInfo Channel
    
        get
        
            return _channel;
        
        set
        
            Set(() => Channel, ref _channel, value);
        
    

    private DeviceCategory _deviceCategory;
    [IgnoreDataMember]
    public DeviceCategory DeviceCategory
    
        get
        
            return _deviceCategory;
        
        set
        
            Set(() => DeviceCategory, ref _deviceCategory, value);
        
    

这里是基类,用于增加 viewmodel 消费的可观察性。

[DataContract]
public class ObservableObject : INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    [IgnoreDataMember]
    protected PropertyChangedEventHandler PropertyChangedHandler
    
        get
        
            return PropertyChanged;
        
    

    [Conditional("DEBUG")]
    [DebuggerStepThrough]
    public void VerifyPropertyName(string propertyName)
    
        var myType = this.GetType();
        if (!string.IsNullOrEmpty(propertyName)
            && myType.GetProperty(propertyName) == null)
        
            throw new ArgumentException("Property not found", propertyName);
        
    

    protected virtual void RaisePropertyChanged(string propertyName)
    
        VerifyPropertyName(propertyName);

        var handler = PropertyChanged;

        if (handler != null)
        
            handler(this, new PropertyChangedEventArgs(propertyName));
        
    

    protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
    
        if (propertyExpression == null)
        
            return;
        

        var handler = PropertyChanged;

        if (handler != null)
        
            var body = propertyExpression.Body as MemberExpression;
            handler(this, new PropertyChangedEventArgs(body.Member.Name));
        
    

    protected void Set<T>(
        Expression<Func<T>> propertyExpression,
        ref T field,
        T newValue)
    
        if (EqualityComparer<T>.Default.Equals(field, newValue))
        
            return;
        

        field = newValue;
        RaisePropertyChanged(propertyExpression);
    

    protected void Set<T>(
        string propertyName,
        ref T field,
        T newValue)
    
        if (EqualityComparer<T>.Default.Equals(field, newValue))
        
            return;
        

        field = newValue;
        RaisePropertyChanged(propertyName);
    

【问题讨论】:

编辑时出现序列化错误。 【参考方案1】:

Type 不能用跨平台的方式表示,所以它没有内置的表示。你最好的办法是将它表示为一个字符串,即

public Type DeviceType  get; set; 
[DataMember(Name="DeviceType")]
private string DeviceTypeName 
    get  return DeviceType == null ? null : DeviceType.AssemblyQualifiedName; 
    set  DeviceType = value == null ? null : Type.GetType(value); 

【讨论】:

马克:我很惊讶你会这么说。 .NET 3.5SP1 中的 DataContractSerializer 支持 ISerializable 实现......并且 System.RuntimeType 实现 ISerializable。添加一个 KnownType 就足够了。也就是说,序列化 AssemblyQualified 名称而不是使用内置的 ISerializable 实现不一定是错误的。 @JeffN825 这完全取决于您想在网络上看到什么。如果添加了ISerializable...那很有趣但是...嗯 - 仍然不确定这是否使它理想就线路上发生的事情而言。跨度> 同意。远非理想,但尽管如此,Type (RuntimeType) 可以通过网络进行序列化【参考方案2】:

在根类中添加KnownType 属性,并将System.RuntimeType 作为类型传入。

在 C# 中,

[KnownType(typeof(System.RuntimeType))]

【讨论】:

什么是“根类”,您的意思是基类吗?还是你的意思是我正在序列化的类。 你要序列化的类。 erm...没有这种类型...你是说System.Dynamic.DynamicMetaObject.RuntimeType吗? 哪种类型?输入 DeviceType get;set; 我在外部代码中明确设置...我们是在完全不同的页面上吗? 它不是公开的,因此由于保护级别,您无法添加。【参考方案3】:

您还可以做的,特别是如果您不想更改PlottingDeviceInfo,是在DataContractSerializer 的构造函数中传递一个IDataContractSurrogate。我所做的(不知道是否有更简单的方法)是定义

public class TypeWrapper

    public string TypeName;

然后像这样使用它:

public class TypeReplacementSurrogate : IDataContractSurrogate

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

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

    public Type GetDataContractType(Type type)
    
        if(type == typeof(Type))
        
            return typeof (TypeWrapper);
        

        return type;
    

    public object GetDeserializedObject(object obj, Type targetType)
    
        var objAsTypeWrapper = obj as TypeWrapper;
        if (objAsTypeWrapper != null)
        
            return Assembly.GetExecutingAssembly().GetType(objAsTypeWrapper.TypeName);
        

        return obj;
    


    public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
    
    

    public object GetObjectToSerialize(object obj, Type targetType)
    
        var objAsType = obj as Type;
        if (objAsType != null)
        
            return new TypeWrapper() TypeName = objAsType.FullName;
        

        return obj;
    

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

    public System.CodeDom.CodeTypeDeclaration ProcessImportedType(
        System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
    
        return null;
    

【讨论】:

以上是关于“Type”属性的DataContract序列化的主要内容,如果未能解决你的问题,请参考以下文章

DataContract XML 序列化和 XML 属性

如何找出类是不是具有 DataContract 属性?

如何使用 DataContract 添加 XML 属性

配置 JSON.NET 以忽略 DataContract/DataMember 属性

如何在不破坏向后兼容性的情况下更改 DataContract 属性的类型?

我可以同时使用 DataContract 和 Serializable 吗?