Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

deserialize with different root element names

The following example gives me "[One xmlns=''] was not expected." exception

public abstract class BaseClass{ }

[XmlRoot("One")]
public class ChildOne : BaseClass {}

[XmlRoot("Two")]
public class ChildTwo : BaseClass { }

class Program
{
    private static void Main(string[] args)
    {
        var ser = new XmlSerializer(typeof (BaseClass), new Type[] {typeof (ChildOne), typeof (ChildTwo)});
        var obj1 = ser.Deserialize(new StringReader(@"<?xml version=""1.0""?><One></One>"));
        var obj2 = ser.Deserialize(new StringReader(@"<?xml version=""1.0""?><Two></Two>"));
    }
}

I need to deserialize XML (generated not by me). Root tag may have different names which I have to map to different classes.

PS. I know there is a lot of questions like this around. I have studied them but my problem is still not solved.

like image 632
m_kramar Avatar asked Oct 03 '22 07:10

m_kramar


2 Answers

No hype building up around my question. Community might be thinking it is just another stupid question asked by an idiot. They might be right. I'll have to answer it myself but beware: the answer might be stupid too.

I ended up probing root element of XML, then mapping it to one of the known types and deserializing using that type. Like so:

public abstract class BaseClass{ }

[XmlRoot("One")]
public class ChildOne : BaseClass {}

[XmlRoot("Two")]
public class ChildTwo : BaseClass { }

class Program
{
    private static void Main(string[] args)
    {
        var known = new Type[] {typeof (ChildOne), typeof (ChildTwo)};
        var obj1 = Deserialize<BaseClass>(@"<?xml version=""1.0""?><One></One>", known);
        var obj2 = Deserialize<BaseClass>(@"<?xml version=""1.0""?><Two></Two>", known);
    }

    private static T Deserialize<T>(string xml, Type[] knownTypes)
    {
        Type rootType = typeof (T);

        if (knownTypes.Any())
        {
            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                reader.MoveToContent();

                rootType = (from kt in knownTypes
                        let xmlRoot = kt.GetCustomAttributes<XmlRootAttribute>().FirstOrDefault()
                        where kt.Name == reader.Name || (xmlRoot != null && xmlRoot.ElementName == reader.Name)
                        select kt).FirstOrDefault() ?? typeof(T);
            }
        }

        return (T) new XmlSerializer(rootType, knownTypes).Deserialize(new StringReader(xml));
    }
}
like image 139
m_kramar Avatar answered Oct 11 '22 09:10

m_kramar


To expand on your answer you could use a little LinqToXml to parse the xml to get the root name.

private T Deserialize<T>(string xml, Type[] knownTypes)
{
    var rootType = knownTypes.FirstOrDefault(t => t.GetCustomAttributes<XmlRootAttribute>()
                                                   .Any(a => a.ElementName == XElement.Parse(xml).Name.LocalName));

    return (T)new XmlSerializer(rootType ?? typeof(T), knownTypes).Deserialize(new StringReader(xml));
}
like image 34
sa_ddam213 Avatar answered Oct 11 '22 08:10

sa_ddam213