Henrique Miranda
Henrique Miranda

Reputation: 1108

(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.

Upvotes: 11

Views: 11341

Answers (4)

Jayesh Tank
Jayesh Tank

Reputation: 129

Use root element as ArrayOfClassName and then try it.

<ArrayOfAlarmSummary>
  <AlarmSummary>
    <ClientId>1</ClientId>
    <StationId>2</StationId>
    <StationName>Station-2</StationName>
    <DateTime>13/09/15</DateTime>
    <TagName>AI2_2</TagName>
    <Description>TR DC Current</Description>
    <Units>Amps</Units>
    <NormalOperation>10 to 100</NormalOperation>
    <AlarmValue>132.48</AlarmValue>
    <AlarmDescription>
    </AlarmDescription>
  </AlarmSummary>
  <AlarmSummary>
    <ClientId>1</ClientId>
    <StationId>2</StationId>
    <StationName>Station-2</StationName>
    <DateTime>13/09/15</DateTime>
    <TagName>AI2_2</TagName>
    <Description>TR AC Current</Description>
    <Units>Amps</Units>
    <NormalOperation>10 to 100</NormalOperation>
    <AlarmValue>132.48</AlarmValue>
    <AlarmDescription>
    </AlarmDescription>
  </AlarmSummary>
</ArrayOfAlarmSummary>

Here is the deserialize Class, using that you can deserialize your Xml file.

 public class SerializeDeserialize<T>
    {
        StringBuilder sbData;
        StringWriter swWriter;
        XmlDocument xDoc;
        XmlNodeReader xNodeReader;
        XmlSerializer xmlSerializer;
        public SerializeDeserialize()
        {
            sbData = new StringBuilder();
        }
        public string SerializeData(T data)
        {
            XmlSerializer employeeSerializer = new XmlSerializer(typeof(T));
            swWriter = new StringWriter(sbData);
            employeeSerializer.Serialize(swWriter, data);
            return sbData.ToString();
        }

        public T DeserializeData(string dataXML)
        {
            xDoc = new XmlDocument();
            xDoc.LoadXml(dataXML);
            xNodeReader = new XmlNodeReader(xDoc.DocumentElement);
            xmlSerializer = new XmlSerializer(typeof(T));
            var employeeData = xmlSerializer.Deserialize(xNodeReader);
            T deserializedEmployee = (T)employeeData;
            return deserializedEmployee;
        }
    }

Call the class

 var appDomain = System.IO.Directory.GetCurrentDirectory() + "\\AlarmFiles";
            var strPath = Path.Combine(appDomain,
                "Alarm_" + DateTime.Now.ToString("ddMMyyyy") + Constants.FileType.XmlFile);
            var fi = new FileInfo(strPath);
            if (fi.Exists)
            {
                try
                {
                    var xmlString = System.IO.File.ReadAllText(strPath);
                    var serializeAlarmSummary =
                        new SerializeDeserialize<List<AlarmSummary>>();
                    return serializeAlarmSummary.DeserializeData(xmlString);
                }
                catch (Exception ex)
                {
                    throw ex;
                }

            }

Upvotes: 0

Henrique Miranda
Henrique Miranda

Reputation: 1108

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.

Upvotes: 15

sm_
sm_

Reputation: 2602

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

Upvotes: 1

CSJ
CSJ

Reputation: 3941

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

Upvotes: 3

Related Questions