Deserializing XML to C# child class - c#

My XML :
<?xml version="1.0" encoding="UTF-8"?>
<response id="41cc788a-bc22-4ce0-8e1c-83bf49bbffed">
<message>Successfully processed the request</message>
<payload>Alive</payload>
</response>
My Classes :
[XmlRoot("response")]
[Serializable]
public abstract class ESBResponseBase<T>
{
[XmlAttribute]
public string Id { get; set; }
public string Message { get; set; }
public T Payload { get; set; }
}
[XmlRoot("response")]
[Serializable]
public class ESBResponseIsAlive : ESBResponseBase<string>
{
}
Note that if I don't have these classes on the child classes it throws an exception so it seems inheritance doesn't work with these.
My Serialization Code :
XmlSerializer serializer = new XmlSerializer(typeof (ESBResponseIsAlive));
var esbResponseIsAlive = (ESBResponseIsAlive) serializer.Deserialize(result);
However when I serialize my object properties are null.
I think more than likely this is an issue with inheritance in classes used for serializing. It is possible to simply change the base class to the actual concrete class and use the Generic, but I preferred to have it strongly typed.

As it turns out, XMLSerializer is case-sensitive. My class now looks like the following and it works.
[XmlAttribute("id")]
public string Id { get; set; }
[XmlElement("message")]
public string Message { get; set; }
[XmlElement("payload")]
public T Payload { get; set; }

Related

Serialization & backward compatibility (DataContractJsonSerializer)

I am using DataContractJsonSerializer.
Currently, my serialized object contained a member of class "Settings"
Now, I would like to extend my support and serialize any class that implements ISettings interface.
[DataContract(Namespace = "")]
[KnownType(typeof(SystemSettings))]
[KnownType(typeof(PrivateSettings))]
public class Data
{
[DataMember]
public ISettings settings { get; set; }
}
public interface ISettings
{
}
[DataContract(Namespace = "")]
public class SystemSettings : ISettings
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string SysName { get; set; }
}
[DataContract(Namespace = "")]
public class PrivateSettings : ISettings
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string PrivateName { get; set; }
}
This works perfectly fine, as the serialized data contains the type of the object
<?xml version="1.0" encoding="utf-8"?>
<root type="object">
<settings __type="SystemSettings" type="object">
<Name>System Settings</Name>
<SysName>New System</SysName>
</settings>
</root>
My issue is with backward compatibility. The existing serialized files do not contain the object type (__type="SystemSettings").
When deserializing old data into my class, I get
"{"Unable to cast object of type 'System.Object' to type 'JsonSerializer.ISettings'."}"
Is there a way to solve this?
Can I direct the serializer what default type to instantiate for the interface?
Thank you!

Compatibility of Serializable and DataContract objects using DataContractSerializer

I have two classes, one inherits another - base class is DataContract, child is Serializable. I want to move property from child to base class, but after moving it I get exception saying that during deserializing the child class BackingProperty is not found. How can I correctly move that property to base class?
Do you have the base class decorated with the KnownType attribute?
[KnownType(typeof(SuperClass))]
[DataContract]
public class BaseClass
{
...
}
It's because serialization for Serializable marked classes and DataContract a bit different.
Here is a serializer I used for the samples:
var ser = new DataContractSerializer(typeof(BaseClass), new List<Type>() { typeof(ChildClass) });
You can compare:
[DataContract]
public class BaseClass
{
[DataMember]
public string Name { get; set; }
}
[Serializable]
public class ChildClass: BaseClass
{
public string SecondName { get; set; }
public int Age { get;set; }
}
Xml for that class (using DataContractSerializer, which is standard for WCF ):
<BaseClass i:type="ChildClass" xmlns="http://schemas.datacontract.org/2004/07/Serialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Name>Ivan</Name>
<_x003C_Age_x003E_k__BackingField>1</_x003C_Age_x003E_k__BackingField>
<_x003C_SecondName_x003E_k__BackingField>Petrov</_x003C_SecondName_x003E_k__BackingField>
</BaseClass>
But when you move a property to base class, which is marked ad DataContract:
[DataContract]
public class BaseClass
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string SecondName { get; set; }
}
[Serializable]
public class ChildClass: BaseClass
{
public int Age { get;set; }
}
Here is xml for that case:
<BaseClass i:type="ChildClass" xmlns="http://schemas.datacontract.org/2004/07/Serialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Name>Ivan</Name>
<SecondName>Petrov</SecondName>
<_x003C_Age_x003E_k__BackingField>1</_x003C_Age_x003E_k__BackingField>
</BaseClass> <BaseClass i:type="ChildClass" xmlns="http://schemas.datacontract.org/2004/07/Serialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Name>Ivan</Name>
<SecondName>Petrov</SecondName>
<_x003C_Age_x003E_k__BackingField>1</_x003C_Age_x003E_k__BackingField>
</BaseClass>
As a result you have different element name in Xml (SecondName vs _x003C_SecondName_x003E_k__BackingField) and these two xml are really different.
It's better to use a common approach in you solution, to mark everything or Serializable or DataContract. I would recommend to use DataContract.
EDIT 1:
Of course you can try to make a trick with property naming:
[DataMember(Name = #"_x003C_SecondName_x003E_k__BackingField", IsRequired = true)]
public string SecondName { get; set; }
And XML will be quite the same in result. You can also try to expose feilds for Serializable objects instead of properties:
[Serializable]
public class ChildClass : BaseClass
{
public string SecondName;// { get; set; }
public int Age; //{get;set;}
}
And xml will be:
<BaseClass i:type="ChildClass" xmlns="http://schemas.datacontract.org/2004/07/Serialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Name>Ivan</Name>
<Age>1</Age>
<SecondName>Petrov</SecondName>
</BaseClass>
But, unfortunately, if you move your property from ChildClass to BaseClass the order of properties will be changed. But DataContractSerializer always enforces the order of elements during deserialization and as a result not all the fields will be deserialzed. But you can switch to use the XmlSerializer in your service, which supports unordered deserialization.
Finally: Marking all data contract with DataContract
Even if you mark both your classes with DataContract, DataMember:
[DataContract]
public class BaseClass
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string SecondName { get; set; }
}
[DataContract]
public class ChildClass : BaseClass
{
//[DataMember]
//public string SecondName { get; set; }
[DataMember]
public int Age {get;set;}
}
And then move a property from ChildClass to BaseClass, it will still not work. Your property will be null (or default value), as the order has been changed :(

DataContractSerializer How to emit clean Xml for generic list of interfaces

I have this object structure:
[DataContract]
[KnownType(typeof(ChildClassA))]
[KnownType(typeof(ChildClassB))]
public class MainClass
{
[DataMember]
public string PropA { get; set; }
[DataMember]
public IList<IMyInterface> GenericInterfaceList { get; set; }
//....
}
public interface IMyInterface
{
int Id { get; set; }
//....
}
[DataContract]
public abstract class BaseClass : IMyInterface
{
[DataMember]
public int Id { get; set; }
//....
}
[DataContract]
public class ChildClassA : BaseClass
{
//....
}
[DataContract]
public class ChildClassB : BaseClass
{
//.....
}
I want that DataContractSerializer emits an Xml without a:anyType i:type= for my generic list of interfaces:
<MainClass xmlns="MYNAMESPACE" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<PropA> dfgdsfg gsd</PropA>
<GenericInterfaceList xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<ChildClassA>
<Id>1</Id>
</ChildClassA>
<ChildClassB>
<Id>2</Id>
</ChildClassB>
</GenericInterfaceList>
</MainClass>
instead of:
<MainClass xmlns="MYNAMESPACE" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<PropA> dfgdsfg gsd</PropA>
<GenericInterfaceList xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a:anyType i:type="ChildClassA">
<Id>1</Id>
</a:anyType>
<a:anyType i:type="ChildClassB">
<Id>2</Id>
</a:anyType>
</GenericInterfaceList>
</MainClass>
I tryed with a custom DataContractResolver but it is not the right tool. Cannot find a way to shrink that Xml.
I could not find a way because there is not.
The DataContractSerializer doesn't allow much control: you can define
quite clearly (using this explicit "opt-in" approach) what gets
serialized, but you have little or no control over how it gets
serialized.

XML deserializing on WCF RestFul service

I've got the following classes (Don't mind the Namespaces):
[DataContract(Namespace = "http://www.test.com/ReqBody2")]
[KnownType(typeof(ReqBody2))]
public class ReqBody2
{
[DataMember]
public string pass { get; set; }
[DataMember]
public int Tout { get; set; }
[DataMember]
public string RequestDate { get; set; }
[DataMember]
public ReqBody2Internal Req { get; set; }
[DataMember]
public string ReqEnc { get; set; }
}
[DataContract(Namespace = "http://www.test.com/ReqBodyInternal")]
[KnownType(typeof(ReqBody2Internal))]
public class ReqBody2Internal
{
[DataMember]
public string Field1 { get; set; }
[DataMember]
public string Field2 { get; set; }
[DataMember]
public string Field3 { get; set; }
[DataMember]
public string Field4 { get; set; }
}
When I post the Xml Serialization of ReqBody2, the service receives and deserializes the object's root attributes properly. However, the attributes from ReqBody2Internal are all null.
The OperationContract is:
[OperationContract]
[WebInvoke(UriTemplate = "Invoke2",RequestFormat=WebMessageFormat.Xml , ResponseFormat=WebMessageFormat.Xml)]
void Invoke2(ReqBody2 req);
This is an example Xml I'm posting using Fiddler:
<?xml version="1.0" encoding="utf-8"?><ReqBody2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.test.com/ReqBody2">
<pass>HOLA</pass>
<Req><Field1>asd</Field1><Field2>asd</Field2><Field3>asd</Field3><Field4>extra value</Field4></Req>
<RequestDate>2013-04-04T14:10:38</RequestDate>
<Tout>30000</Tout>
</ReqBody2>
What I expect to happen is to have access to Req attributes, but they are null on the server.
Any clue as to why this might be happening?
Your document being posted has a default namespace defined with:
xmlns="http://www.test.com/ReqBody2"
This means that unless specified, all child elements will inherit this XML namespace. This includes the Req element which will be deserialized into an element of type ReqBody2Internal.
However your ReqBody2Internal type has a namespace declared as http://www.test.com/ReqBodyInternal. This means the child XML elements are expected to be from this namespace to deseralize correctly, but they inherit the default namespace and thus are seen as the "wrong" elements by the serializer.
To fix this, you need to change the namespace declaration on your data contracts to share the same namespace, or change your XML to specify the correct namespace for the child elements of the Req element.

XML Serialization of an Interface

I have a problem that I'm working in nHibernate project that have the following object:
[Serializable]
public class Prototype
{
public virtual long Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<AttributeGroup> AttributeGroups { get; private set; }
}
I have created a method to deserialize an XML file and put it into object of type Prototype as following :
public static T Deserialize(string fileName)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
XmlTextReader xmlTextReader = new XmlTextReader(fileName);
Object c = xmlSerializer.Deserialize(xmlTextReader);
return (T)c;
}
The problem now is that I have the following exception:
Unable to cast object of type 'NHibernate.Collection.Generic.PersistentGenericBag`1[BCatalog.Entities.AttributeGroup]' to type 'System.Collections.Generic.List`1[BCatalog.Entities.AttributeGroup]'.
I can't change the type of the IList because of the nHibernate and I want to deserialize the object.
What should I do to solve this problem ?
Interfaces seems to be cumbersome for serialization/deserialization processes. You might need to add another public member to the class that uses a concrete type and mark the interface property as xml ignore. This way you can deserialize the object without loosing your contract base.
Something like the following:
[Serializable]
public class Prototype
{
public virtual long Id { get; private set; }
public virtual string Name { get; set; }
[XMLIgnore]
public virtual IList<AttributeGroup> AttributeGroups {
get { return this.AttributeGroupsList; }
}
public virtual List<AttributeGroup> AttributeGroupsList { get; private set;}
}
For more information about deserialization attributes please check XmlAttributes Properties.
Regards,

Categories