예제 #1
0
        /// <summary>
        /// Sets the value of field referred to by specified mapping, inside specified
        /// instance with specified type
        /// </summary>
        /// <param name="desc">FieldMapping object representing the field</param>
        /// <param name="fieldValue">Value to set</param>
        /// <param name="contType">Type of containing object</param>
        /// <param name="contInst">Containing object</param>
        /// <returns></returns>
        public static bool SetFieldValue(FieldMapping desc, Object fieldValue, Type contType, Object contInst)
        {
            bool written = false;

            if (null == desc.ValueSetter) // We haven't figured this out before?
            {
                PropertyInfo pi = null;
                MethodInfo   mi = null;

                //
                // If the field is public, then that's what we'll use to set the value!
                //
                if (desc.FieldInfo.IsPublic)
                {
                    desc.ValueSetter  = desc.FieldInfo;
                    desc.ValueSetType = desc.FieldInfo.FieldType;
                }
                else
                {
                    String pascalName = Helpers.ToPascalCase(desc.ObjBindingName);

                    //
                    // Now we look for a write-access property with a pascal case version
                    // of the field name (e.g. Documentation). //! What about overloads?
                    //
                    if (null != (pi = contType.GetProperty(pascalName)) && pi.CanWrite)
                    {
                        desc.ValueSetter  = pi;
                        desc.ValueSetType = pi.PropertyType;
                    }
                    else if (null != (mi = contType.GetMethod("set" + pascalName,
                                                              new Type[] { desc.FieldInfo.FieldType })))
                    {
                        desc.ValueSetter  = mi;
                        desc.ValueSetType = desc.FieldInfo.FieldType;
                    }
                    else
                    {
                        Console.WriteLine(String.Format("Cannot find {0} write property in {1}",
                                                        pascalName, contInst.GetType()));
                    }
                }
            }

            if (null == desc.ValueSetter)
            {
                throw new Exception(String.Format("Unable to find a way to populate value of {0} in {1}",
                                                  desc.XmlName, null != contInst ? contInst.ToString() : "null"));
            }

            written = InvokeSet(desc.ValueSetter, fieldValue, desc.ValueSetType, contInst);
            return(written);
        }
예제 #2
0
        /// <summary>
        /// Processes a dictionary object, by writing all its key based elements
        /// </summary>
        /// <typeparam name="TA">Type of key within the dictionary</typeparam>
        /// <typeparam name="TB">Type of value within the dictionary</typeparam>
        /// <param name="dict">Dictionary object</param>
        /// <param name="dictMapping">Field mapping describing the dictionary itself within its containing object</param>
        /// <param name="writer">XmlWriter object</param>
        private void ProcessDictElements <TA, TB>(IDictionary <TA, TB> dict, FieldMapping dictMapping, XmlWriter writer)
        {
            foreach (TA key in dict.Keys)
            {
                TB entry = dict[key];

                writer.WriteStartElement(dictMapping.XmlName); // Entry tag

                String mapKeyValue = key.ToString();
                writer.WriteAttributeString(dictMapping.KeyName, mapKeyValue); // Write key attribute

                Write(entry, null, writer, dictMapping.MapEntryXmlName);       // Write the entry

                writer.WriteEndElement();                                      // Close entry tag
            }
        }
예제 #3
0
        /// <summary>
        /// Writes out those fields of specified object that are mapped to XML attributes
        /// </summary>
        /// <param name="obj">Object to write its attributes</param>
        /// <param name="em">Element mapping of the object</param>
        /// <param name="writer">XmlWriter object</param>
        private void WriteAttributes(Object obj, ElementMapping em, XmlWriter writer)
        {
            Type objType = obj.GetType();

            for (int i = 0, length = null != em.Attributes ? em.Attributes.Count : 0; i < length; i++)
            {
                FieldMapping fm    = em.Attributes[i];
                Object       value = Helpers.GetFieldValue(fm, objType, obj, typeof(String));

                if (null != value)
                {
                    String strValue = value.ToString();
                    writer.WriteAttributeString(fm.XmlName, strValue);
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Returns an element mapping object representing the specified type
        /// </summary>
        /// <param name="t">Type to return its element mapping</param>
        /// <returns>Element mapping object if found, otherwise null</returns>
        public ElementMapping GetElementMapping(Type t)
        {
            ElementMapping res = null;

            String cacheKey = t.FullName;

            if (!elemCache.TryGetValue(cacheKey, out res))
            {
                ElementMapping em = new ElementMapping();

                em.XmlName = Helpers.ExtractElementName(t);

                FieldInfo[] fis = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

                for (int i = 0; i < fis.Length; i++)
                {
                    FieldInfo fi = fis[i];

                    FieldMapping desc = GetFieldMapping(fi, fi.Name);
                    if (null == desc)
                    {
                        continue;
                    }

                    switch (desc.NodeType)
                    {
                    case NodeType.Attribute:
                        em.AddAttribute(desc);

                        break;

                    default:
                        em.AddSubElement(desc);
                        break;
                    }
                }

                elemCache.Add(cacheKey, em);
                res = em;
            }

            return(res);
        }
예제 #5
0
        /// <summary>
        /// Populates those field of the specified object that are mapped to XML attributes
        /// </summary>
        /// <param name="t">Type of object</param>
        /// <param name="inst">Object to populate</param>
        /// <param name="reader">XmlReader to read from</param>
        private void PopulateAttributes(Type t, Object inst, XmlReader reader)
        {
            int i = -1;

            for (i = 0; i < reader.AttributeCount; i++)
            {
                reader.MoveToAttribute(i);

                FieldMapping desc = ots.LookupField(t, reader.Name);
                if (null != desc)
                {
                    Helpers.SetFieldValue(desc, reader.Value, t, inst);
                }
            }

            if (i >= 0)
            {
                reader.MoveToElement();
            }
        }
예제 #6
0
        /// <summary>
        /// Looks up and returns a field mapping object given the containing type and the
        /// XML tag/attribute name within the scope of the containing object
        /// </summary>
        /// <param name="t">The type of the containing object</param>
        /// <param name="xmlName">The XML tag/attribute name within the scope of the
        /// containing object</param>
        /// <returns>FieldMapping object if found, otherwise null</returns>
        public FieldMapping LookupField(Type t, String xmlName)
        {
            FieldMapping res = null;

            String cacheKey = t.FullName + "_" + xmlName;

            if (fieldMappingCache.TryGetValue(cacheKey, out res))
            {
                return(res);
            }

            if (null == res)
            {
                FieldInfo[] fis = null;

                if (!fieldsCache.TryGetValue(t.FullName, out fis))
                {
                    fis = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                    fieldsCache.Add(t.FullName, fis);
                }

                for (int i = 0; i < fis.Length; i++)
                {
                    FieldInfo fi = fis[i];

                    FieldMapping desc = GetFieldMapping(fi, xmlName);
                    if (null == desc)
                    {
                        continue;
                    }

                    if (String.Equals(xmlName, desc.XmlName))
                    {
                        res = desc;
                        break;
                    }
                }
            }

            return(res);
        }
예제 #7
0
        /// <summary>
        /// Writes out the specified object. This method writes the element for the object, and
        /// recursively writes out all attributes and sub elements.
        /// </summary>
        /// <param name="obj">Object to write</param>
        /// <param name="fm">Field mapping of the object within its containing object (optional).
        /// When provided, the logic will use it to determine whether element is to be "inline"'d,
        /// in which case the logic will not write an outer tag for it. Also the XmlName field of the
        /// mapping will take precedence in determining the tag name over the name mentioned in
        /// the element mapping.
        /// </param>
        /// <param name="writer">XmlWriter object</param>
        /// <param name="tagName">Specifies the tag name to use for the object (optional). This
        /// parameter can be used to override what the tag name that the logic would normally use,
        /// it is mainly to be used when writing collections, since the name of each entry's tag
        /// can be customized at the collection declaration level (e.g. via [ElementMap] or
        /// [ElemenList] attributes). When this parameter is set to null, the logic will get the
        /// name from either the field mapping object (if provided) or from the element mapping object.
        /// </param>
        private void Write(Object obj, FieldMapping fm, XmlWriter writer, String tagName)
        {
            Type objType = obj.GetType();

            ElementMapping em = ots.GetElementMapping(objType);

            if (null == em)
            {
                throw new ArgumentException("Don;t know how to handle this object", objType.ToString());
            }

            String xmlName = tagName; // Allow caller to override tag name

            //
            // If the tag name was not overriden by the caller, then it would be the name
            // specified by the mapping in the elment's container, if not specified then
            // it is the name as specified by the element mapping.
            //
            if (null == xmlName) // Tag name not provided, use the one in field mapping
            {
                xmlName = null != fm ? fm.XmlName : em.XmlName;
            }

            //
            // For nodes that are marked as inline, we don't write a tag for them.
            //
            bool inline = null != fm ? fm.Inline : false;

            if (!inline)
            {
                writer.WriteStartElement(xmlName); // Start of element
                WriteAttributes(obj, em, writer);  // Write attributes
            }

            int i = 0, length = length = null != em.SubElements ? em.SubElements.Count : 0;

            //
            // If the element has no sub-elements, we treat it as a leaf node so we write its
            // body value. Note that we do the XML encoding ourselves because XmlWriter
            // implementation does not encode single or double quotes within the body of a tag,
            // but SimpleXml framework does.
            //
            if (0 == length)
            {
                writer.WriteRaw(Helpers.XmlEncode(obj.ToString()));
            }
            else // Otherwise, drill down the object
            {
                //
                // Write sub elements.
                //
                for (i = 0; i < length; i++)
                {
                    FieldMapping subMapping = em.SubElements[i];
                    Object       value      = null;

                    switch (subMapping.NodeType)
                    {
                    case NodeType.Element:
                        value = Helpers.GetFieldValue(subMapping, objType, obj, subMapping.ValueType);

                        if (null != value)
                        {
                            Write(value, subMapping, writer, null);     // Drill down
                        }
                        break;

                    case NodeType.Map:
                    {
                        //
                        // Note that we assume that map based fields are using an IDictionary
                        // based object, with its key being of type String.
                        //

                        value = Helpers.GetFieldValue(subMapping, objType, obj, subMapping.ValueType);

                        if (null != value)
                        {
                            Type gt = typeof(IDictionary <,>);

                            if (null != value.GetType().GetInterface("IDictionary`2"))
                            {
                                MethodInfo mi = this.GetType().GetMethod("ProcessDictElements",
                                                                         BindingFlags.NonPublic | BindingFlags.Instance);

                                MethodInfo si = mi.MakeGenericMethod(value.GetType().GetGenericArguments());
                                si.Invoke(this, new Object[] { value, subMapping, writer });
                            }
                        }

                        break;
                    }

                    case NodeType.List:
                    {
                        //
                        // Note that we assume that list based fields are using an IList based object.
                        //

                        value = Helpers.GetFieldValue(subMapping, objType, obj, subMapping.ValueType);
                        if (null != value)
                        {
                            Type gt = typeof(IList <>);

                            if (null != value.GetType().GetInterface("IList`1"))
                            {
                                MethodInfo mi = this.GetType().GetMethod("ProcessListElements",
                                                                         BindingFlags.NonPublic | BindingFlags.Instance);

                                MethodInfo si = mi.MakeGenericMethod(value.GetType().GetGenericArguments());
                                si.Invoke(this, new Object[] { value, subMapping, writer });
                            }
                        }

                        break;
                    }
                    }
                }
            }

            if (!inline)
            {
                writer.WriteEndElement(); // End of element
            }
        }
예제 #8
0
        /// <summary>
        /// Returns the value of field referred to be specified mapping, inside specified
        /// instance with specified type
        /// </summary>
        /// <param name="desc">FieldMapping object representing the field</param>
        /// <param name="contType">Type of containing object</param>
        /// <param name="contInst">Contianing object</param>
        /// <param name="valueAsType"></param>
        /// <returns></returns>
        public static Object GetFieldValue(FieldMapping desc, Type contType, Object contInst, Type valueAsType)
        {
            Object res = null;

            if (null == desc.ValueGetter) // We haven't figured this out before?
            {
                PropertyInfo pi = null;
                MethodInfo   mi = null;

                //
                // If the field is public, then that's what we'll use to get the value!
                //
                if (desc.FieldInfo.IsPublic)
                {
                    desc.ValueGetter  = desc.FieldInfo;
                    desc.ValueGetType = desc.FieldInfo.FieldType;
                }
                else
                {
                    String pascalName = Helpers.ToPascalCase(desc.ObjBindingName);

                    //
                    // Now we look for a read-access property with a pascal case version
                    // of the field name (e.g. Documentation). //! What about overloads?
                    //
                    if (null != (pi = contType.GetProperty(pascalName)) && pi.CanRead)
                    {
                        desc.ValueGetter  = pi;
                        desc.ValueGetType = pi.PropertyType;
                    }
                    else if (null != (mi = contType.GetMethod("get" + pascalName)))
                    {
                        desc.ValueGetter  = mi;
                        desc.ValueGetType = desc.FieldInfo.FieldType;
                    }
                    else
                    {
                        Console.WriteLine(String.Format("Cannot find {0} read property in {1}",
                                                        pascalName, contInst.GetType()));
                    }
                }
            }

            if (null == desc.ValueGetter)
            {
                throw new Exception(String.Format("Unable to find a way to read value of {0} in {1}",
                                                  desc.ObjBindingName, null != contInst ? contInst.ToString() : "null"));
            }

            res = InvokeGet(desc.ValueGetter, desc.ValueType, valueAsType, contInst);

            if (null != res && !IsTypeDescendantOf(res, valueAsType))
            {
                if (res is Boolean)
                {
                    res = ((Boolean)res) ? "true" : "false";
                }
                else
                {
                    res = ChangeType(res, valueAsType);
                }
            }

            return(res);
        }
예제 #9
0
        /// <summary>
        /// Reads and returns an object of specified type from specified XmlReader. This
        /// method instantiates an object of specified type, and recursively populates
        /// its fields from attributes and sub elements.
        /// </summary>
        /// <param name="t">Type of object</param>
        /// <param name="r">XmlReader object</param>
        /// <returns>The read object</returns>
        public Object Read(Type t, XmlReader r)
        {
            using (XmlReader reader = r.ReadSubtree())
            {
                reader.Read();

                //
                // We assume reader is positioned on the XML element for the specified type
                //
                Object res = InstantiateObject(t);

                PopulateAttributes(t, res, reader); // Populate from attributes if any

                //
                // This variable is used in the situations when we read the content
                // of a node, in which case the pointer has already been advanced to
                // the next node so there's no need to call Read().
                //
                bool dontAdvance = false;
                while (dontAdvance || reader.Read())
                {
                    dontAdvance = false; // Clear the flag

                    if (reader.NodeType == XmlNodeType.EndElement || reader.NodeType == XmlNodeType.Text)
                    {
                        continue;
                    }

                    FieldMapping desc = ots.LookupField(t, reader.Name);
                    if (null == desc)
                    {
                        //Console.WriteLine(string.Format("{0} skipped. within type: {1}", reader.Name, t.ToString()));

                        reader.Skip();
                        continue; // Skip ones we don't care for
                    }

                    Object fieldValue = null;

                    switch (desc.NodeType)
                    {
                    case NodeType.Element:

                        if (Helpers.IsSerializable(desc.FieldInfo.FieldType))
                        {
                            fieldValue = Read(desc.FieldInfo.FieldType, reader);
                        }
                        else
                        {
                            fieldValue  = reader.ReadElementContentAsString();
                            dontAdvance = true;
                        }

                        Helpers.SetFieldValue(desc, fieldValue, t, res);
                        break;

                    case NodeType.Map:

                        //
                        // Ensure we have a dictionary instance.
                        //
                        Object dict = desc.FieldInfo.GetValue(res);
                        if (null == dict)
                        {
                            Type   t1   = typeof(Dictionary <,>);
                            Type[] args = { desc.MapEntryKeyType, desc.MapEntryValueType };

                            Type t2 = t1.MakeGenericType(args);

                            dict = Activator.CreateInstance(t2);

                            Helpers.SetFieldValue(desc.ObjBindingName, dict, t, res);
                        }

                        String key = reader.GetAttribute(desc.KeyName);     // Get the key

                        //
                        // Get the entry value
                        //
                        fieldValue = null;

                        if (reader.ReadToDescendant(desc.MapEntryXmlName))
                        {
                            if (Helpers.IsSerializable(desc.MapEntryValueType))
                            {
                                fieldValue = Read(desc.MapEntryValueType, reader);
                            }
                            else
                            {
                                fieldValue  = reader.ReadElementContentAsString();
                                dontAdvance = true;
                            }

                            if (null != fieldValue)
                            {
                                MethodInfo mi = desc.ValueType.GetMethod("Add");
                                mi.Invoke(dict, new Object[] { key, fieldValue });
                            }
                        }

                        break;

                    case NodeType.List:

                        //
                        // Ensure we have a dictionary instance.
                        //
                        Object list = desc.FieldInfo.GetValue(res);
                        if (null == list)
                        {
                            Type   t1   = typeof(List <>);
                            Type[] args = { desc.MapEntryValueType };

                            Type t2 = t1.MakeGenericType(args);

                            list = Activator.CreateInstance(t2);

                            Helpers.SetFieldValue(desc.ObjBindingName, list, t, res);
                        }

                        //
                        // Get the entry value
                        //
                        fieldValue = null;

                        if (Helpers.IsSerializable(desc.MapEntryValueType))
                        {
                            fieldValue = Read(desc.MapEntryValueType, reader);
                        }
                        else
                        {
                            fieldValue  = reader.ReadElementContentAsString();
                            dontAdvance = true;
                        }

                        if (null != fieldValue)
                        {
                            MethodInfo mi = list.GetType().GetMethod("Add");
                            mi.Invoke(list, new Object[] { fieldValue });
                        }

                        break;
                    }
                }

                return(res);
            }
        }
예제 #10
0
        /// <summary>
        /// Returns a field mapping object given a FieldInfo object
        /// </summary>
        /// <param name="fi">FieldInfo object representing a field within its container</param>
        /// <param name="cacheKeyValue">Key to be used by field mapping caching</param>
        /// <returns>FieldMapping object if found, otherwise null</returns>
        public FieldMapping GetFieldMapping(FieldInfo fi, String cacheKeyValue)
        {
            FieldMapping desc = null;

            BaseAttribute attr = Helpers.ExtractAttribute(fi);

            if (null != attr)
            {
                String cacheKey = fi.DeclaringType.FullName + "_" + cacheKeyValue;

                if (!fieldMappingCache.TryGetValue(cacheKey, out desc))
                {
                    if (attr is XmlAttributeAttribute)
                    {
                        XmlAttributeAttribute attrAttr = attr as XmlAttributeAttribute;

                        desc = new FieldMapping(NodeType.Attribute);

                        desc.XmlName        = null != attrAttr.Name ? attrAttr.Name : fi.Name;
                        desc.Required       = attrAttr.Required;
                        desc.ValueType      = fi.FieldType;
                        desc.ObjBindingName = fi.Name;
                        desc.FieldInfo      = fi;
                    }
                    else if (attr is ElementAttribute)
                    {
                        ElementAttribute elemAttr = attr as ElementAttribute;

                        desc                = new FieldMapping(NodeType.Element);
                        desc.XmlName        = null != elemAttr.Name ? elemAttr.Name : fi.Name;
                        desc.Required       = elemAttr.Required;
                        desc.ValueType      = fi.FieldType;
                        desc.ObjBindingName = fi.Name;
                        desc.FieldInfo      = fi;
                    }
                    else if (attr is ElementMapAttribute)
                    {
                        ElementMapAttribute mapAttr = attr as ElementMapAttribute;

                        desc                = new FieldMapping(NodeType.Map);
                        desc.Inline         = mapAttr.Inline;
                        desc.KeyName        = mapAttr.Key;
                        desc.ValueType      = fi.FieldType;
                        desc.ObjBindingName = fi.Name;
                        desc.FieldInfo      = fi;

                        //
                        // Get entry element name.
                        //
                        if (fi.FieldType.IsGenericType)
                        {
                            Type[] gargs = fi.FieldType.GetGenericArguments();
                            desc.MapEntryKeyType   = gargs[0];
                            desc.MapEntryValueType = gargs[1];

                            desc.MapEntryXmlName = Helpers.ExtractElementName(desc.MapEntryValueType);
                        }

                        //
                        // Get map element name
                        //
                        if (mapAttr.Inline)
                        {
                            desc.XmlName = null != mapAttr.Entry ? mapAttr.Entry : "entry";
                        }
                        else
                        {
                            desc.XmlName = mapAttr.Name;
                        }
                    }
                    else if (attr is ElementListAttribute)
                    {
                        ElementListAttribute listAttr = attr as ElementListAttribute;

                        desc                = new FieldMapping(NodeType.List);
                        desc.Inline         = listAttr.Inline;
                        desc.ValueType      = fi.FieldType;
                        desc.ObjBindingName = fi.Name;
                        desc.FieldInfo      = fi;

                        //
                        // Get entry element name.
                        //
                        if (fi.FieldType.IsGenericType)
                        {
                            Type[] gargs = fi.FieldType.GetGenericArguments();
                            desc.MapEntryValueType = gargs[0];

                            //!desc.MapEntryName = Helpers.ExtractElementName(desc.MapEntryValueType);
                        }

                        //
                        // Get list entry name.
                        //
                        if (listAttr.Inline)
                        {
                            String entryName = null != desc.MapEntryValueType ?
                                               Helpers.ToCamelCase(desc.MapEntryValueType.Name) : null;

                            desc.XmlName = null != listAttr.Entry ? listAttr.Entry :
                                           (null != entryName ? entryName : "entry");
                        }
                        else
                        {
                            desc.XmlName = listAttr.Name;
                        }
                    }

                    if (desc.XmlName.Equals(cacheKeyValue))
                    {
                        fieldMappingCache.Add(cacheKey, desc);
                    }
                }
            }

            return(desc);
        }