Bruno Henri
Bruno Henri

Reputation: 373

Most efficient way to determine how class use to Deserialize XML

I have many .xsd files for many xml schemas

example

XML 1.0 - xml_1_0.xml

<?xml version="1.0" encoding="UTF-8"?>
     <cars version="1.00">
         <car>Honda</car>
         <car>Ferrari</car>
     </cars>

XML 2.0 - xml_2_0.xml

<?xml version="1.0" encoding="UTF-8"?>
 <cars version="2.00">
     <car>
       <name>Honda</name>
       <color>White</color>
     </car>

     <car>
       <name>Honda</name>
       <color>Red</color>
     </car>
 </cars>

I create my classes from .xsd like this

xsd.exe cars_1_0.xsd /c

xsd.exe cars_2_0.xsd /c

And Deserialize like this:

foreach(string file in files) {
     XmlDocument doc = new XmlDocument();
     doc.Load(file);    
     string version =   doc.SelectSingleNode("/Cars/@version").Value;

     if(version == "1.00")
     {
         Stream reader = new FileStream(file, FileMode.Open);
         XmlSerializer serializer = new XmlSerializer(typeof(v1.Cars));

         v1.Cars XML = new v1.Cars();
         XML = (v1.Cars)serializer.Deserialize(reader);
     } 
     else if(version == "2.00") 
     {
         Stream reader = new FileStream(file, FileMode.Open);
         XmlSerializer serializer = new XmlSerializer(typeof(v2.Cars));

         v2.Cars XML = new v2.Cars();
         XML = (v2.Cars)serializer.Deserialize(reader);
     }
}

Does anyone know a better way to do this, or have a better performance?

Upvotes: 2

Views: 119

Answers (1)

Dan Field
Dan Field

Reputation: 21661

You have several options, depending on how far you want to take this. One fairly non invasive option would be to not use XmlDocument and avoid loading the stream more than once. For example, your existing code could be simplified/streamlined to :

foreach (string file in files)
{
    using (var stream = new FileStream(file, FileMode.Open))
    {
        var settings = new XmlReaderSettings();
        settings.CloseInput = false;
        string version = "";
        using (var xmlReader = XmlReader.Create(stream))
        {
            if (xmlReader.ReadToFollowing("Cars"))
            {
                version = xmlReader.GetAttribute("version");
            }
            else
            {
                throw new XmlException("Could not get 'version' attribute of 'Cars' root element!");
            }                    
        }
        stream.Position = 0;
        if (version == "1.00")
        {
            XmlSerializer serializer = new XmlSerializer(typeof(v1.Cars));

            v1.Cars XML = new v1.Cars();
            XML = (v1.Cars)serializer.Deserialize(stream);
        }
        else if (version == "2.00")
        {
            XmlSerializer serializer = new XmlSerializer(typeof(v2.Cars));

            v2.Cars XML = new v2.Cars();
            XML = (v2.Cars)serializer.Deserialize(stream);

        }
    }
}

Since you're just reading off the root element, you might even be able to get away with deserializing from the XmlReader and not have to reset the position on the FileStream.

This avoids the overhead of loading the entire file twice (once for XmlDocument, then again for XmlSerializer) - and particularly avoids the memory overhead of creating a DOM for each document.

A more nuclear option would be implementing IXmlSerializable on a set of custom classes, which would have custom logic in the ReadXml methods to parse the version attribute and instantiate the correct child type(s) - e.g. a CarCollection class that has a List<Car> property, where Car is an abstract class that has CarV1 and CarV2 as descendants. This would be about as efficient as you could get (and offer very fine grained control over your class hierarchy design), but would eliminate the possibility of using xsd.exe to generate your classes.

Upvotes: 1

Related Questions