DataContract 无法序列化集合成员

Posted

技术标签:

【中文标题】DataContract 无法序列化集合成员【英文标题】:DataContract not able to serialize collection members 【发布时间】:2020-03-09 14:56:18 【问题描述】:

我的数据最好被描述为“洋葱状”,因为每个外层都建立在它下面的一层之上。下面您将看到一个大大简化的版本(我的版本更深几层,但在每个级别都表现出相同的行为)。

[CollectionDataContract]
public abstract class AbstractTestGroup : ObservableCollection<AbstractTest>

    [DataMember]
    public abstract string Name  get; set; 


[CollectionDataContract]
[KnownType(typeof(Test))]
public class TestGroup : AbstractTestGroup

    public override string Name
    
        get  return "TestGroupName"; 
        set  
    

    [DataMember]
    public string Why  get  return "Why";  set   


[DataContract]
public abstract class AbstractTest

    [DataMember]
    public abstract string SayHello  get; set; 



[DataContract]
public class Test : AbstractTest

    //Concrete class - members in this class get serialized
    [DataMember]
    public string Month  get  return "June";  set   

    public override string SayHello  get  return "HELLO";  set   

我创建了一个TestGroup 的实例,并使用ObservableCollection 附带的.Add 向其中添加Test 对象。

当我序列化和反序列化这个结构时,我得到以下内容

<TestGroup xmlns="http://schemas.datacontract.org/2004/07/WpfApplication2" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <AbstractTest i:type="Test">
        <SayHello>HELLO</SayHello>
        <Month>June</Month>
    </AbstractTest>
</TestGroup>

输出已离开TestGroup 中的DataMembers。随着我对洋葱的深入了解,不包括更高的DataMembers(即使来自抽象类)。我尝试将[KnownType(typeof(TestGroup))] 添加到TestGroupAbstractTestGroup 均未成功。

问题:为什么我不能序列化TestGroup类中的DataMemberWhy

后续问题:是否有另一种方法来序列化和反序列化这种形状的结构?我计划在本地使用输出来“加载”用户指定的配置。如果可以避免的话,我宁愿不必指定自己的序列化方案。


对于那些感兴趣的人来说,我是如何生成类、序列化和反序列化的。

TestGroup tg = new TestGroup();
tg.Add(new Test());

DataContractSerializer ser = new DataContractSerializer(typeof(TestGroup));
MemoryStream memoryStream = new MemoryStream();
ser.WriteObject(memoryStream, tg);

memoryStream.Seek(0, SeekOrigin.Begin);
string str;
using (StreamReader sr = new StreamReader(memoryStream))
    str = sr.ReadToEnd();

编辑:值得我尝试改用 Serializable 并遇到同样的问题。

【问题讨论】:

另见c-sharp-xml-serializing-listt-descendant-with-xml-attribute 【参考方案1】:

属性Why没有序列化的原因是因为TestGroup是一个集合。并且 DataContract 特别对待集合。最终结果是只存储集合中的数据,不存储任何属性。

列表以任何其他列表都可以读取的方式存储。唯一的区别在于集合和字典之间。一个很好的参考是http://msdn.microsoft.com/en-us/library/aa347850%28v=vs.110%29.aspx

【讨论】:

您能详细说明一下这个答案吗?您指向的参考是一个很好的参考,但有点啰嗦。根据给出的示例,有没有办法做我需要做的事情? 这个答案正确,但不完整。将[DataContract] 应用于自定义集合类型将导致其属性而不是其项被序列化。来自Advanced Collection Rules:如果将 DataContractAttribute 属性应用于集合类型,则该类型将被视为常规数据协定类型,而不是集合。【参考方案2】:

更新:我在网上看到了一些可能对您有所帮助的东西。特别是,将抽象类属性声明更改为以下内容:

[DataContract]
[KnownTypes(typeof(Test))]
public abstract class AbstractTest  /* ... */ 

您可以查看 MSDN 上 KnownTypesAttribute 上的文档。显然,还有一个构造函数重载,它接受一个解析为方法名称的字符串,该方法名称将通过反射找到,并由DataContractSerializer 调用以确定基类的已知类型(如果您有多个已知类型和/或者可能需要动态返回编译时可能不知道的已知类型)。还有web.config XML 配置用于设置已知类型。

更新:我注意到KnownTypesAttribute 属性似乎在 OP 的代码示例中被滥用。所以,我想用应该工作的完整代码来详细说明上述内容。

[CollectionDataContract]
[KnownTypes(typeof(TestGroup))] // Need to tell DCS that this class's metadata will be included with members from this abstract base class.
public abstract class AbstractTestGroup : ObservableCollection<AbstractTest>

    [DataMember]
    public abstract string Name  get; set; 


[CollectionDataContract]
//[KnownTypes(typeof(Test))] -- You don't need this here....
public class TestGroup : AbstractTestGroup

    [DataMember] // Even though this is a derived class, you still need to tell DCS to serialize this overridden property when serializing this type
    public override string Name
    
        get  return "TestGroupName"; 
        set  
    

    [DataMember]
    public string Why  get  return "Why";  set   


[DataContract]
[KnownTypes(typeof(Test))] // Again, you need to inform DCS
public abstract class AbstractTest

    [DataMember]
    public abstract string SayHello  get; set; 



[DataContract]
public class Test : AbstractTest

    //Concrete class - members in this class get serialized
    [DataMember]
    public string Month  get  return "June";  set   

    [DataMember] // Even though this is a derived class, you still need to tell DCS to serialize this overridden property when serializing this type
    public override string SayHello  get  return "HELLO";  set   

请参阅上面示例中 KnownTypesAttribute 属性旁边的 cmets。

更新:DataMemberAttribute 属性添加到派生类的覆盖属性中。

更新:好的,可能有一个额外的维度导致您引用的行为。你有一个interface 或一个class 装饰有ServiceContractAttribute 属性,其中服务包含返回上述这些抽象类型之一的方法?如果是这样,那么您还需要装饰上述interfaceclass 方法,该方法返回具有ServiceKnownTypesAttribute 属性的抽象类型。下面是一个快速而肮脏的例子:

[ServiceContract]
//[ServiceKnownTypes(typeof(TestGroup))] -- You could also place the attribute here...not sure what the difference is, though.
public interface ITestGroupService

    [OperationContract]
    [ServiceKnownTypes(typeof(TestGroup))]
    AbstractTestGroup GetTestGroup();

HTH。

【讨论】:

不幸的是,这也不起作用。使用抽象类上的属性,它会生成相同的输出。如果没有属性,我在尝试序列化时收到错误消息:“类型 '.Test' 不能从未标记 DataContractAttribute 或 SerializableAttribute 的类型继承。考虑将基类型 '.AbstractTest' 标记为DataContractAttribute 或 SerializableAttribute,或从派生类型中删除它们。" 感谢您的更新。当我使用 DataContractSerializer 时,我还有其他需要归档的东西。嗯,我会尝试对可能的解决方案进行更多研究。 感谢您继续调查此问题 - 但添加 KnownTypes(对任何/所有内容)并没有改变结果。我同意转换中似乎缺少某些东西,但我不明白为什么序列化程序似乎缺少具体实现。 查看上面的代码示例,您可能误用了KnownTypesAttribute 属性。我已经扩展了我的答案以包含您的代码的完整代码。从我读过的内容来看,这应该可行。我真的很努力! :) 我真的很想知道最终的解决方案是什么。 似乎最后的更改导致了细微的差异,我现在得到以下开始标签,内容没有其他更改:schemas.datacontract.org/2004/07/WpfApplication2" xmlns :i="w3.org/2001/XMLSchema-instance" xmlns:z="schemas.microsoft.com/2003/10/Serialization"> 我想我已经提供了足够的资源来重新创建你的整个场景 - 也许可以快速启动一个项目来测试它?感谢您的坚持!挺烦的!

以上是关于DataContract 无法序列化集合成员的主要内容,如果未能解决你的问题,请参考以下文章

数据合同和数据成员

Protobuf-net RuntimeTypeModel 不序列化基类的成员

如何拥有一个带有 json 动态成员的 WCF DataContract

WCF -Rest- DataContract:反序列化 XML 包装的响应

使用 DataContract 属性时 ProtoBuf.NET 未序列化

DataContract 序列化异常(不需要数据合约名称)