Click here to Skip to main content
15,880,956 members
Articles / Programming Languages / XML

Yet Another XML Serialization Library for the .NET Framework

Rate me:
Please Sign up or sign in to vote.
4.92/5 (91 votes)
2 Oct 2012MIT24 min read 508K   269   207  
A flexible XML serialization library that lets developers design the XML file structure, and select the exception handling policy. YAXLib supports polymorphic serialization and serializing generic and non-generic collection classes and arrays.
// Copyright 2009 - 2010 Sina Iravanian - <sina@sinairv.com>
//
// This source file(s) may be redistributed, altered and customized
// by any means PROVIDING the authors name and all copyright
// notices remain intact.
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO
// LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE.
//-----------------------------------------------------------------------

using System;
using System.Reflection;

namespace YAXLib
{
    /// <summary>
    /// A wrapper class for members which only can be properties or member variables
    /// </summary>
    internal class MemberWrapper
    {
		#region Fields 

        /// <summary>
        /// reference to the underlying <c>MemberInfo</c> from which this instance is built
        /// </summary>
        private MemberInfo m_memberInfo = null;

        /// <summary>
        /// the member type of the underlying member
        /// </summary>
        private Type m_memberType;

        /// <summary>
        /// a type wrapper around the underlying member type
        /// </summary>
        private UdtWrapper m_memberTypeWrapper = null;

        /// <summary>
        /// the <c>PropertyInfo</c> instance, if this member corrsponds to a property, <c>null</c> otherwise
        /// </summary>
        private PropertyInfo m_propertyInfoInstance = null;

        /// <summary>
        /// the <c>FieldInfo</c> instance, if this member corrsponds to a field, <c>null</c> otherwise
        /// </summary>
        private FieldInfo m_fieldInfoInstance = null;

        /// <summary>
        /// The collection attribute instance
        /// </summary>
        private YAXCollectionAttribute m_collectionAttributeInstance = null;

        /// <summary>
        /// the dictionary attribute instance
        /// </summary>
        private YAXDictionaryAttribute m_dictionaryAttributeInstance = null;

        /// <summary>
        /// <c>true</c> if this instance corresponds to a property, <c>false</c> 
        /// if it corrsponds to a field (i.e., a member variable)
        /// </summary>
        private bool m_isProperty = false;
        /// <summary>
        /// The location of the serialization
        /// </summary>
        private string m_serializationLocation = "";

        /// <summary>
        /// The alias specified by the user
        /// </summary>
        private string m_alias = "";

		#endregion Fields

		#region Constructors 

        /// <summary>
        /// Initializes a new instance of the <see cref="MemberWrapper"/> class.
        /// </summary>
        /// <param name="memberInfo">The member-info to build this instance from.</param>
        /// <param name="callerSerializer">The caller serializer.</param>
        public MemberWrapper(MemberInfo memberInfo, YAXSerializer callerSerializer)
        {
            if (!(memberInfo.MemberType == MemberTypes.Property || memberInfo.MemberType == MemberTypes.Field))
                throw new Exception("Member must be either property or field");

            m_memberInfo = memberInfo;
            m_isProperty = (memberInfo.MemberType == MemberTypes.Property);

            this.Alias = m_memberInfo.Name;

            if (m_isProperty)
                m_propertyInfoInstance = (PropertyInfo)memberInfo;
            else
                m_fieldInfoInstance = (FieldInfo)memberInfo;

            if (m_isProperty)
                m_memberType = m_propertyInfoInstance.PropertyType;
            else
                m_memberType = m_fieldInfoInstance.FieldType;

            m_memberTypeWrapper = TypeWrappersPool.Pool.GetTypeWrapper(this.MemberType, callerSerializer);

            InitInstance();

            if (callerSerializer != null)
            {
                this.TreatErrorsAs = callerSerializer.DefaultExceptionType;
            }
            else
            {
                this.TreatErrorsAs = YAXExceptionTypes.Error;
            }

            foreach (var attr in m_memberInfo.GetCustomAttributes(false))
            {
                if (attr is YAXBaseAttribute)
                    ProcessYAXAttribute(attr);
            }
        }

		#endregion Constructors 

		#region Properties 

        /// <summary>
        /// Gets the alias specified for this member.
        /// </summary>
        /// <value>The alias specified for this member.</value>
        public string Alias 
        {
            get
            {
                return m_alias;
            }

            private set
            {
                m_alias = StringUtils.RefineSingleElement(value);
            }
        }

        /// <summary>
        /// Gets a value indicating whether the member corrsponding to this instance can be read from.
        /// </summary>
        /// <value><c>true</c> if the member corrsponding to this instance can be read from; otherwise, <c>false</c>.</value>
        public bool CanRead
        {
            get
            {
                if (m_isProperty)
                {
                    return m_propertyInfoInstance.CanRead;
                }
                else
                {
                    return true;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether the member corrsponding to this instance can be written to.
        /// </summary>
        /// <value><c>true</c> if the member corrsponding to this instance can be written to; otherwise, <c>false</c>.</value>
        public bool CanWrite
        {
            get
            {
                if (m_isProperty)
                {
                    return m_propertyInfoInstance.CanWrite;
                }
                else
                {
                    return true;
                }
            }
        }

        /// <summary>
        /// Gets an array of comment lines.
        /// </summary>
        /// <value>The comment lines.</value>
        public string[] Comment { get; private set; }

        /// <summary>
        /// Gets the default value for this instance.
        /// </summary>
        /// <value>The default value for this instance.</value>
        public object DefaultValue { get; private set; }

        /// <summary>
        /// Gets the format specified for this value; <c>null</c> if no format is specified.
        /// </summary>
        /// <value>the format specified for this value; <c>null</c> if no format is specified.</value>
        public string Format { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this instance has comments.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has comments; otherwise, <c>false</c>.
        /// </value>
        public bool HasComment
        {
            get
            {
                return Comment != null && Comment.Length > 0;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance has format values specified
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance has format values specified; otherwise, <c>false</c>.
        /// </value>
        public bool HasFormat
        {
            get
            {
                // empty string may be considered as a valid format
                return this.Format != null;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is attributed as dont serialize.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is attributed as dont serialize; otherwise, <c>false</c>.
        /// </value>
        public bool IsAttributedAsDontSerialize { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this instance is attributed as not-collection.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is attributed as not-collection; otherwise, <c>false</c>.
        /// </value>
        public bool IsAttributedAsNotCollection { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this instance is attributed as serializable.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is attributed as serializable; otherwise, <c>false</c>.
        /// </value>
        public bool IsAttributedAsSerializable { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this instance is serialized as an XML attribute.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is serialized as an XML attribute; otherwise, <c>false</c>.
        /// </value>
        public bool IsSerializedAsAttribute { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this instance is serialized as an XML element.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is serialized as an XML element; otherwise, <c>false</c>.
        /// </value>
        public bool IsSerializedAsElement
        {
            get
            {
                return !this.IsSerializedAsAttribute;
            }
        }

        /// <summary>
        /// Gets the type of the member.
        /// </summary>
        /// <value>The type of the member.</value>
        public Type MemberType
        {
            get
            {
                return m_memberType;
            }
        }

        /// <summary>
        /// Gets the type wrapper instance corrsponding to the member-type of this instance.
        /// </summary>
        /// <value>The type wrapper instance corrsponding to the member-type of this instance.</value>
        public UdtWrapper MemberTypeWrapper
        {
            get
            {
                return m_memberTypeWrapper;
            }
        }

        /// <summary>
        /// Gets the original of this member (as opposed to its alias).
        /// </summary>
        /// <value>The original of this member .</value>
        public string OriginalName
        {
            get
            {
                return this.m_memberInfo.Name;
            }
        }

        /// <summary>
        /// Gets the serialization location.
        /// </summary>
        /// <value>The serialization location.</value>
        public string SerializationLocation 
        {
            get
            {
                return m_serializationLocation;
            }

            private set
            {
                m_serializationLocation = StringUtils.RefineLocationString(value);
            }
        }

        /// <summary>
        /// Gets the exception type for this instance in case of encountering missing values
        /// </summary>
        /// <value>The exception type for this instance in case of encountering missing values</value>
        public YAXExceptionTypes TreatErrorsAs { get; private set; }

        /// <summary>
        /// Gets the collection attribute instance.
        /// </summary>
        /// <value>The collection attribute instance.</value>
        public YAXCollectionAttribute CollectionAttributeInstance
        {
            get
            {
                return m_collectionAttributeInstance;
            }
        }

        /// <summary>
        /// Gets the dictionary attribute instance.
        /// </summary>
        /// <value>The dictionary attribute instance.</value>
        public YAXDictionaryAttribute DictionaryAttributeInstance
        {
            get
            {
                return m_dictionaryAttributeInstance;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is treated as a collection.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is treated as a collection; otherwise, <c>false</c>.
        /// </value>
        public bool IsTreatedAsCollection
        {
            get
            {
                return !IsAttributedAsNotCollection && m_memberTypeWrapper.IsTreatedAsCollection;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is treated as a dictionary.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is treated as a dictionary; otherwise, <c>false</c>.
        /// </value>
        public bool IsTreatedAsDictionary
        {
            get
            {
                return !IsAttributedAsNotCollection && m_memberTypeWrapper.IsTreatedAsDictionary;
            }
        }

		#endregion Properties 

		#region Methods 

		// Public Methods

        /// <summary>
        /// Gets the original value of this member in the specified object
        /// </summary>
        /// <param name="obj">The object whose value corresponding to this instance, must be retreived.</param>
        /// <param name="index">The array of indeces (usually <c>null</c>).</param>
        /// <returns>the original value of this member in the specified object</returns>
        public object GetOriginalValue(object obj, object[] index)
        {
            if(m_isProperty)
            {
                return m_propertyInfoInstance.GetValue(obj, index);
            }
            else
            {
                return m_fieldInfoInstance.GetValue(obj);
            }
        }

        /// <summary>
        /// Gets the processed value of this member in the specified object
        /// </summary>
        /// <param name="obj">The object whose value corresponding to this instance, must be retreived.</param>
        /// <returns>the processed value of this member in the specified object</returns>
        public object GetValue(object obj)
        {
            object elementValue = GetOriginalValue(obj, null);

            if (elementValue == null)
                return null;

            if (m_memberTypeWrapper.IsEnum)
            {
                return m_memberTypeWrapper.EnumWrapper.GetAlias(elementValue);
            }

            // trying to build the element value
            if (this.HasFormat && !this.IsTreatedAsCollection)
            {
                // do the formatting. If formatting succeeds the type of 
                // the elementValue will become 'System.String'
                elementValue = ReflectionUtils.TryFormatObject(elementValue, this.Format);
            }

            return elementValue;
        }

        /// <summary>
        /// Sets the value of this member in the specified object
        /// </summary>
        /// <param name="obj">The object whose member corresponding to this instance, must be given value.</param>
        /// <param name="value">The value.</param>
        public void SetValue(object obj, object value)
        {
            if(m_isProperty)
            {
                m_propertyInfoInstance.SetValue(obj, value, null);
            }
            else
            {
                m_fieldInfoInstance.SetValue(obj, value);
            }
        }

        /// <summary>
        /// Determines whether this instance of <c>MemberWrapper</c> can be serialized.
        /// </summary>
        /// <param name="serializationFields">The serialization fields.</param>
        /// <returns>
        /// <c>true</c> if this instance of <c>MemberWrapper</c> can be serialized; otherwise, <c>false</c>.
        /// </returns>
        public bool IsAllowedToBeSerialized(YAXSerializationFields serializationFields)
        {
            if (serializationFields == YAXSerializationFields.AllFields)
                return !this.IsAttributedAsDontSerialize;
            else if (serializationFields == YAXSerializationFields.AttributedFieldsOnly)
                return !this.IsAttributedAsDontSerialize && this.IsAttributedAsSerializable;
            else if (serializationFields == YAXSerializationFields.PublicPropertiesOnly)
                return !this.IsAttributedAsDontSerialize && m_isProperty && ReflectionUtils.IsPublicProperty(m_propertyInfoInstance);
            else
                throw new Exception("Unknown serialization field option");
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            return m_memberInfo.ToString();
        }

		// Private Methods 

        /// <summary>
        /// Initializes this instance of <c>MemberWrapper</c>.
        /// </summary>
        private void InitInstance()
        {
            this.Comment = null;
            this.IsAttributedAsSerializable = false;
            this.IsAttributedAsDontSerialize = false;
            this.IsAttributedAsNotCollection = false;
            this.IsSerializedAsAttribute = false;
            this.SerializationLocation = ".";
            this.Format = null;
            InitDefaultValue();
        }

        /// <summary>
        /// Initializes the default value for this instance of <c>MemberWrapper</c>.
        /// </summary>
        private void InitDefaultValue()
        {
            if(this.MemberType.IsValueType)
                this.DefaultValue = this.MemberType.InvokeMember(string.Empty, System.Reflection.BindingFlags.CreateInstance, null, null, new object[0]);
            else
                this.DefaultValue = null;
        }

        /// <summary>
        /// Processes the specified attribute which is an instance of <c>YAXAttribute</c>.
        /// </summary>
        /// <param name="attr">The attribute to process.</param>
        private void ProcessYAXAttribute(object attr)
        {
            if (attr is YAXCommentAttribute) 
            {
                string comment = (attr as YAXCommentAttribute).Comment;
                if (!String.IsNullOrEmpty(comment))
                {
                    string[] comments = comment.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
                    for (int i = 0; i < comments.Length; i++)
                    {
                        comments[i] = String.Format(" {0} ", comments[i].Trim());
                    }

                    this.Comment = comments;
                }
            }
            else if (attr is YAXSerializableFieldAttribute) 
            {
                this.IsAttributedAsSerializable = true;
            }
            else if (attr is YAXAttributeForClassAttribute) 
            { 
                if(ReflectionUtils.IsBasicType(this.MemberType))
                {
                    this.IsSerializedAsAttribute = true;
                    this.SerializationLocation = ".";
                }
            }
            else if (attr is YAXAttributeForAttribute) 
            { 
                if(ReflectionUtils.IsBasicType(this.MemberType))
                {
                    this.IsSerializedAsAttribute = true;
                    this.SerializationLocation = (attr as YAXAttributeForAttribute).Parent;
                }
            }
            else if (attr is YAXDontSerializeAttribute) 
            {
                this.IsAttributedAsDontSerialize = true;
            }
            else if (attr is YAXSerializeAsAttribute) 
            {
                this.Alias = (attr as YAXSerializeAsAttribute).SerializeAs;
            }
            else if (attr is YAXElementForAttribute) 
            {
                this.IsSerializedAsAttribute = false;
                this.SerializationLocation = (attr as YAXElementForAttribute).Parent;
            }
            else if (attr is YAXCollectionAttribute) 
            {
                m_collectionAttributeInstance = attr as YAXCollectionAttribute;
            }
            else if (attr is YAXDictionaryAttribute) 
            { 
                m_dictionaryAttributeInstance = attr as YAXDictionaryAttribute;
            }
            else if (attr is YAXErrorIfMissedAttribute) 
            {
                YAXErrorIfMissedAttribute temp = attr as YAXErrorIfMissedAttribute;
                this.DefaultValue = temp.DefaultValue;
                this.TreatErrorsAs = temp.TreatAs;
            }
            else if (attr is YAXFormatAttribute) 
            { 
                this.Format = (attr as YAXFormatAttribute).Format;
            }
            else if (attr is YAXNotCollectionAttribute) 
            {
                // arrays are always treated as collections
                if(!ReflectionUtils.IsArray(this.MemberType))
                    this.IsAttributedAsNotCollection = true;
            }
            else if (attr is YAXSerializableTypeAttribute)
            {
                // this should not happen
                throw new Exception("This attribute is not applicable to fields and properties!");
            }
            else
            {
                throw new Exception("Added new attribute type to the library but not yet processed!");
            }
        }

		#endregion Methods 
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer
Australia Australia
A software designer and developer

Comments and Discussions