【问题标题】:Invalid output with inherit class继承类的无效输出
【发布时间】:2016-09-21 10:27:25
【问题描述】:

我有两节课

[DataContract, KnownType(typeof(B))]
public class A
{
    [DataMember]
    public string prop1 { get; set; }
    [DataMember]
    public string prop2 { get; set; }
    [DataMember]
    public string prop3 { get; set; }
}

[DataContract]
public class B : A
{
    [DataMember]
    public string prop4 { get; set; }
}

还有以下方法:

List<B> BList = new List<B>();
BList = new List<B>() { new B() { prop1 = "1", prop2 = "2", prop3 = "3", prop4 = "4" } };
List<A> AList = BList.Cast<A>().ToList();
DataContractSerializer ser = new DataContractSerializer(typeof(List<A>));
FileStream fs = new FileStream(@"C:\temp\AResult.xml", FileMode.Create);
using (fs)
{
    ser.WriteObject(fs, AList);
}

将其写入输出的 XML 文件:

<ArrayOfProgram.A xmlns="http://schemas.datacontract.org/2004/07/foo" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Program.A i:type="Program.B">
<prop1>1</prop1>
<prop2>2</prop2>
<prop3>3</prop3>
<prop4>4</prop4>
</Program.A></ArrayOfProgram.A>

如何发生这种情况,prop4 在结果中,我该如何避免这种情况? prop4 不是正在序列化的List&lt;A&gt; 的一部分。

【问题讨论】:

  • prop4 不是A 类的一部分,但List&lt;A&gt; 可以存储B 类型的对象,因此当您序列化List&lt;A&gt; 的元素时,序列化程序将采用存储的实际类型(不仅仅是列表的元素类型)。

标签: c# xml inheritance datacontractserializer


【解决方案1】:

这是因为您在 AList 中存储了指向 B 对象实例的指针。当您执行 "new B() { prop1 = "1", prop2 = "2", prop3 = "3", prop4 = "4" }" 时,您创建了一个 B 对象实例。 当序列化器反映存储在AList中的对象时,它会找到一个实际的B对象实例,因为您没有更改B对象实例,只是将它存储在AList中。编译器允许您这样做,因为继承链允许这样做,但 B 对象实例未更改,那么无论您将其存储在何处,它都是 B 对象实例。

而不是做:

List<A> AList = BList.Cast<A>().ToList();

做:

List<A> AList = BList.Select(b => new A() 
{ prop1 = b.prop1, prop2 = b.prop2, prop3 = b.prop3 })
.ToList();

这将为 BList 中的每个 B 实例创建一个新的 A 实例

【讨论】:

  • 这可行,但有很多属性 - 没有更简单的方法吗?
  • 为了更轻松地进行此属性映射,您可以考虑使用 automapper。 automapper.org
【解决方案2】:

DataContractResolver 允许您自定义如何解析 DataContract。在这种情况下,您只需要将子类型解析为基本类型。

以下代码来自这篇博文。

https://blogs.msdn.microsoft.com/youssefm/2009/06/05/configuring-known-types-dynamically-introducing-the-datacontractresolver/

public class DeserializeAsBaseResolver : DataContractResolver
{
    public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
    {
        return knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace);
    }

    public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
    {
        return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ?? declaredType;
    }
}

根据框架,将上述类传递给数据合同序列化程序,它应该会给您所需的结果。

DataContractSerializer ser = new DataContractSerializer(typeof(List<A>));), null, Int32.MaxValue, false, false, null, new DeserializeAsBaseResolver ());

【讨论】:

    【解决方案3】:

    在 C# 中向下转换的一种简单方法是序列化子级,然后将其反序列化到父级中。

    List<B> BList = new List<B>();
    BList = new List<B>() { new B() { prop1 = "1", prop2 = "2", prop3 = "3", prop4 = "4" } };
    var serializedChildList = JsonConvert.SerializeObject(BList);
    List<A> AList = JsonConvert.DeserializeObject<List<A>>(serializedChildList);
    DataContractSerializer ser = new DataContractSerializer(typeof(List<A>));
    FileStream fs = new FileStream(@"C:\temp\AResult.xml", FileMode.Create);
    using (fs)
    {
        ser.WriteObject(fs, AList);
    }
    

    样本输出:

    <ArrayOfA xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <A>
        <prop1>1</prop1>
        <prop2>2</prop2>
        <prop3>3</prop3>
      </A>
    </ArrayOfA>
    

    【讨论】:

      【解决方案4】:

      如果您还没有像其中一个 cmets 中提到的那样自己完成,这里是我摆弄的一个小代码。我有一段时间没有使用AutoMapper,所以我不记得也想不出如何映射List&lt;T&gt; 类型。无论如何,这是小提琴:

      var list = new List<B> { new B { prop1 = "1", prop2 = "2", prop3 = "3", prop4 = "4" } };
      Mapper.Initialize(i => i.CreateMap<B, A>());
      using (var stream = new FileStream(@"output.xml", FileMode.Create))
      {
          var serializer = new DataContractSerializer(typeof(List<A>));
          serializer.WriteObject(stream, list.Select(i => Mapper.Map<A>(i)).ToList());
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-03-11
        • 2015-03-14
        • 1970-01-01
        • 1970-01-01
        • 2015-04-28
        • 2021-10-18
        • 1970-01-01
        相关资源
        最近更新 更多