Deserializzazione utilizzando XElement
-
21-09-2019 - |
Domanda
ho dovuto scrivere il mio deserializzatore, perché XmlSerializer e DataContractSerializer non sono buone per le mie esigenze. Quindi, ecco la base del mio 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;
}
La classe BaseElement:
public abstract class BaseElement
{
public BaseElement()
{
Elements = new List<Element>();
}
public IList<Element> Elements
{
get;
set;
}
}
L'unica limitazione è che non posso avere gli attributi digitati su misura perché non riesco a convertire i tipi personalizzati utilizzando Convert.ChangeType. Tutte le idee su come risolvere questo problema?
Grazie.
Soluzione
È possibile creare i propri tipi personalizzati convertibile implementando l'interfaccia IConvertible
. Ecco un esempio da MSDN.
Altri suggerimenti
IConvertible non definisce come una stringa può essere usato per creare un'istanza del tipo come sarebbe necessario in questo caso. È possibile creare un costruttore di conversione, ma, fastidiosamente, non nto automaticamente richiamate quando si tenta di impostare i valori attraverso la riflessione per qualche motivo. Quindi dovrete cercare manualmente il costruttore di conversione e chiamarlo quando applicabile. Questo è su come lo farei:
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;
}
}
}
}