/// <summary> /// Set the value of the input field or property using the element. This function will examine the inputs to determine the /// best match for the element. In some cases this may trigger a recursive examination of the element's children. /// </summary> /// <param name="fieldOrProperty">Field or property whose value will be set.</param> /// <param name="element">Element to obtain the value from.</param> /// <param name="o">Object instance whose field or property will be set.</param> void _SetFieldOrProperty(IFieldOrProperty fieldOrProperty, XmlNode element, ref object o) { // first check for valueOf object value = _CheckValueOf(element); if (value != null) { fieldOrProperty.SetValue(o, value); // done return; } // check for aggregate if (_CheckAggregate(fieldOrProperty, element, ref o)) // no need to set value; values of aggregates are set as post process action. return; string objName = _GetObjectName(o); // if its a string we can just set it if (fieldOrProperty.DeclaredType == typeof(System.String)) { fieldOrProperty.SetValue(o, element.InnerText); } else if (TypeUtil.IsPrimitiveType(fieldOrProperty.DeclaredType)) { if (element.InnerText == null || element.InnerText.Trim() == string.Empty) { _Error("Empty primitive value for type, won't set it: element: " + element.LocalName + ", type: " + fieldOrProperty.DeclaredType.FullName + ", object: " + objName); } else { // extract primitive value and set it try { fieldOrProperty.SetValue(o, TypeUtil.GetPrimitiveValue(fieldOrProperty.DeclaredType, element.InnerText)); } catch (Exception) { _Error("TorqueXmlDeserializer._SetFieldOrProperty - Unable to parse format string {0} for type {1} for element {2} in object {3}.", element.InnerText, fieldOrProperty.DeclaredType.FullName, element.LocalName, objName); } } } // if it is a reference type, we can deserialize in to it else if (fieldOrProperty.DeclaredType.IsClass || fieldOrProperty.DeclaredType.IsInterface || fieldOrProperty.DeclaredType.IsAbstract) { bool isInstantiable = !(fieldOrProperty.DeclaredType.IsInterface || fieldOrProperty.DeclaredType.IsAbstract); object subObj; TypeInfo subType; // check for name ref if (_BindNameRef(element, fieldOrProperty, ref o) != null) // done with this field return; // for reference types, allow deserializing into the existing object, if the attribute is present // and there is a valid object. subObj = fieldOrProperty.GetValue(o); bool inPlace = false; if (subObj != null) { // instance exists, check for attribute - use the property/field type for this, not // declared type. object[] attributes = fieldOrProperty.GetCustomAttributes(true); foreach (object attr in attributes) if (attr is TorqueXmlDeserializeInPlace) { inPlace = true; break; } if (!inPlace) { // check for xml attribute XmlNode inPlaceAttribute = element.Attributes.GetNamedItem("inPlace"); if (inPlaceAttribute != null && Boolean.Parse(inPlaceAttribute.Value)) inPlace = true; } } if (subObj != null && inPlace) { // ok to deserialize in place; get the typeinfo object for later use. subType = TypeUtil.FindTypeInfo(subObj.GetType().FullName); } else { // log if we are replacing, but there was an instance already //if (subObj != null) // _Info("Field has object instance but TorqueXmlDeserializeInPlace/inPlace attribute not specified: creating new object:" + element.LocalName); // in case we are making a fixed size list, pass the number of children down to the object maker int childCount = element.ChildNodes.Count; // if type is not instantiable, we might be able to come up with an instantiable type by looking for type mappings if (!isInstantiable) _MakeNewObject(element, out subObj, out subType, childCount); else // instantiable type, so make object used declared type _MakeNewObject(fieldOrProperty.DeclaredType.FullName, out subObj, out subType, childCount); if (subObj == null) { _Error("Unable to create new instance for element {0} on object {1}", element.LocalName, objName); return; } } // if it is a list of objects, handle it specially if (subType.IsList) { DeserializedList dlist = new DeserializedList(subObj); _RecurseList(element, ref dlist, subType); } else _Recurse(element, ref subObj, subType); // set value // it is important to do this after the element has been deserialized - some property accessors // assume this (such as lists of link points and object types). fieldOrProperty.SetValue(o, subObj); } // if it is a value type, then it must be a struct (because we handled primitives earlier) else if (fieldOrProperty.DeclaredType.IsValueType) { // look for object type ref if (_BindObjectTypeRef(element, fieldOrProperty, ref o) != null) return; object subObj; TypeInfo subType; _MakeNewObject(fieldOrProperty.DeclaredType.FullName, out subObj, out subType); if (subType == null) // check type for null instead of object, since this is a value type { _Error("Unable to create new instance for element {0} on object {1}", element.LocalName, objName); return; } _Recurse(element, ref subObj, subType); // set value fieldOrProperty.SetValue(o, subObj); } else _Error("Don't know how to set field or property of type {0} for element {1} on object: {2}", fieldOrProperty.DeclaredType.FullName, element.LocalName, objName); }
/// <summary> /// Examine the children of the specifid list element and add its values to the input list. /// </summary> /// <param name="element">Element to examine</param> /// <param name="list">List that will receive the values</param> /// <param name="ti">TypeInfo of the list that is receiving the values.</param> void _RecurseList(XmlNode element, ref DeserializedList list, TypeInfo ti) { // get the type of the list. if it is a specific type (i.e., not "object"), we can use that type // to create instances for children that do not specify a type attribute. Type listType = list.GetListType(); bool listTypeInstantiable = TypeUtil.IsInstantiable(listType); foreach (XmlNode child in element.ChildNodes) { if (child.NodeType != XmlNodeType.Element) continue; TypeInfo subType = null; object subObj = null; // does the element have inPlace specified? If so, search for an existing instance of the same type // in the list. if it exists, deserialize into that instance, instead of into a new object, bool inPlace = false; XmlNode inPlaceAttribute = child.Attributes.GetNamedItem("inPlace"); if (inPlaceAttribute != null && Boolean.Parse(inPlaceAttribute.Value)) { // has attribute. lookup the type string typeName = _GetObjectTypeName(child); subType = TypeUtil.FindTypeInfo(typeName); // try to find an instance of the type in the list (will be null if unsuccessful) subObj = list.GetFirstInstanceOfType(subType); inPlace = subObj != null; } if (!inPlace) // create new object _MakeNewObject(child, out subObj, out subType); if (subObj == null && subType == null && listTypeInstantiable) { // couldn't find a mapping for this type, but the list itself has an instantiable type that we could use. // if we are creating a String, just use the text value from the xml instead of creating a new object. // we can't actual create a new object anyway because String has no zero argument constructor. if (listType == typeof(String)) { subObj = child.InnerText; subType = TypeUtil.FindTypeInfo(typeof(String).FullName); } else { // create the new object using the list's type. // sometimes a warning but sometimes what the user intends. we'll call it _Info // ok, this is just excessive console spam. don't worry about this case. //_Info("List subelement '{0}' has no default type mapping or type attribute specified. Using the type of the list container, '{1}', to create list element instance.", child.LocalName, listType.FullName); _MakeNewObject(listType.FullName, out subObj, out subType); } } // still no object? warn about it and continue if (subObj == null) { _Warn("Ignoring List subelement '{0}': no default type mapping, no type attribute, and list type '{1}' is not instantiable", child.LocalName, listType.FullName); continue; } // if the subType is a primitive or enum, do not recurse on it - instead just set the value directly if (TypeUtil.IsPrimitiveType(subType.Type)) { try { subObj = TypeUtil.GetPrimitiveValue(subType.Type, child.InnerText); } catch (Exception) { _Error("TorqueXmlDeserializer._SetFieldOrProperty - Unable to parse format string {0} for type {1}.", child.InnerText, subType.Type.FullName); } } else { _Recurse(child, ref subObj, subType); } if (!inPlace) list.Add(subObj); } }