using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml.Linq;
using System.IO;
using System.Diagnostics;
using System.Collections;
using System.Xml;
using System.Xml.Schema;
namespace YAXLib
{
public class YAXSerializer
{
#region Private Members
private Type m_type;
private YAXExceptionHandlingPolicies m_exPolicy = YAXExceptionHandlingPolicies.ThrowErrorsOnly;
private YAXParsingErrors m_parsingErrors = new YAXParsingErrors();
private YAXExceptionTypes m_defaultExceptionType = YAXExceptionTypes.Warning;
private YAXSerializationOptions m_serializationOption = YAXSerializationOptions.SerializeNullObjects;
/// <summary>
/// Gets the parsing errors.
/// </summary>
/// <value>The parsing errors.</value>
public YAXParsingErrors ParsingErrors
{
get
{
return m_parsingErrors;
}
}
#endregion
#region Construction Stuff
/// <summary>
/// Initializes a new instance of the <see cref="YAXSerializer"/> class.
/// </summary>
/// <param name="t">The type of the object being serialized/deserialized.</param>
public YAXSerializer(Type t)
: this(t, YAXExceptionHandlingPolicies.ThrowWarningsAndErrors, YAXExceptionTypes.Error, YAXSerializationOptions.SerializeNullObjects)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="YAXSerializer"/> class.
/// </summary>
/// <param name="t">The type of the object being serialized/deserialized.</param>
/// <param name="exPolicy">The exception handling policy.</param>
public YAXSerializer(Type t, YAXExceptionHandlingPolicies exPolicy)
: this(t, exPolicy, YAXExceptionTypes.Error, YAXSerializationOptions.SerializeNullObjects)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="YAXSerializer"/> class.
/// </summary>
/// <param name="t">The type of the object being serialized/deserialized.</param>
/// <param name="exPolicy">The exception handling policy.</param>
/// <param name="defaultExType">The exceptions are treated as the value specified, unless otherwise specified.</param>
public YAXSerializer(Type t, YAXExceptionHandlingPolicies exPolicy, YAXExceptionTypes defaultExType)
: this(t, exPolicy, defaultExType, YAXSerializationOptions.SerializeNullObjects)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="YAXSerializer"/> class.
/// </summary>
/// <param name="t">The type of the object being serialized/deserialized.</param>
/// <param name="exPolicy">The exception handling policy.</param>
/// <param name="defaultExType">The exceptions are treated as the value specified, unless otherwise specified.</param>
/// <param name="option">The serialization option.</param>
public YAXSerializer(Type t, YAXExceptionHandlingPolicies exPolicy, YAXExceptionTypes defaultExType, YAXSerializationOptions option)
{
this.m_type = t;
this.m_exPolicy = exPolicy;
this.m_defaultExceptionType = defaultExType;
this.m_serializationOption = option;
}
#endregion
#region Utilities
/// <summary>
/// Gets the friendly name for the type. Recommended for generic types.
/// </summary>
/// <param name="type">The type to get its friendly name</param>
/// <returns></returns>
private static string GetTypeFriendlyName(Type type)
{
if (type == null)
throw new System.ArgumentNullException("type");
string name = type.Name;
if (type.IsGenericType)
{
int backqIndex = name.IndexOf('`');
if (backqIndex == 0)
{
throw new InvalidOperationException("Bad type name: " + name);
}
else if (backqIndex > 0)
{
name = name.Substring(0, backqIndex);
}
name += "Of";
foreach (Type genType in type.GetGenericArguments())
{
name += GetTypeFriendlyName(genType);
}
}
return name;
}
/// <summary>
/// Determines whether the type specified contains generic parameters or not.
/// </summary>
/// <param name="type">The type to check.</param>
/// <returns></returns>
private static bool TypeContainsGenericParameters(Type type)
{
if (type == null)
throw new System.ArgumentNullException("type");
if (type.IsGenericType)
{
foreach (Type genType in type.GetGenericArguments())
{
if (genType.IsGenericParameter)
return true;
else if (TypeContainsGenericParameters(genType))
return true;
}
}
return false;
}
/// <summary>
/// Gets the type of the items of a collection type.
/// </summary>
/// <param name="type">The type of the collection.</param>
/// <returns>the type of the items of a collection type.</returns>
private Type GetCollectionItemType(Type type)
{
Type itemType = typeof(object);
if (type.IsInterface && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
itemType = type.GetGenericArguments()[0];
}
else if (type.IsInterface && type == typeof(IEnumerable))
{
itemType = typeof(object);
}
else
{
foreach (Type interfaceType in type.GetInterfaces())
{
if (interfaceType.IsGenericType &&
interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
itemType = interfaceType.GetGenericArguments()[0];
}
}
}
return itemType;
}
/// <summary>
/// Determines whether the specified type is basic type. A basic type is one that can be wholly expressed
/// as an XML attribute. All primitive data types and type <c>string</c> and <c>DataTime</c> are basic.
/// </summary>
/// <param name="t">The type</param>
private static bool IsBasicType(Type t)
{
if (t == typeof(string) || t.IsPrimitive || t.IsEnum || t == typeof(DateTime))
return true;
else
return false;
}
/// <summary>
/// Determines whether the specified type is a collection type.
/// </summary>
/// <param name="t">The type to check.</param>
/// <returns>
/// <c>true</c> if the specified type is a collection type; otherwise, <c>false</c>.
/// </returns>
private static bool IsCollectionType(Type t)
{
if (t == typeof(string))
return false;
return typeof(IEnumerable).IsAssignableFrom(t);
}
/// <summary>
/// Determines whether the specified type is array.
/// </summary>
/// <param name="t">The type</param>
/// <returns>
/// <c>true</c> if the specified type is array; otherwise, <c>false</c>.
/// </returns>
private static bool IsArray(Type t)
{
return (t.BaseType == typeof(System.Array));
}
/// <summary>
/// Determines whether the specified type is <c>List</c>.
/// </summary>
/// <param name="t">The type to check.</param>
/// <returns>
/// <c>true</c> if the specified type is list; otherwise, <c>false</c>.
/// </returns>
private static bool IsList(Type t)
{
foreach (Type interfaceType in t.GetInterfaces())
{
if (interfaceType.IsGenericType &&
interfaceType.GetGenericTypeDefinition() == typeof(IList<>))
{
return true;
}
}
return false;
}
/// <summary>
/// Determines whether the specified type has implemented the ICollection interface.
/// </summary>
/// <param name="t">The type to check.</param>
/// <param name="itemType">Type of the member items.</param>
/// <returns>
/// <c>true</c> if the specified type has implemented the ICollection interface; otherwise, <c>false</c>.
/// </returns>
private static bool IsICollection(Type t, out Type itemType)
{
itemType = typeof(object);
if (t.IsInterface && t.GetGenericTypeDefinition() == typeof(ICollection<>))
{
itemType = t.GetGenericArguments()[0];
return true;
}
foreach (Type interfaceType in t.GetInterfaces())
{
if (interfaceType.IsGenericType &&
interfaceType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
itemType = interfaceType.GetGenericArguments()[0];
return true;
}
}
return false;
}
/// <summary>
/// Determines whether the specified type is a generic dictionary.
/// </summary>
/// <param name="type">The type to check.</param>
/// <param name="keyType">Type of the key.</param>
/// <param name="valueType">Type of the value.</param>
/// <returns>
/// <c>true</c> if the specified type is dictionary; otherwise, <c>false</c>.
/// </returns>
private static bool IsDictionary(Type type, out Type keyType, out Type valueType)
{
keyType = typeof(object);
valueType = typeof(object);
foreach (Type interfaceType in type.GetInterfaces())
{
if (interfaceType.IsGenericType &&
interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
Type[] genArgs = interfaceType.GetGenericArguments();
keyType = genArgs[0];
valueType = genArgs[1];
return true;
}
}
return false;
}
/// <summary>
/// Determines whether the specified type is a generic dictionary.
/// </summary>
/// <param name="type">The type to check.</param>
/// <returns>
/// <c>true</c> if the specified type is dictionary; otherwise, <c>false</c>.
/// </returns>
private bool IsDictionary(Type type)
{
Type keyType, valueType;
return IsDictionary(type, out keyType, out valueType);
}
/// <summary>
/// Determines whether the specified type has implemented or is an <c>IEnumerable</c> or <c>IEnumerable<></c> .
/// </summary>
/// <param name="type">The type to check.</param>
/// <param name="seqType">Type of the sequence items.</param>
/// <returns>
/// <c>true</c> if the specified type is enumerable; otherwise, <c>false</c>.
/// </returns>
private static bool IsEnumerable(Type type, out Type seqType)
{
seqType = typeof(object);
bool isNongenericEnumerable = false;
if (type.IsInterface && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
seqType = type.GetGenericArguments()[0];
return true;
}
foreach (Type interfaceType in type.GetInterfaces())
{
if (interfaceType.IsGenericType &&
interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
Type[] genArgs = interfaceType.GetGenericArguments();
seqType = genArgs[0];
return true;
}
else if(interfaceType == typeof(IEnumerable))
{
isNongenericEnumerable = true;
}
}
if (isNongenericEnumerable)
return true;
return false;
}
/// <summary>
/// Called when an exception occurs inside the library. It applies the exception handling policies.
/// </summary>
/// <param name="ex">The exception that has occurred.</param>
/// <param name="exType">Type of the exception.</param>
private void OnExceptionOccurred(YAXException ex, YAXExceptionTypes exType)
{
if (exType == YAXExceptionTypes.Ignore)
return;
m_parsingErrors.AddException(ex, exType);
if ((m_exPolicy == YAXExceptionHandlingPolicies.ThrowWarningsAndErrors) ||
(m_exPolicy == YAXExceptionHandlingPolicies.ThrowErrorsOnly && exType == YAXExceptionTypes.Error))
throw ex;
}
/// <summary>
/// Tries to format the specified object using the format string provided.
/// If the formatting operation is not applicable, the source object is returned intact.
/// Note: The type of the returned object will be 'System.String' if formatting succeeds.
/// </summary>
/// <param name="src">The source object.</param>
/// <param name="format">The format string.</param>
/// <returns></returns>
private object TryFormatObject(object src, string format)
{
if (format == null || src == null)
return src;
object formattedObject = null;
try
{
formattedObject = src.GetType().InvokeMember("ToString", BindingFlags.InvokeMethod, null, src, new object[] { format } );
}
catch
{
OnExceptionOccurred(new YAXInvalidFormatProvided(src.GetType(), format), m_defaultExceptionType);
}
return (formattedObject != null) ? formattedObject : src;
}
#endregion
#region Serialization Stuff
/// <summary>
/// Serializes the specified object and returns a string containing the XML.
/// </summary>
/// <param name="obj">The object to serialize.</param>
/// <returns></returns>
public string Serialize(object obj)
{
return SerializeBase(obj).ToString();
}
/// <summary>
/// Serializes the specified object into a <c>TextWriter</c> instance.
/// </summary>
/// <param name="obj">The object to serialize.</param>
/// <param name="textWriter">The <c>TextWriter</c> instance.</param>
public void Serialize(object obj, TextWriter textWriter)
{
textWriter.Write(Serialize(obj));
}
/// <summary>
/// Serializes the specified object into a <c>XmlWriter</c> instance.
/// </summary>
/// <param name="obj">The object to serialize.</param>
/// <param name="xmlWriter">The <c>XmlWriter</c> instance.</param>
public void Serialize(object obj, XmlWriter xmlWriter)
{
SerializeBase(obj).WriteTo(xmlWriter);
}
/// <summary>
/// Serializes the specified object to file.
/// </summary>
/// <param name="obj">The object to serialize.</param>
/// <param name="fileName">Path to the file.</param>
public void SerializeToFile(object obj, string fileName)
{
string ser = String.Format("{0}{1}{2}",
"<?xml version=\"1.0\" encoding=\"utf-8\"?>",
Environment.NewLine,
Serialize(obj));
File.WriteAllText(fileName, ser, Encoding.UTF8);
}
private XElement MakeDictionaryElement(string elementName, object elementValue, YAXDictionaryAttribute dicAttrInst, YAXCollectionAttribute collectionAttrInst)
{
if (elementValue == null)
return new XElement(elementName, elementValue);
Type keyType, valueType;
if(!IsDictionary(elementValue.GetType(), out keyType, out valueType))
throw new ArgumentException("elementValue must be a Dictionary");
IEnumerable dicInst = elementValue as IEnumerable;
string eachElementName = null;
bool isKeyAttrib = false;
bool isValueAttrib = false;
string keyFormat = null;
string valueFormat = null;
string keyAlias = "Key";
string valueAlias = "Value";
if (collectionAttrInst != null)
{
eachElementName = collectionAttrInst.EachElementName;
}
if (dicAttrInst != null)
{
eachElementName = dicAttrInst.EachPairName ?? eachElementName;
if (dicAttrInst.SerializeKeyAs == YAXNodeTypes.Attribute)
isKeyAttrib = IsBasicType(keyType);
if (dicAttrInst.SerializeValueAs == YAXNodeTypes.Attribute)
isValueAttrib = IsBasicType(valueType);
keyFormat = dicAttrInst.KeyFormatString;
valueFormat = dicAttrInst.ValueFormatString;
keyAlias = dicAttrInst.KeyName ?? "Key";
valueAlias = dicAttrInst.ValueName ?? "Value";
}
XElement elem = new XElement(elementName, null);
foreach (object obj in dicInst)
{
object keyObj = obj.GetType().GetProperty("Key").GetValue(obj, null);
object valueObj = obj.GetType().GetProperty("Value").GetValue(obj, null);
if (keyFormat != null)
keyObj = TryFormatObject(keyObj, keyFormat);
if (valueFormat != null)
valueObj = TryFormatObject(valueObj, valueFormat);
XElement elemChild = new XElement(eachElementName ?? GetTypeFriendlyName(obj.GetType()), null);
if (isKeyAttrib)
elemChild.Add(new XAttribute(keyAlias, keyObj ?? ""));
else
elemChild.Add(MakeElement(keyAlias, keyObj));
if (isValueAttrib)
elemChild.Add(new XAttribute(valueAlias, valueObj ?? ""));
else
elemChild.Add(MakeElement(valueAlias, valueObj));
elem.Add(elemChild);
}
return elem;
}
private XElement MakeCollectionElement(string elementName, object elementValue, YAXCollectionAttribute collectionAttrInst, string format)
{
if (elementValue == null)
return new XElement(elementName, elementValue);
if (!(elementValue is IEnumerable))
throw new ArgumentException("elementValue must be an IEnumerable");
IEnumerable collectionInst = elementValue as IEnumerable;
YAXCollectionSerializationTypes serType = YAXCollectionSerializationTypes.Recursive;
string seperator = "";
string eachElementName = null;
if (collectionAttrInst != null)
{
serType = collectionAttrInst.SerializationType;
seperator = collectionAttrInst.SeparateBy;
eachElementName = collectionAttrInst.EachElementName;
}
if (eachElementName == null)
{
Type colItemType = GetCollectionItemType(elementValue.GetType());
eachElementName = GetTypeFriendlyName(colItemType);
}
if (serType == YAXCollectionSerializationTypes.Serially)
{
StringBuilder sb = new StringBuilder();
bool isFirst = true;
object objToAdd = null;
foreach(object obj in collectionInst)
{
objToAdd = (format == null) ? obj : TryFormatObject(obj, format);
if(isFirst)
{
sb.Append(objToAdd.ToString());
isFirst = false;
}
else
{
sb.AppendFormat("{0}{1}", seperator, objToAdd.ToString());
}
}
return MakeElement(elementName, sb.ToString());
}
else
{
XElement elem = new XElement(elementName, null);
object objToAdd = null;
foreach (object obj in collectionInst)
{
objToAdd = (format == null) ? obj : TryFormatObject(obj, format);
elem.Add(MakeElement(eachElementName, objToAdd));
}
return elem;
}
}
private XElement MakeElement(string name, object value)
{
if (value == null || IsBasicType(value.GetType()))
return new XElement(name, value);
else
{
YAXSerializer ser = new YAXSerializer(value.GetType(), m_exPolicy, m_defaultExceptionType, m_serializationOption);
XElement elem = ser.SerializeBase(value, name);
m_parsingErrors.AddRange(ser.ParsingErrors);
return elem;
}
}
protected XElement SerializeBase(object obj)
{
if (!m_type.IsInstanceOfType(obj))
throw new YAXObjectTypeMismatch(m_type, obj.GetType());
string className = GetTypeFriendlyName(obj.GetType());
foreach (var classAttr in obj.GetType().GetCustomAttributes(true))
{
if (classAttr is YAXSerializeAsAttribute)
{
className = (classAttr as YAXSerializeAsAttribute).SerializeAs;
}
}
return SerializeBase(obj, className);
}
protected XElement SerializeBase(object obj, string className)
{
XElement baseElement = new XElement(
className, null);
// find interesting class attributes
foreach (var classAttr in obj.GetType().GetCustomAttributes(true))
{
if (classAttr is YAXCommentAttribute)
{
baseElement.Add(new XComment((classAttr as YAXCommentAttribute).Comment));
}
}
// iterate through public properties
foreach(var p in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
string elementName = "";
object elementValue = null;
bool dontSerialize = false;
bool isClassAttr = false;
bool attributeFor = false;
bool elementFor = false;
string format = null;
string parentElement = "";
bool isNotCollection = false; // Set by the [YAXNotCollection] Attribute
bool isCollection = IsCollectionType(p.PropertyType);
YAXCollectionAttribute collectionAttrInst = null;
bool isDictionary = IsDictionary(p.PropertyType);
YAXDictionaryAttribute dictionaryAttrInst = null;
if (p.CanRead)
{
if (p.PropertyType == m_type)
{
throw new YAXCannotSerializeSelfReferentialTypes(m_type);
}
elementName = p.Name;
elementValue = p.GetValue(obj, null);
if (elementValue == null && IsNotAllowdNullObjectSerialization())
continue;
foreach (var attr in p.GetCustomAttributes(true))
{
if (attr is YAXSerializeAsAttribute)
{
elementName = (attr as YAXSerializeAsAttribute).SerializeAs;
}
else if (attr is YAXDontSerializeAttribute)
{
dontSerialize = true;
}
else if (attr is YAXAttributeForClassAttribute)
{
isClassAttr = IsBasicType(p.PropertyType);
}
else if (attr is YAXAttributeForAttribute)
{
attributeFor = IsBasicType(p.PropertyType);
parentElement = (attr as YAXAttributeForAttribute).Parent;
}
else if (attr is YAXElementForAttribute)
{
elementFor = true;
parentElement = (attr as YAXElementForAttribute).Parent;
}
else if (attr is YAXFormatAttribute)
{
format = (attr as YAXFormatAttribute).Format;
}
else if (attr is YAXCollectionAttribute)
{
collectionAttrInst = attr as YAXCollectionAttribute;
}
else if (attr is YAXDictionaryAttribute)
{
dictionaryAttrInst = attr as YAXDictionaryAttribute;
}
else if (attr is YAXNotCollectionAttribute)
{
isNotCollection = true;
}
}
if (isNotCollection)
isCollection = isDictionary = false;
if (format != null && !isCollection)
{
// do the formatting. If formatting succeeds the type of
// the elementValue will become 'System.String'
elementValue = TryFormatObject(elementValue, format);
}
if (!dontSerialize)
{
if (isClassAttr) // it gets true only for basic data types
{
if (baseElement.Attribute(elementName) == null)
baseElement.Add(new XAttribute(elementName, elementValue ?? ""));
else
throw new YAXAttributeAlreadyExistsException(elementName);
}
else
{
if (attributeFor || elementFor)
{
XElement parElem = baseElement.Element(parentElement);
if (parElem == null)
{
parElem = new XElement(parentElement);
baseElement.Add(parElem);
}
if (attributeFor)
{
if (parElem.Attribute(elementName) != null)
throw new YAXAttributeAlreadyExistsException(elementName);
else
parElem.Add(new XAttribute(elementName, elementValue ?? ""));
}
else if (elementFor)
{
XElement elemToAdd;
if (isDictionary)
elemToAdd = MakeDictionaryElement(elementName, elementValue, dictionaryAttrInst, collectionAttrInst);
else if (isCollection)
elemToAdd = MakeCollectionElement(elementName, elementValue, collectionAttrInst, format);
else
elemToAdd = MakeElement(elementName, elementValue);
parElem.Add(elemToAdd);
}
}
else
{
XElement t = baseElement.Element(elementName);
if (t == null)
{
if (isDictionary)
baseElement.Add(MakeDictionaryElement(elementName, elementValue, dictionaryAttrInst, collectionAttrInst));
else if (isCollection)
baseElement.Add(MakeCollectionElement(elementName, elementValue, collectionAttrInst, format));
else
baseElement.Add(MakeElement(elementName, elementValue));
}
else
{
if (IsBasicType(p.PropertyType))
{
t.SetValue(elementValue);
}
else
{
XElement elemToAdd;
if (isDictionary)
elemToAdd = MakeDictionaryElement(elementName, elementValue, dictionaryAttrInst, collectionAttrInst);
else if (isCollection)
elemToAdd = MakeCollectionElement(elementName, elementValue, collectionAttrInst, format);
else
elemToAdd = MakeElement(elementName, elementValue);
MoveDescendants(elemToAdd, t);
}
}
}
}
}
}
}
return baseElement;
}
private bool IsNotAllowdNullObjectSerialization()
{
return m_serializationOption == YAXSerializationOptions.DontSerializeNullObjects;
}
private static void MoveDescendants(XElement src, XElement dst)
{
foreach (XAttribute attr in src.Attributes())
{
if (dst.Attribute(attr.Name) != null)
{
throw new YAXAttributeAlreadyExistsException(attr.Name.ToString());
}
dst.Add(attr);
}
foreach (XElement elem in src.Elements())
{
dst.Add(elem);
}
}
#endregion
#region Deserialization Stuff
/// <summary>
/// Deserializes the specified string containing the XML serialization and returns an object.
/// </summary>
/// <param name="input">The input string containing the XML serialization.</param>
/// <returns></returns>
public object Deserialize(string input)
{
try
{
TextReader tr = new StringReader(input);
XDocument xdoc = XDocument.Load(tr);
XElement baseElement = xdoc.Root;
return DeserializeBase(baseElement);
}
catch (XmlException ex)
{
OnExceptionOccurred(new YAXBadlyFormedXML(ex), m_defaultExceptionType);
return null;
}
}
/// <summary>
/// Deserializes an object while reading input from an instance of <c>XmlReader</c>.
/// </summary>
/// <param name="xmlReader">The <c>XmlReader</c> instance to read input from.</param>
/// <returns></returns>
public object Deserialize(XmlReader xmlReader)
{
try
{
XDocument xdoc = XDocument.Load(xmlReader);
XElement baseElement = xdoc.Root;
return DeserializeBase(baseElement);
}
catch (XmlException ex)
{
OnExceptionOccurred(new YAXBadlyFormedXML(ex), m_defaultExceptionType);
return null;
}
}
/// <summary>
/// Deserializes an object while reading input from an instance of <c>TextReader</c>.
/// </summary>
/// <param name="textReader">The <c>TextReader</c> instance to read input from.</param>
/// <returns></returns>
public object Deserialize(TextReader textReader)
{
try
{
XDocument xdoc = XDocument.Load(textReader);
XElement baseElement = xdoc.Root;
return DeserializeBase(baseElement);
}
catch (XmlException ex)
{
OnExceptionOccurred(new YAXBadlyFormedXML(ex), m_defaultExceptionType);
return null;
}
}
/// <summary>
/// Deserializes an object from the specified file which contains the XML serialization of the object.
/// </summary>
/// <param name="fileName">Path to the file.</param>
/// <returns></returns>
public object DeserializeFromFile(string fileName)
{
try
{
return Deserialize(File.ReadAllText(fileName));
}
catch (XmlException ex)
{
OnExceptionOccurred(new YAXBadlyFormedXML(ex), m_defaultExceptionType);
return null;
}
}
private object ConvertBasicType(object value, Type dstType)
{
if (!IsBasicType(dstType))
throw new ArgumentException("Destination type must be a basic type", "dstType");
object convertedObj = null;
if (dstType.IsEnum)
convertedObj = Enum.Parse(dstType, value.ToString());
else
convertedObj = Convert.ChangeType(value, dstType);
return convertedObj;
}
private object DeserializeKeyValuePair(XElement baseElement)
{
Type[] genArgs = m_type.GetGenericArguments();
Type keyType = genArgs[0];
Type valueType = genArgs[1];
object keyValue, valueValue;
if (IsBasicType(keyType))
{
try
{
keyValue = ConvertBasicType(baseElement.Element("Key").Value, keyType);
}
catch (NullReferenceException)
{
keyValue = null;
}
}
else
{
YAXSerializer ser = new YAXSerializer(keyType, m_exPolicy, m_defaultExceptionType, m_serializationOption);
keyValue = ser.DeserializeBase(baseElement.Element("Key"));
m_parsingErrors.AddRange(ser.ParsingErrors);
}
if (IsBasicType(valueType))
{
try
{
valueValue = ConvertBasicType(baseElement.Element("Value").Value, valueType);
}
catch (NullReferenceException)
{
valueValue = null;
}
}
else
{
YAXSerializer ser = new YAXSerializer(valueType, m_exPolicy, m_defaultExceptionType, m_serializationOption);
valueValue = ser.DeserializeBase(baseElement.Element("Value"));
m_parsingErrors.AddRange(ser.ParsingErrors);
}
object pair = m_type.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, null, new object[] {keyValue, valueValue});
return pair;
}
private object DeserializeBase(XElement baseElement)
{
if (baseElement == null)
return null;
if (m_type.IsGenericType && m_type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
return DeserializeKeyValuePair(baseElement);
object o = m_type.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, null, new object[0]);
foreach (var prop in m_type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (prop.CanWrite)
{
string propName = prop.Name;
string elemValue = "";
XElement tElem = null;
object convertedObj = null;
bool dontSerialize = false;
bool isClassAttr = false;
bool attributeFor = false;
bool elementFor = false;
string parentElement = "";
string format = null;
YAXExceptionTypes exType = m_defaultExceptionType;
object exDefaultValue = null;
bool isNotCollection = false;
bool isCollection = IsCollectionType(prop.PropertyType);
YAXCollectionAttribute collectionAttrInst = null;
bool isDictionary = IsDictionary(prop.PropertyType);
YAXDictionaryAttribute dictionaryAttrInst = null;
// state variable
bool exOccurred = false;
foreach (var propAttr in prop.GetCustomAttributes(true))
{
if (propAttr is YAXSerializeAsAttribute)
{
propName = (propAttr as YAXSerializeAsAttribute).SerializeAs;
}
else if (propAttr is YAXDontSerializeAttribute)
{
dontSerialize = true;
}
else if (propAttr is YAXAttributeForClassAttribute)
{
isClassAttr = IsBasicType( prop.PropertyType );
}
else if (propAttr is YAXAttributeForAttribute)
{
attributeFor = IsBasicType(prop.PropertyType);
parentElement = (propAttr as YAXAttributeForAttribute).Parent;
}
else if (propAttr is YAXElementForAttribute)
{
elementFor = true;
parentElement = (propAttr as YAXElementForAttribute).Parent;
}
else if (propAttr is YAXFormatAttribute)
{
format = (propAttr as YAXFormatAttribute).Format;
}
else if (propAttr is YAXCollectionAttribute)
{
collectionAttrInst = propAttr as YAXCollectionAttribute;
}
else if (propAttr is YAXDictionaryAttribute)
{
dictionaryAttrInst = propAttr as YAXDictionaryAttribute;
}
else if (propAttr is YAXErrorIfMissedAttribute)
{
YAXErrorIfMissedAttribute theErrAttr = (propAttr as YAXErrorIfMissedAttribute);
exType = theErrAttr.TreatAs;
exDefaultValue = theErrAttr.DefaultValue;
}
else if (propAttr is YAXNotCollectionAttribute)
{
isNotCollection = true;
}
}
if (isNotCollection)
isCollection = isDictionary = false;
if (!dontSerialize)
{
#region Trying to assign element value in proper objects to be retrieved later
if (!baseElement.HasAttributes && !baseElement.HasElements && baseElement.Value == "")
{
if (isCollection)
return o; //exDefaultValue;
else
{
return IsNotAllowdNullObjectSerialization() ? o : null;
}
}
//if (!baseElement.HasAttributes && !baseElement.HasElements && baseElement.Value == "" && m_type != typeof(string))
// return null; //exDefaultValue;
if (isClassAttr)
{
XAttribute tattr = baseElement.Attribute(propName);
if (tattr == null)
{
OnExceptionOccurred(new YAXAttributeMissingException(propName), exType);
exOccurred = true;
}
else
{
elemValue = tattr.Value;
}
}
else
{
if (attributeFor || elementFor)
{
XElement parElem = baseElement.Element(parentElement);
if (parElem == null)
{
OnExceptionOccurred(new YAXElementMissingException(parentElement), m_defaultExceptionType);
exOccurred = true;
}
if (attributeFor)
{
XAttribute tAttr = parElem.Attribute(propName);
if (tAttr != null)
{
elemValue = tAttr.Value;
}
else
{
OnExceptionOccurred(new YAXAttributeMissingException(propName), exType);
exOccurred = true;
}
}
else if (elementFor)
{
tElem = parElem.Element(propName);
if (tElem != null)
{
elemValue = tElem.Value;
}
else
{
OnExceptionOccurred(new YAXElementMissingException(propName), exType);
exOccurred = true;
}
}
}
else
{
tElem = baseElement.Element(propName);
if (tElem != null)
{
elemValue = tElem.Value;
}
else
{
OnExceptionOccurred(new YAXElementMissingException(propName), exType);
exOccurred = true;
}
}
}
#endregion
#region Now try to retrieve elemValue's value
// also note that tElem refers to the XElement instance containing our desired data
if (exOccurred) // if some error/exception has occurred
{
if (exDefaultValue != null)
{
try
{
prop.SetValue(o, exDefaultValue, null);
}
catch
{
OnExceptionOccurred(new YAXDefaultValueCannotBeAssigned(propName, exDefaultValue), m_defaultExceptionType);
exOccurred = true;
}
}
else
{
if (!prop.PropertyType.IsValueType)
{
prop.SetValue(o, null /*the value to be assigned */, null);
}
}
}
else if (elemValue != null)
{
Type pType = prop.PropertyType;
if (pType == typeof(string))
{
try
{
prop.SetValue(o, elemValue, null);
}
catch
{
OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
exOccurred = true;
}
}
else
{
if (IsBasicType(pType))
{
try
{
convertedObj = ConvertBasicType(elemValue, pType);
try
{
prop.SetValue(o, convertedObj, null);
}
catch
{
OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
exOccurred = true;
}
}
catch (Exception ex)
{
if (ex is YAXException)
throw;
OnExceptionOccurred(new YAXBadlyFormedInput(propName, elemValue), exType);
exOccurred = true;
try
{
prop.SetValue(o, exDefaultValue, null);
}
catch
{
OnExceptionOccurred(new YAXDefaultValueCannotBeAssigned(propName, exDefaultValue), m_defaultExceptionType);
exOccurred = true;
}
}
}
else // p's type is not even primitive
{
if (isDictionary && dictionaryAttrInst != null) // otherwise the "else if(isCollection)" block solves the problem
{
Type keyType, valueType;
if (!IsDictionary(pType, out keyType, out valueType))
throw new Exception("elemValue must be a Dictionary");
Type pairType = null;
IsEnumerable(pType, out pairType);
string eachElementName = GetTypeFriendlyName(pairType);
bool isKeyAttrib = false;
bool isValueAttrib = false;
string keyAlias = "Key";
string valueAlias = "Value";
if (collectionAttrInst != null)
{
eachElementName = collectionAttrInst.EachElementName ?? eachElementName;
}
if (dictionaryAttrInst != null)
{
eachElementName = dictionaryAttrInst.EachPairName ?? eachElementName;
if (dictionaryAttrInst.SerializeKeyAs == YAXNodeTypes.Attribute)
isKeyAttrib = IsBasicType(keyType);
if (dictionaryAttrInst.SerializeValueAs == YAXNodeTypes.Attribute)
isValueAttrib = IsBasicType(valueType);
keyAlias = dictionaryAttrInst.KeyName ?? keyAlias;
valueAlias = dictionaryAttrInst.ValueName ?? valueAlias;
}
object dic = prop.PropertyType.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, null, new object[0]);
object key = null, value = null;
YAXSerializer keySer = null, valueSer = null;
foreach (XElement childElem in tElem.Elements(eachElementName))
{
if (isKeyAttrib)
key = ConvertBasicType(childElem.Attribute(keyAlias).Value, keyType);
else if (IsBasicType(keyType))
key = ConvertBasicType(childElem.Element(keyAlias).Value, keyType);
else
{
if(keySer == null)
keySer = new YAXSerializer(keyType, m_exPolicy, m_defaultExceptionType, m_serializationOption);
key = keySer.DeserializeBase(childElem.Element(keyAlias));
m_parsingErrors.AddRange(keySer.ParsingErrors);
}
if(isValueAttrib)
value = ConvertBasicType(childElem.Attribute(valueAlias).Value, valueType);
else if (IsBasicType(valueType))
value = ConvertBasicType(childElem.Element(valueAlias).Value, valueType);
else
{
if (valueSer == null)
valueSer = new YAXSerializer(valueType, m_exPolicy, m_defaultExceptionType, m_serializationOption);
value = valueSer.DeserializeBase(childElem.Element(valueAlias));
m_parsingErrors.AddRange(valueSer.ParsingErrors);
}
try
{
prop.PropertyType.InvokeMember("Add", BindingFlags.InvokeMethod, null, dic, new object[] { key, value });
}
catch
{
OnExceptionOccurred(new YAXCannotAddObjectToCollection(propName, new KeyValuePair<object, object>( key, value )),
m_defaultExceptionType);
exOccurred = true;
}
}
try
{
prop.SetValue(o, dic, null);
}
catch
{
OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
exOccurred = true;
}
}
else if (isCollection)
{
List<object> lst = new List<object>(); // this will hold the actual data items
Type itemType = GetCollectionItemType(prop.PropertyType);
if (collectionAttrInst != null && collectionAttrInst.SerializationType == YAXCollectionSerializationTypes.Serially)
{
#region what if the collection was serialized serially
char[] seps = collectionAttrInst.SeparateBy.ToCharArray();
// can white space characters be added to the separators?
if (collectionAttrInst == null || collectionAttrInst.IsWhiteSpaceSeparator)
seps = seps.Union(new char[] { ' ', '\t', '\r', '\n' }).ToArray();
string[] items = elemValue.Split(seps, StringSplitOptions.RemoveEmptyEntries);
foreach (string wordItem in items)
{
try
{
lst.Add(ConvertBasicType(wordItem, itemType));
}
catch
{
OnExceptionOccurred(new YAXBadlyFormedInput(propName, elemValue), m_defaultExceptionType);
exOccurred = true;
}
}
#endregion
}
else
{
#region what if the collection was serialized recursive
bool isPrimitive = false;
YAXSerializer ser = null;
if (IsBasicType(itemType))
{
isPrimitive = true;
}
else
{
ser = new YAXSerializer(itemType, m_exPolicy, m_defaultExceptionType, m_serializationOption);
}
string eachElemName = GetTypeFriendlyName(itemType);
if (collectionAttrInst != null && collectionAttrInst.EachElementName != null)
eachElemName = collectionAttrInst.EachElementName;
foreach (XElement childElem in tElem.Elements(eachElemName))
{
if (isPrimitive)
{
try
{
lst.Add(ConvertBasicType(childElem.Value, itemType));
}
catch
{
OnExceptionOccurred(new YAXBadlyFormedInput(childElem.Name.ToString(), childElem.Value), m_defaultExceptionType);
exOccurred = true;
}
}
else
{
lst.Add(ser.DeserializeBase(childElem));
}
}
if (ser != null)
m_parsingErrors.AddRange(ser.ParsingErrors);
#endregion
} // end of else if (!isCollection)
// Now what should I do with the filled list: lst
Type dicKeyType, dicValueType, enumType;
Type auxColTypeToCreate;
Type colTypeToCreate;
Type[] typeArgs;
if (IsArray(prop.PropertyType))
{
#region The collection is array
auxColTypeToCreate = typeof(List<>);
typeArgs = new Type[] { itemType };
colTypeToCreate = auxColTypeToCreate.MakeGenericType(typeArgs);
object genArObj = Activator.CreateInstance(colTypeToCreate);
foreach (object lstItem in lst)
{
try
{
colTypeToCreate.InvokeMember("Add", BindingFlags.InvokeMethod, null, genArObj, new object[] { lstItem });
}
catch
{
OnExceptionOccurred(new YAXCannotAddObjectToCollection(propName, lstItem), m_defaultExceptionType);
exOccurred = true;
}
}
try
{
prop.SetValue(o,
colTypeToCreate.InvokeMember("ToArray", BindingFlags.InvokeMethod, null, genArObj, new object[0]),
null);
}
catch
{
OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
exOccurred = true;
}
#endregion
}
else if (IsDictionary(prop.PropertyType, out dicKeyType, out dicValueType))
{
#region The collection is a Dictionary
object dic = prop.PropertyType.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, null, new object[0]);
object key, value;
foreach (object lstItem in lst)
{
key = itemType.GetProperty("Key").GetValue(lstItem, null);
value = itemType.GetProperty("Value").GetValue(lstItem, null);
try
{
prop.PropertyType.InvokeMember("Add", BindingFlags.InvokeMethod, null, dic, new object[] { key, value });
}
catch
{
OnExceptionOccurred(new YAXCannotAddObjectToCollection(propName, lstItem), m_defaultExceptionType);
exOccurred = true;
}
}
try
{
prop.SetValue(o, dic, null);
}
catch
{
OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
exOccurred = true;
}
#endregion
}
else if (IsICollection(prop.PropertyType, out enumType))
{
#region The collection is an ICollection e.g. HashSet
object col = prop.PropertyType.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, null, new object[0]);
foreach (object lstItem in lst)
{
try
{
prop.PropertyType.InvokeMember("Add", BindingFlags.InvokeMethod, null, col, new object[] { lstItem });
}
catch
{
OnExceptionOccurred(new YAXCannotAddObjectToCollection(propName, lstItem), m_defaultExceptionType);
exOccurred = true;
}
}
try
{
prop.SetValue(o, col, null);
}
catch
{
OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
exOccurred = true;
}
#endregion
}
else if (IsEnumerable(prop.PropertyType, out enumType)) // this MUST always be the last "else if"
{
#region The collection is an IEnumerable
auxColTypeToCreate = typeof(List<>);
typeArgs = new Type[] { enumType };
colTypeToCreate = auxColTypeToCreate.MakeGenericType(typeArgs);
object genArObj = Activator.CreateInstance(colTypeToCreate);
foreach (object lstItem in lst)
{
try
{
colTypeToCreate.InvokeMember("Add", BindingFlags.InvokeMethod, null, genArObj, new object[] { lstItem });
}
catch
{
OnExceptionOccurred(new YAXCannotAddObjectToCollection(propName, lstItem), m_defaultExceptionType);
exOccurred = true;
}
}
try
{
prop.SetValue(o, genArObj, null);
}
catch
{
OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
exOccurred = true;
}
#endregion
}
} // end of if (isCollection)
else
{
YAXSerializer ser = new YAXSerializer(prop.PropertyType, m_exPolicy, m_defaultExceptionType, m_serializationOption);
convertedObj = ser.DeserializeBase(tElem);
m_parsingErrors.AddRange(ser.ParsingErrors);
try
{
prop.SetValue(o, convertedObj, null);
}
catch
{
OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
exOccurred = true;
}
} // end of else if(isCollection)
} // end of else if prop is not primitive
} // end of else if prop type is not string
} // end of else if no exception occured
#endregion
} // end if(!dontSerialize)
} // end if(prop.CanWrite)
} // end of foreach property loop
return o;
}
#endregion
}
}