XML deserialize derivied class - c#

Could anyone help me with deserialize and build the model? with this xml:
<event>
<argument type="Type1" id="1" name="test1"/>
<argument type="Type2" id="1" extra="5"/>
</event>
Event class
[XmlType("event")]
public class Event
{
[XmlElement("argument")]
public List<Argument> Arguments { get; set; }
}
Argument class
public abstract class Argument
{
[XmlAttribute("id")]
public int Id { get; set; }
}
Type1 class
public class Type1 : Argument
{
[XmlAttribute("name")]
public string Name { get; set; }
}
Type2 class
public class Type2 : Argument
{
[XmlAttribute("extra")]
public string Extra { get; set; }
}
Inner exception:
{"The specified type is abstract: name='Argument', namespace='', at ."}
Maybe I don't have to use custom deserializer, I just want to deserialize the objects depending on "type" attribute value.
Deserializing way I use:
public static T XmlDeserializeFromString<T>(this string objectData)
{
return (T)XmlDeserializeFromString(objectData, typeof(T));
}
var obj = xml.XmlDeserializeFromString<Event>();

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!

Deserializing XML to C# child class

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

How to Deserialize a xml file's listed items which is present within another listed items

I have an auto generated xml file with the following format.
<?xml version="1.0"?>
<School>
<Classes numberOfFields="5">
<Class name="10" dataType="double">
<Section value="A"/>
<Section value="B"/>
<Section value="C"/>
</Class>
<Class dataType="double"/>
<Class dataType="double"/>
<Class dataType="double"/>
<Class dataType="double"/>
</Classes>
</School>
I deserialize using "XmlDeserializer" as follows
School schoolResult = (School)xmlSerializer.Deserialize(stream);
XmlRootElement contains a collection of "Class" under "Classes" tag and further each "Class" would contain Collection of "Section".
And in C#, I have declared like this to deserialize "Classes" into a List of classes.
[XmlArray("Classes")]
[XmlArrayItem("Class", typeof(Class))]
public List<Class> Classes {};
Now to further deserialize Class into List of Sections, I have added the code as below.
[XmlArray("Class")]
[XmlArrayItem(ElementName="Section")]
public List<Section> ClassSections {};
My problem is I couldn't get the List of Sections values properly. Because I have "Class" as a class name in the first part and in second part I have mentioned same "Class" as Array element. So could any one tell how can I properly Deserialize my "School" object using "XmlSerializer" to get all the values properly.
Note: I cannot have a Array root tag like "Sections" under "Class". Because my xml document is auto generated. I cannot specify my own format.
Thanks...
try this :
public class School
{
[XmlAttribute]
public int numberOfFields { get; set; }
[XmlArray("Classes")]
[XmlArrayItem("Class", typeof(Class))]
public List<Class> Classes { get; set; }
}
public class Class
{
[XmlAttribute]
public string name { get; set; }
[XmlAttribute]
public string dataType { get; set; }
[XmlElement("Section")]
public List<Section> ClassSections { get; set; }
}
public class Section
{
[XmlAttribute]
public string value { get; set; }
}
* EDIT #1 **
--------------- Update Structure due to NumberOfFields is not readed ----------------
in my opinion it's not correct structure, but when u said it's an auto generated XML file, i think it simplest way to read it.
public class School
{
[XmlElement("Classes")]
public List<ArrayClass> Classes { get; set; }
}
public class ArrayClass
{
[XmlAttribute]
public int numberOfFields { get; set; }
[XmlElement("Class")]
public List<Class> Class { get; set; }
}
static void Main(string[] args)
{
var xml ="<?xml version=\"1.0\"?><School><Classes numberOfFields=\"5\"><Class name=\"10\" dataType=\"double\"><Section value=\"A\"/><Section value=\"B\"/><Section value=\"C\"/></Class><Class dataType=\"double\"/><Class dataType=\"double\"/><Class dataType=\"double\"/><Class dataType=\"double\"/></Classes></School>";
School result;
var serializer = new XmlSerializer(typeof(School));
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);
using (var reader = new XmlNodeReader(xmlDoc))
{
result = (School)serializer.Deserialize(reader);
}
}
public class School
{
[XmlArray("Classes")]
[XmlArrayItem("Class")]
public List<Class> Classes { get; set; }
}
public class Class
{
[XmlElement("Section")]
public List<Section> ClassSections { get; set; }
}
public class Section
{
[XmlAttribute("value")]
public string Value { get; set; }
}

Xml-attributes in interfaces and abstract classes

I found something that confused me today:
1. If I have this:
public interface INamed
{
[XmlAttribute]
string Name { get; set; }
}
public class Named : INamed
{
public string Name { get; set; }
}
It gives the following output (Name property serialized as element):
<Named>
<Name>Johan</Name>
</Named>
2. If I have this:
public abstract class NamedBase
{
[XmlAttribute]
public abstract string Name { get; set; }
}
public class NamedDerived : NamedBase
{
public override string Name { get; set; }
}
The XmlSerializer throws System.InvalidOperationException
Member 'NamedDerived.Name' hides inherited member 'NamedBase.Name',
but has different custom attributes.
The code I used for serialization:
[TestFixture]
public class XmlAttributeTest
{
[Test]
public void SerializeTest()
{
var named = new NamedDerived {Name = "Johan"};
var xmlSerializer = new XmlSerializer(named.GetType());
var stringBuilder = new StringBuilder();
using (var stringWriter = new StringWriter(stringBuilder))
{
xmlSerializer.Serialize(stringWriter, named);
}
Console.WriteLine(stringBuilder.ToString());
}
}
My question is:
Am I doing it wrong and if so what is the correct way to use xml attributes in interfaces and abstract classes?
Attributes are not inherited on overriden properties. You need to redeclare them.
Also in your first example the behavior is not the "expected" one as you declared XmlAttribute at the interface level and yet the serialized xml contains the value as an element. So the attribute in the interface is ignored and only info taken from the actual class matters.
I think you should xmlignore your abstract class property
public abstract class NamedBase
{
[XmlIgnore]
public abstract string Name { get; set; }
}
public class NamedDerived : NamedBase
{
[XmlAttribute]
public override string Name { get; set; }
}

Serialize Property as Xml Attribute in Element

I have the following class:
[Serializable]
public class SomeModel
{
[XmlElement("SomeStringElementName")]
public string SomeString { get; set; }
[XmlElement("SomeInfoElementName")]
public int SomeInfo { get; set; }
}
Which (when populated with some test data) and Serialized using XmlSerializer.Serialize() results in the following XML:
<SomeModel>
<SomeStringElementName>testData</SomeStringElementName>
<SomeInfoElementName>5</SomeInfoElementName>
</SomeModel>
What I need to have is:
<SomeModel>
<SomeStringElementName Value="testData" />
<SomeInfoElementName Value="5" />
</SomeModel>
Is there a way to specify this as attributes without writing my own custom serialization code?
You will need wrapper classes:
public class SomeIntInfo
{
[XmlAttribute]
public int Value { get; set; }
}
public class SomeStringInfo
{
[XmlAttribute]
public string Value { get; set; }
}
public class SomeModel
{
[XmlElement("SomeStringElementName")]
public SomeStringInfo SomeString { get; set; }
[XmlElement("SomeInfoElementName")]
public SomeIntInfo SomeInfo { get; set; }
}
or a more generic approach if you prefer:
public class SomeInfo<T>
{
[XmlAttribute]
public T Value { get; set; }
}
public class SomeModel
{
[XmlElement("SomeStringElementName")]
public SomeInfo<string> SomeString { get; set; }
[XmlElement("SomeInfoElementName")]
public SomeInfo<int> SomeInfo { get; set; }
}
And then:
class Program
{
static void Main()
{
var model = new SomeModel
{
SomeString = new SomeInfo<string> { Value = "testData" },
SomeInfo = new SomeInfo<int> { Value = 5 }
};
var serializer = new XmlSerializer(model.GetType());
serializer.Serialize(Console.Out, model);
}
}
will produce:
<?xml version="1.0" encoding="ibm850"?>
<SomeModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SomeStringElementName Value="testData" />
<SomeInfoElementName Value="5" />
</SomeModel>
Kind of, use the XmlAttribute instead of XmlElement, but it won't look like what you want. It will look like the following:
<SomeModel SomeStringElementName="testData">
</SomeModel>
The only way I can think of to achieve what you want (natively) would be to have properties pointing to objects named SomeStringElementName and SomeInfoElementName where the class contained a single getter named "value". You could take this one step further and use DataContractSerializer so that the wrapper classes can be private. XmlSerializer won't read private properties.
// TODO: make the class generic so that an int or string can be used.
[Serializable]
public class SerializationClass
{
public SerializationClass(string value)
{
this.Value = value;
}
[XmlAttribute("value")]
public string Value { get; }
}
[Serializable]
public class SomeModel
{
[XmlIgnore]
public string SomeString { get; set; }
[XmlIgnore]
public int SomeInfo { get; set; }
[XmlElement]
public SerializationClass SomeStringElementName
{
get { return new SerializationClass(this.SomeString); }
}
}

Categories