views:

666

answers:

2

I had to write my own deserializer, because XmlSerializer and DataContractSerializer aren't good for my needs. So, here's the base of my deserializer:

    static BaseElement ParseXml(XElement element)
    {
        var e = (Element)Activator.CreateInstance(Type.GetType("Elements." + element.Name));

        foreach (var attr in element.Attributes())
        {
            var property = e.GetType().GetProperty(attr.Name.LocalName);
            property.SetValue(e, Convert.ChangeType(attr.Value, property.PropertyType), null);
        }

        foreach (var x in element.Elements())
            e.Elements.Add(ParseXml(x));

        return e;
    }

The BaseElement class:

public abstract class BaseElement
{
    public BaseElement()
    {
        Elements = new List<Element>();
    }

    public IList<Element> Elements
    {
        get;
        set;
    }
}

The only limitation is, that I can't have custom typed attributes because I can't convert to custom types using Convert.ChangeType. Any ideas on how to solve this?

Thanks.

A: 

You can make your own custom types convertible by implementing the IConvertible interface. Here's an example from MSDN.

Cory Larson
A: 

IConvertible doesn't define how a string can be used to create an instance of the type as would be needed in this case. You can create a conversion constructor, but, annoyingly, it does nto automatically get called when attempting to set values via reflection for some reason. So you'll have to manually look up the conversion constructor and call it when applicable. This is about how I'd do it:

namespace Elements
{
  class Program
  {
     static void Main(string[] args)
     {
        System.Xml.Linq.XElement sample = System.Xml.Linq.XElement.Parse(
           "<Element a=\"3\" b=\"Havarti\" modeSel=\"Complex\" />");

        Element c1 = Element.ParseXml(sample);
     }
  }

  public class ModeSelection
  {
     private int mode;

     public static explicit operator ModeSelection(string value)
     {
        ModeSelection result = new ModeSelection();
        if (String.Compare(value, "Simple", true) == 0)
           result.mode = 1;
        else if (String.Compare(value, "Complex", true) == 0)
           result.mode = 2;
        else if (!int.TryParse(value, out result.mode))
           throw new FormatException("Cannot convert value to type " + result.GetType().Name);
        return result;
     }

     string Description
     {
        get
        {
           switch (mode)
           {
              case 1:
                 return "Simple";
              case 2:
                 return "Complex";
              default:
                 return "Other";
           }
        }  
     }
  }

  public abstract class BaseElement<T> where T : BaseElement<T>, new()
  {
     public static T ParseXml(System.Xml.Linq.XElement element)
     {
        var e = (T)Activator.CreateInstance(Type.GetType("Elements." + element.Name));

        Type[] convParamTypes = new Type[] {typeof(string)};

        foreach (var attr in element.Attributes())
        {
           var property = e.GetType().GetProperty(attr.Name.LocalName);
           System.Reflection.MethodInfo conv = property.PropertyType.GetMethod(
              "op_Explicit", convParamTypes);

           if (conv != null)
              property.SetValue(e, conv.Invoke(null, new object[] {attr.Value}), null);
           else
              property.SetValue(e, Convert.ChangeType(attr.Value, property.PropertyType), null);
        }

        foreach (var x in element.Elements())
           e.Elements.Add(ParseXml(x));

        return e;
     }

     public BaseElement()
     {
        Elements = new List<T>();
     }

     public IList<T> Elements
     {
        get;
        set;
     }
  }

  public class Element : BaseElement<Element>
  {
     int _a;
     string _b;
     ModeSelection _modeSel;

     public int a
     {
        get
        {
           return _a;
        }
        set
        {
           _a = value;
        }
     }

     public string b
     {
        get
        {
           return _b;
        }
        set
        {
           _b = value;
        }
     }

     public ModeSelection modeSel
     {
        get
        {
           return _modeSel;
        }
        set
        {
           _modeSel = value;
        }
     }
  }
}
BlueMonkMN