使用DataContractSerializer兼容Serializable和DataContract对象

时间:2014-04-15 08:42:55

标签: c# xml wcf xml-serialization

我有两个类,一个继承另一个类 - 基类是DataContract,子类是Serializable。我想将属性从子类移动到基类,但在移动之后我得到例外,说在反序列化子类时没有找到BackingProperty。如何正确地将该属性移动到基类?

2 个答案:

答案 0 :(得分:0)

您是否使用KnownType属性修饰了基类?

[KnownType(typeof(SuperClass))]
[DataContract]
public class BaseClass
{
...
}

答案 1 :(得分:0)

这是因为Serializable标记类和DataContract的序列化有点不同。

这是我用于样本的序列化器:

 var ser = new DataContractSerializer(typeof(BaseClass), new List<Type>() { typeof(ChildClass) });

您可以比较:

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

[Serializable]
public class ChildClass: BaseClass
{
    public string SecondName { get; set; }
    public int Age { get;set; }
}

该类的Xml(使用DataContractSerializer,这是WCF的标准):

<BaseClass i:type="ChildClass" xmlns="http://schemas.datacontract.org/2004/07/Serialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Name>Ivan</Name>
  <_x003C_Age_x003E_k__BackingField>1</_x003C_Age_x003E_k__BackingField>
  <_x003C_SecondName_x003E_k__BackingField>Petrov</_x003C_SecondName_x003E_k__BackingField>
</BaseClass>

但是当您将属性移动到基类时,标记为广告DataContract:

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

}

[Serializable]
public class ChildClass: BaseClass
{

    public int Age { get;set; }
}

以下是该案例的xml:

<BaseClass i:type="ChildClass" xmlns="http://schemas.datacontract.org/2004/07/Serialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Name>Ivan</Name>
  <SecondName>Petrov</SecondName>
  <_x003C_Age_x003E_k__BackingField>1</_x003C_Age_x003E_k__BackingField>
</BaseClass>    <BaseClass i:type="ChildClass" xmlns="http://schemas.datacontract.org/2004/07/Serialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Name>Ivan</Name>
  <SecondName>Petrov</SecondName>
  <_x003C_Age_x003E_k__BackingField>1</_x003C_Age_x003E_k__BackingField>
</BaseClass>

因此,你在Xml中有不同的元素名称(SecondName vs _x003C_SecondName_x003E_k__BackingField),这两个xml真的不同。

最好在解决方案中使用通用方法,标记所有内容或Serializable或DataContract。我建议使用DataContract。

编辑1:

当然,您可以尝试使用属性命名来制作技巧:

[DataMember(Name = @"_x003C_SecondName_x003E_k__BackingField", IsRequired = true)]
public string SecondName { get; set; }

结果中XML将完全相同。您还可以尝试为Serializable对象而不是属性公开feild:

[Serializable]
public class ChildClass : BaseClass
{
    public string SecondName;// { get; set; }
    public int Age; //{get;set;}
}

xml将是:

<BaseClass i:type="ChildClass" xmlns="http://schemas.datacontract.org/2004/07/Serialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Name>Ivan</Name>
  <Age>1</Age>
  <SecondName>Petrov</SecondName>
</BaseClass>

但是,不幸的是,如果将属性从ChildClass移动到BaseClass,属性的顺序将会改变。但DataContractSerializer始终在反序列化期间强制执行元素的顺序,因此不会对所有字段进行反序列化。但您可以切换到服务中使用XmlSerializer,它支持无序反序列化。

最后:使用DataContract标记所有数据合同

即使您使用DataContract标记您的类,DataMember:

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

[DataContract]
public class ChildClass : BaseClass
{
    //[DataMember]
    //public string SecondName { get; set; }
    [DataMember]
    public int Age {get;set;}
}

然后将属性从ChildClass移动到BaseClass,它仍然无效。您的属性将为null(或默认值),因为订单已更改:(