XML deserialization: if two elements are present how can I bind to one in preference to the other?

64 views Asked by At

I need to deserialize XML containing elements in one of the following three forms:

<Element1>xxx</Element1>

or

<Element2>xxx</Element2>

or

<Element1>xxx</Element1>
<Element2>xxx</Element2>

If both are present I need to use value of Element1 in preference to Element2. How can I do that?

I am using System.Xml.Serialization to parse the xml This is my property

[XmlElement("Element1")]
[XmlElement("Element2")]
public string Data
{
    get
    {
        return this.data;
    }
    set
    {
        this.data = value;
    }
}

In my case, I need to use Element1 if both are present, else use the one which is present.

I was not able to come up with the solution for this.

2

There are 2 answers

3
jdweng On

Try this.

   public class Parent : IXmlSerializable
    {
        public string data { get; set; }
        

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteString(data);
        }

        public void ReadXml(XmlReader reader)
        {
            XElement node = (XElement)XElement.ReadFrom(reader);
            XElement element1 = node.Element("Element1");
            XElement element2 = node.Element("Element2");


            if(element1 == null)
            {
                data = (string)element2;
            }
            else
            {
                data = (string)element1;
            }



        }

        public XmlSchema GetSchema()
        {
            return (null);
        }

    }
0
Alexander Petrov On

There are many ways to solve your problem.

One of them is to use the UnknownElement event.

using System.Xml.Serialization;

var ser = new XmlSerializer(typeof(Root));
ser.UnknownElement += Serializer_UnknownElement;

void Serializer_UnknownElement(object? sender, XmlElementEventArgs e)
{
    var root = (Root)e.ObjectBeingDeserialized;
    var elem = e.Element;

    if (elem.Name == "Element1")
        root.Data = elem.InnerText;
    else if (elem.Name == "Element2" && root.Data == null)
        root.Data = elem.InnerText;
}

using var fs = new FileStream("test.xml", FileMode.Open);
var root = (Root)ser.Deserialize(fs);
Console.WriteLine(root.Data);

public class Root
{
    public string Data { get; set; }
}

Another way is to use a custom reader that replaces data on the fly.

using System.Xml;
using System.Xml.Serialization;

var ser = new XmlSerializer(typeof(Root));
using var reader = new CustomXmlReader("test.xml");
var root = (Root)ser.Deserialize(reader);
Console.WriteLine(root.Data);

public class CustomXmlReader : XmlTextReader
{
    public CustomXmlReader(string url) : base(url) { }

    private bool dataRead;

    public override string LocalName
    {
        get
        {
            if (base.LocalName == "Element1")
            {
                dataRead = true;
                return "Data";
            }
            else if (base.LocalName == "Element2" && !dataRead)
                return "Data";

            return base.LocalName;
        }
    }
}

public class Root
{
    public string Data { get; set; }
}