DataContract cannot serialize collection items

I have data that is best described as “look like onions,” since each outer layer is built on what's below it. Below you will see a significantly simplified version (I have several levels deeper, but they exhibit the same behavior at each level).

[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 { } } } 

I create an instance of TestGroup and add Test objects to it using the .Add that comes with the ObservableCollection .

When I serialize and de-serialize this structure, I get the following

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

The output is disconnected from the DataMember in the TestGroup . As I get deeper in my bow, DataMember that are higher (even from abstract classes) are not included. I tried adding [KnownType(typeof(TestGroup))] to TestGroup and AbstractTestGroup without success.

Question: Why can't I serialize the DataMember Why in the TestGroup class?

Next question: Is there an alternative way to serialize and de-serialize the structure of this figure? I plan to use the output locally to “load” the configuration that the user specifies. I would prefer not to specify my own Serialization scheme if I can avoid it.


For those interested here, I create a class by serializing it and de-serializing it.

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

Edit:. Why did I try to switch to using Serializable instead and have the same problem.

+4
source share
2 answers

The reason why the Why not serialized property is because TestGroup is a collection. And DataContract treats collections specifically. The end result is that only data is stored in the collection, and none of the properties are saved.

Lists are stored in such a way that any other list can read them. The only difference between collections and dictionaries. Good link http://msdn.microsoft.com/en-us/library/aa347850%28v=vs.110%29.aspx

+2
source

UPDATE: I saw some things on the Internet that may help you. In particular, change the declarations of the attributes of an abstract class to the following:

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

You can see the documentation on MSDN at KnownTypesAttribute . Apparently, there is also a constructor overload that takes a string that resolves the method name that will be found through reflection, and will call DataContractSerializer to determine the known types for the base class (if you had several known types and / or, possibly necessary for the dynamic return of known types that cannot be known at compile time). There is also a web.config XML configuration to configure known types.

UPDATE: I noticed that the KnownTypesAttribute attribute KnownTypesAttribute to be misused in the code examples in the OP. So, I would like to develop above, with a complete code that should work.

 [CollectionDataContract] [KnownTypes(typeof(TestGroup))] // Need to tell DCS that this class 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 { } } } 

See the comments next to the KnownTypesAttribute attributes in the example above.

UPDATE: Added the DataMemberAttribute for overridden properties of a derived class.

UPDATE: OK, a dimension can be added that causes the behavior you are referencing. Do you have an interface or class that is decorated with the ServiceContractAttribute attribute, where the service contains a method that returns one of these abstract types above? If so, then you also need to decorate the mentioned interface or class method, which returns an abstract type with the ServiceKnownTypesAttribute attribute. Below is a quick and dirty example:

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

NTN.

-1
source

Source: https://habr.com/ru/post/1486051/


All Articles