Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(De)serializing different root element names using one class

I have several XML files with different root elements, but same type of child elements and I would like to be able to create one single class to hold the different root elements and another to hold each child element. Here's two examples of how the XML files looks like.

File 1:

<?xml version="1.0" encoding="utf-8" ?>
<Sandra>
  <Address>
    <Street></Street>
    <Number></Number>
  </Address>
</Sandra>

File 2:

<?xml version="1.0" encoding="utf-8" ?>
<John>
  <Address>
    <Street></Street>
    <Number></Number>
  </Address>
</John>

I want to be able to both serialize and deserialize this using just 2 classes, like:

[Serializable]
[XmlRoot]
public class Person
{
    [XmlElement("Address")]
    public List<Address> Adresses { get; set; }
}

[Serializable]
public class Address
{
    public string Street { get; set; }

    public string Number { get; set; }
}

I tried to read them using:

    var ser = new XmlSerializer(typeof(Person));
    var reader = XmlReader.Create("person1.xml");
    var person = (Person)ser.Deserialize(reader);

But I get "There is an error in XML document (2, 2).", because the deserializer is was expecting a <"Person"> root element and not a <"John"> or <"Paul">. It works fine if I change [XmlRoot] to [XmlRoot("John")] but that's exactly what I'm trying to avoid here.

Also, I must be able to serialize it back using that same weird XML structure, so I need to store the root element name inside the Person class.

I know I could simply create my own (de)serializer but I would like to know if it's possible to achieve it using the existing methods.

Edit 1: (rolled back).

Edit 2: rolled back "Edit 1"'s modifications since I found a simpler way to achieve what I needed. See my answer below.

like image 748
Henrique Miranda Avatar asked Jul 15 '13 15:07

Henrique Miranda


3 Answers

Found a neat and fast way to solve my problem! I just had to use a XmlRootAttribute when instantiating the XmlSerializer. This way I can set the Root Element's name at runtime.

var personsName = "Sandra";
var ser = new XmlSerializer(typeof(Person), 
   new XmlRootAttribute { ElementName = personsName });
var reader = XmlReader.Create("person1.xml");
var person = (Person)ser.Deserialize(reader);

Of course it also works the same way if I want to serialize it.

like image 174
Henrique Miranda Avatar answered Oct 24 '22 09:10

Henrique Miranda


Using person names as XML element names feels a bit wonky. What does each element represent, a John or a Person?

It might be better, if you're able to control the shape of those XML files, to represent them like so:

<?xml version="1.0" encoding="utf-8" ?>
<Person name="Sandra">
  <Address>
    <Street></Street>
    <Number></Number>
  </Address>
</Person>

And then you'll have an easy way to store the name in a property, mapped as an XML attribute:

[Serializable]
[XmlRoot]
public class Person
{
    [XmlElement("Address")]
    public List<Address> Adresses { get; set; }

    [XmlAttribute("name")]
    public string Name { get; set;}
}
like image 43
CSJ Avatar answered Oct 24 '22 07:10

CSJ


Implement IXmlSerializable in your class and do serialization the way you want in the functions :

ReadXml(System.Xml.XmlReader reader) and WriteXml(System.Xml.XmlWriter writer)

example :

[Serializable]
public class Threshold : IXmlSerializable
{

public int Type {get;set;}
public object Value {get;set;}
public string Name {get;set;}
public void ReadXml(System.Xml.XmlReader reader)
{


XElement thresholdXML = XElement.Load(reader);

if (!thresholdXML.HasElements || thresholdXML.IsEmpty)
return;

Type = (ThresholdType)int.Parse(thresholdXML.Element("Type").Value);
Value = Type.Equals(ThresholdType.Complex) ? thresholdXML.Element("Value").Value : (object)Decimal.Parse(thresholdXML.Element("Value").Value);
Name = thresholdXML.Element("Name").Value;


}


public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}

public void WriteXml(System.Xml.XmlWriter writer)
{
XmlSerializerNamespaces xmlnsEmpty = new XmlSerializerNamespaces();
xmlnsEmpty.Add("", "");


writer.WriteElementString("Type", ((int)Type).ToString("D"));
writer.WriteElementString("Value", Value.ToString());
writer.WriteElementString("Name", Name);
}
}
like image 1
Siraj Mansour Avatar answered Oct 24 '22 09:10

Siraj Mansour