“Type”属性的DataContract序列化

11
如何在我的DataContract属性类中有效地序列化一个“Type”类型的属性?我假设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);
        }
    }
}

以下是基础类,用于为视图模型提供可观察性。

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

序列化错误已编辑。 - Firoso
3个回答

12

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实现并不一定是错误的。 - Jeff
@JeffN825 这完全取决于您想在线路上看到什么。如果添加了ISerializable...那很有趣,但是...还不确定它是否在线路上是可取的 - Marc Gravell
同意。虽然远非理想,但是类型(RuntimeType)确实可以在网络上进行序列化。 - Jeff

0
在根类中,添加带有传入 System.RuntimeType 类型的 KnownType 属性。
在C#中,
[KnownType(typeof(System.RuntimeType))]

1
“Root class”是什么意思?你是指基类吗?还是指我正在序列化的类? - Firoso
2
嗯...没有这种类型...你是指System.Dynamic.DynamicMetaObject.RuntimeType吗? - Firoso
从哪一端开始?序列化?还是编程方式? - Firoso
O_O 是一个类型!那又怎样,我正在使用来自我的程序集的明确定义的类型。 - Firoso
4
因为保护级别的原因,它不是公共的,所以您无法添加。 - Tiju John
显示剩余3条评论

0

如果您不想更改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;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接