继承类的输出无效

时间:2016-09-21 10:27:26

标签: c# xml inheritance datacontractserializer

我有2个班级

[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<A>的序列化部分。

4 个答案:

答案 0 :(得分:5)

这是因为您在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实例

答案 1 :(得分:1)

如果您还没有像其中一条评论中提到的那样自己完成,那么这里有一些代码我在摆弄。我暂时没有与AutoMapper合作,所以我无法记住并研究如何映射List<T>类型。无论如何,这里是小提琴:

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

答案 2 :(得分:1)

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

根据框架,将上面的类传递给datacontract序列化程序,它应该为您提供所需的结果。

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

答案 3 :(得分:1)

在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>