DataContractSerializer属性的反序列化从派生类移动到基类

时间:2012-05-10 20:39:22

标签: c# .net datacontractserializer

使用DCS我试图从XML反序列化对象,其中序列化对象的类型为Child继承类Base,其中Child有一些属性被反序列化但稍后被移动到代码中的类Base。现在这些属性不会被反序列化:

看看这个XML:

<Base i:type="a:DirectoryEntry" xmlns="http://schemas.datacontract.org/2004/07/pending.Core.Models" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:a="http://schemas.datacontract.org/2004/07/pending.Core.Models.Directory">
    <Active>true</Active>
    <ContentType>DirectoryEntry</ContentType>
    <Created>2012-03-12T11:51:25.3401552+01:00</Created>
    <a:Location>location</a:Location>
    <a:OpenHours>opening</a:OpenHours>
</Base>

xmlns:a表示派生类型名称。这个类用于保存那些道具。现在它们被移动到Base类中,它们不再被DCS反序列化(反序列化后属性为null)。我在谈论属性:在他们面前(在这种情况下是位置和OpenHours)。

我们有很多这些文件保存数据。使用DCS正确反序列化这些文件有哪些选择?

请注意,Base和Child类同时使用[KnownType]属性

4 个答案:

答案 0 :(得分:1)

将属性移动到基类时,它们有效地移动到另一个XML命名空间。基类可能是在命名空间pending.Core.Models定义的,而派生类是在pending.Core.Models.Directory定义的(我假设你没有在Namespace的{​​{1}}属性中指定任何内容。因此,如果您更改了元素所在的命名空间,那么序列化程序将无法将其与您拥有的数据相匹配。如果您将最后两个属性移动到基类,那么您将需要更改XML元素[DataContract]Location的命名空间。

OpenHours

答案 1 :(得分:0)

我的解决方案是将属性移回原始类,并将它们保留在首次移动到的类中。它现在给出了Property hides the inherited member...Use the new keyword if hiding was intended的警告,但我可以忍受,因为反序列化现在再次起作用,我们可以将数据从一个提供者移动到另一个提供者。我无法找到任何其他解决方案,并且无法修改所有序列化数据文件。

答案 2 :(得分:0)

正如母马在上面(或下面)所提到的那样,在接受的答案中,没有解决方案不具有原始类中的属性。但是,通过在原始类的属性声明中使用new关键字,可以禁用警告。我还得到了以下结构:

[DataContract]
class Base
{
    [DataMember]
    public int X { get; set; }
} 

[DataContract]
class Derived : Base
{
    [DataMember]
    private new int X
    {
        get => base.X;
        set => base.X = value;
    }
}

DataContractSerializer通过反射读取属性,因此它并不真正关心属性是私有的,但是将该属性放在其原始位置是私有的,从而阻止了代码的使用(按预期使用)。

答案 3 :(得分:0)

基于@mare的解决方案,您可以考虑另一种方法。

如果将属性更改为非自动属性,并使用 named DataMember属性标记私有字段,则序列化程序将读取并写入私有字段。他们不必公开。举例来说,您在一个类中具有以下属性:

[DataContract]
class SomeClass
{

  [DataMember( Name = "SomeProp")]  //--> note that we're decorating the field
  private int someProp;
  public int SomeProp
  {
    get { return someProp; }
    set { someProp = value; }
  }

}

然后,当您决定需要对类型进行子类化并且仅希望通过子类公开该属性时,可以删除 Property ,但将字段保留为受保护成员< / em>:

[DataContract]
class SomeClass
{

  [DataMember( Name = "SomeProp")]
  protected int someProp;           //--> change to protected

}

...然后在派生类中,将该属性放回:

[DataContract]
class DerivedClass: SomeClass
{

  public int SomeProp
  {
    get { return someProp; }
    set { someProp = value; }
  }

}

这很好用,并且符合DataContractSerializer的序列化规则(首先是基本类型,然后是派生类型,并且每个成员都按alpha顺序排列)...而且您不会得到混乱的警告。

缺点是,您不能使用自动属性...但这并非不合理。

此方法的另一个好处是,DataContractSerializer在反序列化对象时最终不会行使您的属性set的行为。长期以来,摆脱具有set类型的DataContract行为的属性一直是我最喜欢的最佳实践