问题
I have data that is best described as "onion-like" in that each outer layer builds on the one below it. Below you will see a vastly simplified version (mine is several layers deeper but exhibits 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 has left off the DataMember
s in TestGroup
. As I get deeper in my onion, no DataMember
s that are higher are included (even from the abstract classes). I have tried adding [KnownType(typeof(TestGroup))]
to both TestGroup
and AbstractTestGroup
without success.
The question: Why am I not able to serialize the DataMember
Why
in the TestGroup
class?
Follow up question: Is there an alternative way to serialize and de-serialize a structure of this shape? I am planning on using the output locally to "load" the configuration the user specifies. I would prefer to not have to specify my own Serialization scheme if I can avoid it.
For those interested here is how I am generating the class, serializing, 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: For what it's worth I tried changing to using Serializable
instead and have the same issue.
回答1:
The reason why the property Why is not serialized is because TestGroup is a collection. And DataContract treats collections specially. The end result is that only the data in the collection is stored and none of the properties are stored.
Lists are stored in a way that any other list could read them in. The only differentiation is between collections and dictionaries. A good reference is http://msdn.microsoft.com/en-us/library/aa347850%28v=vs.110%29.aspx
回答2:
UPDATE: I've seen some things online that may help you. In particular, change the abstract class attribute declarations to the following:
[DataContract]
[KnownTypes(typeof(Test))]
public abstract class AbstractTest { /* ... */ }
You could have a look at the documentation at MSDN on the KnownTypesAttribute
. Apparently, there's also a constructor overload that takes a string that resolves to a method name that would be found via reflection and would be called by the DataContractSerializer
to determine the known types for a base class (if you had multiple known types and/or possibly needed to dynamically return known types that may not be known at compile time). There's also web.config
XML configurations for setting up known types.
UPDATE: I noticed that the KnownTypesAttribute
attribute seems to be misused in the code examples in the OP. So, I wanted to elaborate the above with the full code that should work.
[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 { } }
}
See the comments next to the KnownTypesAttribute
attributes in the example above.
UPDATE: Added the DataMemberAttribute
attribute to the derived class' overridden properties.
UPDATE: OK, there may be an added dimension to this that is causing the behavior you're referencing. Do you have an interface
or a class
that is decorated with the ServiceContractAttribute
attribute, where the service contains a method which returns one of these abstract types above? If so, then you also need to decorate said interface
or class
method that returns the abstract type with the ServiceKnownTypesAttribute
attribute. A quick and dirty example follows:
[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.
来源:https://stackoverflow.com/questions/17091313/datacontract-not-able-to-serialize-collection-members