DataContractSerializer not serializing properties of custom collections [duplicate] - c#

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 DataMembers in TestGroup. As I get deeper in my onion, no DataMembers 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.

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

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.

Related

Is there an elegant pattern for Serialization?

I frequently find myself implementing this sort of class:
public class Something
{
public string Serialize()
{
// serialization code goes here
}
public static Something Deserialize(string str)
{
// deserialization code goes here
}
}
I would like to enforce this across all classes of this type by making the above class implement an interface that looks something like this:
public interface ISerializationItem<T>
{
string Serialize();
T Deserialize(string str);
}
Alas, this is not possible, because the the interface can't cover the static method, and the method needs to be static so that it does not depend on any instance of the class.
Update: Typically, I would deserialize as shown below; the static method effectively serves to construct an instance of the class, so I don't want to already have an instance at hand to be able to do this:
var str = "Whatever";
var something = Something.Deserialize(str);
Is there a proper way to enforce this constraint?
Keep your "data" classes simple/pure of any logic and then write the serialization processes in a separate class. This will make maintaining the data classes and serializer easier. If each class needs customization then create attribute classes and decorate your data classes with these attributes.
Here is some pseudo example...
public class Employee
{
public int Id { get; set;}
[ForceSpecialHandling]
public string Name { get; set; }
}
public class CustomSerializer
{
public T Serialize<T>(string data)
{
// Write the serialization code here.
}
}
// This can be whatever attribute name you want.
// You can then check if the property or class has this attribute using reflection.
public class ForceSpecialHandlingAttribute : Attribute
{
}

WebAPI serializer serializes base class instead of kludgy child class

The class that I need gets serialized as a web service response body.
The problem is, the properties from the base class get serialized along with it, and I can't have that for this service.
I need to block those properties from being serialized on only this subclass. So I tried hiding the properties using new but the base class properties are still being serialized (i.e. "Hello, world" is in the resulting http response body):
public class MyBaseClass
{
public string MyProperty { get { return "Hello, world"; } }
}
public class MyChildClass : MyBaseClass
{
[XmlIgnore]
[JsonIgnore]
public new string MyProperty { get; set; }
}
this gets returned via something like this:
return myHttpRequestMessage.CreateResponse(myStatusCode, myChildClassInstance);
So two questions
What up with that? Why isn't it honoring the child class with its decorations?
Is there another way to achieve what I'm trying to achieve (which is preventing the decorated properties from being serialized?
I know it's a total kludge, but until I have the time to fix the deeper issue (which is the operation that's forcing this inheritance), this is what I have to work with.
JSON
If you are using JSON.NET (which is a default JSON serializer in Web.API), then you will probably need to use custom ContractResolver. Answers for this question has a good examples of creating such type of class. In the following examples I will use IgnorableSerializerContractResolver from one of the answers.
Now you can register it in Global.asax:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new IgnorableSerializerContractResolver()
.Ignore<MyBaseClass>(x => x.MyProperty);
If you already using some contract resolver (for example CamelCasePropertyNamesContractResolver) then you will need to combine them somehow.
XML
I don't know what type (DataContractSerializer or XmlSerializer) of XML serialization are you using, but as I know, DataContractSerializer doesn't allow to exclude properties in runtime. You will need to use XmlSerializer. You can set custom serializers per type:
var xmlOver = new XmlAttributeOverrides();
var xmlAttr = new XmlAttributes { XmlIgnore = true };
xmlOver.Add(typeof(MyBaseClass), "MyProperty", xmlAttr);
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.SetSerializer<MyChildClass>(new XmlSerializer(typeof(MyChildClass), xmlOver));
Siple way
If you have access to your MyBaseClass and allowed to do some changes, then you can solve your problem using Data attributes:
[DataContract]
public class MyBaseClass
{
public string MyProperty { get; set; }
}
[DataContract]
public class MyChildClass : MyBaseClass
{
[DataMember]
public string MyProperty2 { get; set; }
}
In this case you can use default serializers (JSON.NET for JSON and DataContractSerializer for XML) and it will be solved for you automatically. Please note that you must add DataContract to MyBaseClass, or otherwise XML serialization will fail.
In order to ignore a property from the base class you could override this property in the derived class and decorate it with JsonIgnoreAttribute:
public class MyBaseClass
{
public virtual string MyProperty { get { return "Hello, world"; } }
}
public class MyChildClass : MyBaseClass
{
[JsonIgnore]
public override string MyProperty { get; }
}
You get an empty json object serializing it:
Debug.Assert(Newtonsoft.Json.JsonConvert.SerializeObject(new MyChildClass()) == "{}");

extending / changing values of an attribute of a property when extending a class

Is it possible to 'change' values of an attribute, when extending classes? Something similar to below:
public class Mammal
{
[Validation(Required=true, HelpMessage="This is the body shape of a mammal")]
public virtual string BodyShape { get; set; }
}
public class Dog : Mammal
{
[Validation(HelpMessage = "This is the body shape of a dog")]
public override string BodyShape
{
get
{
return base.BodyShape;
}
set
{
base.BodyShape = value;
}
}
}
I would like to be able to read the attribute for Dog.BodyShape, and get it as Required=True, and HelpMessage = "This is the body shape of a dog". Is this possible? If not, any suggested alternatives where one can add 'meta-data' information to properties, directly in the compiler?
I don't think you can inherit attributes; they are attached to a specific method/class.
You could however change the standard implementation of the validation to also check the base classes similarly named properties.

Specify interface member not by name but type

I have a lot of similar classes generated by svcutil from some external WSDL file. Any class has a Header property and string property which named class name + "1".
For instance, I have classes: SimpleRequest that has Header property and SimpleRequest1 property.
Another one is ComplexRequest that has Header property and ComplexRequest1 property.
So, I want to create a common interface for such classes. So, basically I can define something like that:
interface ISomeRequestClass {
string Header;
// here is some definition for `class name + "1"` properties...
}
Is it possible to define such member in interface?
Here is post edit goes...
Here is sample of generated class:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(IsWrapped=false)]
public partial class SimpleRequest
{
public string Header;
[System.ServiceModel.MessageBodyMemberAttribute(Name="SimpleRequest", Namespace="data", Order=0)]
public SimpleRequestMsg SimpleRequest1;
public SimpleRequest()
{
}
public SimpleRequest(string Header, SimpleRequestMsg SimpleRequest1)
{
this.Header = Header;
this.SimpleRequest1 = SimpleRequest1;
}
}
POST EDIT 2
I changed definition of this annoying +1 property to represent real actual picture. It's all has different class types. So how can I pull it out to common interface?
POST EDIT 3
Here is coupled question that could bring more clarify.
EDIT (after seeing your code sample): Technically speaking, your code does not have a Header property, it has a Header field. This is an important difference, since you cannot specify fields in an interface. However, using the method described below, you can add properties to your classes that return the field values.
Is it possible to define such member in interface?
No, interface names cannot be dynamic. Anyway, such an interface would not be very useful. If you had an instance of class ISomeRequestClass, what name would you use to access that property?
You can, however, use explicit interface implementation:
interface ISomeRequestClass {
string Header { get; set; }
string ClassName1 { get; set; }
}
class SomeClass : ISomeRequestClass {
string Header { ... }
string SomeClass1 { ... }
// new: explicit interface implementation
string ISomeRequestClass.ClassName1 {
get { return SomeClass1; }
set { SomeClass1 = value; }
}
}
You could define your interface more generally:
interface ISomeRequestClass {
string HeaderProp {get; set;}
string Prop {get; set;}
}
And your concrete classes could be extended (in an extra code file) by mapping interface members to class fields like so:
public partial class SimpleRequest : ISomeRequestClass
{
public string HeaderProp
{
get
{
return Header;
}
set
{
Header = value;
}
}
public string Prop
{
get
{
return SimpleRequest1;
}
set
{
SimpleRequest1= value;
}
}
}
Putting aside for a moment the naming of your classes and properties.
If you're looking to create an interface with a property relevant to your specific +1 type, you have a couple of options.
Use a base class for your +1's
If both of your +1 classes inherit from the same base class you can use this in your interface definition:
public interface IFoo
{
[...]
PlusOneBaseType MyPlusOneObject{get;set;}
}
Create a generic property on your interface
This method allows you to specify the type for the +1 property as a generic parameter:
public interface IFoo<TPlusOneType>
{
[...]
TPlusOneType MyPlusOneObject{get;set;}
}
Which you might use like:
public class SimpleRequest : IFoo<SimpleRequest1>
{
[...]
}
Update
Given that your classes are partial classes, you could always create a second (non machine generated) version of the partial class that impliments your interface.
You mentioned svcutil so I assume you are using these classes as WCF DataContracts?
If that is the case then you could make use the name property of DataMemberAttribute.
interface IRequest
{
string Header { get; set; }
string Request1 { get; set; }
}
[DataContract]
class SimpleRequest : IRequest
{
[DataMember]
public string Header { get; set; }
[DataMember(Name="SimpleRequest1"]
public string Request1 { get; set; }
}
[DataContract]
class ComplexRequest : IRequest
{
[DataMember]
public string Header { get; set; }
[DataMember(Name="ComplexRequest1"]
public string Request1 { get; set; }
}
If you are concerned giving yourself more work when you regenerate the code at some point in the future, then I recommend you write a PowerShell script to do this transformation automatically. After all svcutil is just a script written by some guy at Microsoft. It is not magic or "correct" or "standard". Your script can make a call to scvutil and then make a few quick changes to the resulting file.
EDIT (After seeing your edit)
You are already using MessageBodyMemberAttribute's Name property so just change this:
public string SimpleRequest1;
To
public string Request1;
Do you actually need these classes to have a common interface? I'd be tempted to instead create a wrapper interface (or just a concrete class) which could then use reflection to access the fields in question:
// TODO: Make this class implement an appropriate new interface if you want
// to, for mocking purposes.
public sealed class RequestWrapper<TRequest, TMessage>
{
private static readonly FieldInfo headerField;
private static readonly FieldInfo messageField;
static RequestWrapper()
{
// TODO: Validation
headerField = typeof(TRequest).GetField("Header");
messageField = typeof(TRequest).GetField(typeof(TRequest).Name + "1");
}
private readonly TRequest;
public RequestWrapper(TRequest request)
{
this.request = request;
}
public string Header
{
get { return (string) headerField.GetValue(request); }
set { headerField.SetValue(request, value); }
}
public TMessage Message
{
get { return (TMessage) messageField.GetValue(request); }
get { messageField.SetValue(request, value); }
}
}
You could use expression trees to build delegates for this if the reflection proves too slow, but I'd stick to a simple solution to start with.
The advantage of this is that you only need to write this code once - but it does mean creating a wrapper around the real request objects, which the partial class answers don't.

Serialization DataMember (name) override issue

I am using a DataContractJsonSerializer and have an issue with the DataMember Name.
I made a base class and several derived classes. I need the derived classes because I have different json strings. I want to deserialize the json strings and therefore need different names for the datamembers. I try to change the DataMember name as in the following example:
Baseclass:
[DataContract]
public abstract class BaseClass
{
[DataMember]
public virtual string FirstMethod { get; protected set; }
}
Derived class:
[DataContract]
[KnownType(typeof(BaseAccess))]
public class DerivedClass
{
[DataMember(Name="first_method")]
public virtual string FirstMethod { get; protected set; }
}
Problem is that when I use a derived class the serialization seems to ignore the given DataMember name. So when I deserialize with the type DerivedClass the serialization seems to take place with the name "FirstMethod" (of the base class) instead of "first_method" (of the derived class). Is it possible to use the DataMember name of the derived class (which is different for several derived classes in my situation).
Another question. I found examples with KnownType added on the base class and added on the derived class. Seems logic to me to do it on the derived class (espcially for inheritance concerns). What is correct?
I had this same issue. I was using VB.NET and I had to Shadow (or Overload) the property to get WCF to respect the DataMember property in my derived class. In C# you should be able to use the new operator.
public class DerivedClass
{
[DataMember(Name = "first_method")]
new public string FirstMethod { get; protected set; }
}
The trick is to specify EmitDefaultValue = false for the base class's virtual data member, and in its implementation in the derived class return default value so the data member is not serialized. In the derived class define another data member with the required name.
[DataContract(Name = "baseclass", Namespace = "")]
[KnownType(typeof(DerivedClass))]
public class BaseClass
{
[DataMember(Name = "attributes", EmitDefaultValue = false)]
public virtual SomeType Fields { get; set; }
}
[DataContract(Name = "derivedclass", Namespace = "")]
public class DerivedClass : BaseClass
{
public override SomeType Fields
{
get { return null; }
}
[DataMember(Name = "fields")]
public SomeType DerivedFields
{
get { return base.Fields; }
}
}

Categories