// // System.Xml.Serialization.XmlReflectionImporter // // Author: // Tim Coleman (tim@timcoleman.com) // Erik LeBel (eriklebel@yahoo.ca) // Lluis Sanchez Gual (lluis@ximian.com) // // Copyright (C) Tim Coleman, 2002 // (C) 2003 Erik LeBel // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Xml.Schema; namespace System.Xml.Serialization { public class XmlReflectionImporter { string initialDefaultNamespace; XmlAttributeOverrides attributeOverrides; ArrayList includedTypes; ReflectionHelper helper = new ReflectionHelper(); int arrayChoiceCount = 1; ArrayList relatedMaps = new ArrayList (); bool allowPrivateTypes = false; static readonly string errSimple = "Cannot serialize object of type '{0}'. Base " + "type '{1}' has simpleContent and can be only extended by adding XmlAttribute " + "elements. Please consider changing XmlText member of the base class to string array"; static readonly string errSimple2 = "Cannot serialize object of type '{0}'. " + "Consider changing type of XmlText member '{1}' from '{2}' to string or string array"; #region Constructors public XmlReflectionImporter () : this (null, null) { } public XmlReflectionImporter (string defaultNamespace) : this (null, defaultNamespace) { } public XmlReflectionImporter (XmlAttributeOverrides attributeOverrides) : this (attributeOverrides, null) { } public XmlReflectionImporter (XmlAttributeOverrides attributeOverrides, string defaultNamespace) { if (defaultNamespace == null) this.initialDefaultNamespace = String.Empty; else this.initialDefaultNamespace = defaultNamespace; if (attributeOverrides == null) this.attributeOverrides = new XmlAttributeOverrides(); else this.attributeOverrides = attributeOverrides; } /* void Reset () { helper = new ReflectionHelper(); arrayChoiceCount = 1; } */ internal bool AllowPrivateTypes { get { return allowPrivateTypes; } set { allowPrivateTypes = value; } } #endregion // Constructors #region Methods public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember [] members, bool hasWrapperElement) { return ImportMembersMapping (elementName, ns, members, hasWrapperElement, true); } #if NET_2_0 [MonoTODO] public #endif XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool rpc) { return ImportMembersMapping (elementName, ns, members, hasWrapperElement, rpc, true); } #if NET_2_0 [MonoTODO] public #endif XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool rpc, bool openModel) { return ImportMembersMapping (elementName, ns, members, hasWrapperElement, rpc, openModel, XmlMappingAccess.Read | XmlMappingAccess.Write); } #if NET_2_0 [MonoTODO] // FIXME: handle writeAccessors, validate, and mapping access public #endif XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool rpc, bool openModel, XmlMappingAccess access) { // Reset (); Disabled. See ChangeLog ArrayList mapping = new ArrayList (); for (int n=0; n= 0; } else if (order != null && isOrderExplicit != ((int) order >= 0)) throw new InvalidOperationException ("Inconsistent XML sequence was detected. If there are XmlElement/XmlArray/XmlAnyElement attributes with explicit Order, then every other member must have an explicit order too."); } if (isOrderExplicit == true) members.Sort ((m1, m2) => (int) m1.XmlAttributes.SortableOrder - (int) m2.XmlAttributes.SortableOrder); foreach (XmlReflectionMember rmember in members) { string ns = map.XmlTypeNamespace; if (rmember.XmlAttributes.XmlIgnore) continue; if (rmember.DeclaringType != null && rmember.DeclaringType != type) { XmlTypeMapping bmap = ImportClassMapping (rmember.DeclaringType, root, defaultNamespace); ns = bmap.XmlTypeNamespace; } try { XmlTypeMapMember mem = CreateMapMember (type, rmember, ns); mem.CheckOptionalValueType (type); classMap.AddMember (mem); } catch (Exception ex) { throw new InvalidOperationException (string.Format ( CultureInfo.InvariantCulture, "There was an error" + " reflecting field '{0}'.", rmember.MemberName), ex); } } // Import extra classes if (type == typeof (object) && includedTypes != null) { foreach (Type intype in includedTypes) map.DerivedTypes.Add (ImportTypeMapping (intype, defaultNamespace)); } // Register inheritance relations if (type.BaseType != null) { XmlTypeMapping bmap = ImportClassMapping (type.BaseType, root, defaultNamespace); ClassMap cbmap = bmap.ObjectMap as ClassMap; if (type.BaseType != typeof (object)) { map.BaseMap = bmap; if (!cbmap.HasSimpleContent) classMap.SetCanBeSimpleType (false); } // At this point, derived classes of this map must be already registered RegisterDerivedMap (bmap, map); if (cbmap.HasSimpleContent && classMap.ElementMembers != null && classMap.ElementMembers.Count != 1) throw new InvalidOperationException (String.Format (errSimple, map.TypeData.TypeName, map.BaseMap.TypeData.TypeName)); } ImportIncludedTypes (type, defaultNamespace); if (classMap.XmlTextCollector != null && !classMap.HasSimpleContent) { XmlTypeMapMember mem = classMap.XmlTextCollector; if (mem.TypeData.Type != typeof(string) && mem.TypeData.Type != typeof(string[]) && #if !MOONLIGHT mem.TypeData.Type != typeof(XmlNode[]) && #endif mem.TypeData.Type != typeof(object[])) throw new InvalidOperationException (String.Format (errSimple2, map.TypeData.TypeName, mem.Name, mem.TypeData.TypeName)); } return map; } void RegisterDerivedMap (XmlTypeMapping map, XmlTypeMapping derivedMap) { map.DerivedTypes.Add (derivedMap); map.DerivedTypes.AddRange (derivedMap.DerivedTypes); if (map.BaseMap != null) RegisterDerivedMap (map.BaseMap, derivedMap); else { XmlTypeMapping obmap = ImportTypeMapping (typeof(object)); if (obmap != map) obmap.DerivedTypes.Add (derivedMap); } } string GetTypeNamespace (TypeData typeData, XmlRootAttribute root, string defaultNamespace) { string typeNamespace = null; XmlAttributes atts = null; if (!typeData.IsListType) { if (attributeOverrides != null) atts = attributeOverrides[typeData.Type]; } if (atts == null) atts = new XmlAttributes (typeData.Type); if (atts.XmlType != null) { if (atts.XmlType.Namespace != null && atts.XmlType.Namespace.Length != 0 && typeData.SchemaType != SchemaTypes.Enum) typeNamespace = atts.XmlType.Namespace; } if (typeNamespace != null && typeNamespace.Length != 0) return typeNamespace; if (atts.XmlRoot != null && root == null) root = atts.XmlRoot; if (root != null) { if (root.Namespace != null && root.Namespace.Length != 0) return root.Namespace; } if (defaultNamespace == null) return ""; else return defaultNamespace; } XmlTypeMapping ImportListMapping (Type type, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel) { TypeData typeData = TypeTranslator.GetTypeData (type); return ImportListMapping (typeData, root, defaultNamespace, atts, nestingLevel); } XmlTypeMapping ImportListMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel) { Type type = typeData.Type; ListMap obmap = new ListMap (); if (!allowPrivateTypes) ReflectionHelper.CheckSerializableType (type, true); if (atts == null) atts = new XmlAttributes(); Type itemType = typeData.ListItemType; // warning: byte[][] should not be considered multiarray bool isMultiArray = (type.IsArray && (TypeTranslator.GetTypeData(itemType).SchemaType == SchemaTypes.Array) && itemType.IsArray); XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList(); foreach (XmlArrayItemAttribute att in atts.XmlArrayItems) { if (att.Namespace != null && att.Form == XmlSchemaForm.Unqualified) throw new InvalidOperationException ("XmlArrayItemAttribute.Form must not be Unqualified when it has an explicit Namespace value."); if (att.NestingLevel != nestingLevel) continue; Type elemType = (att.Type != null) ? att.Type : itemType; XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData(elemType, att.DataType)); elem.Namespace = att.Namespace != null ? att.Namespace : defaultNamespace; if (elem.Namespace == null) elem.Namespace = ""; elem.Form = att.Form; if (att.Form == XmlSchemaForm.Unqualified) elem.Namespace = string.Empty; elem.IsNullable = att.IsNullable && CanBeNull (elem.TypeData); elem.NestingLevel = att.NestingLevel; if (isMultiArray) { elem.MappedType = ImportListMapping (elemType, null, elem.Namespace, atts, nestingLevel + 1); } else if (elem.TypeData.IsComplexType) { elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace); } if (att.ElementName.Length != 0) { elem.ElementName = XmlConvert.EncodeLocalName (att.ElementName); } else if (elem.MappedType != null) { elem.ElementName = elem.MappedType.ElementName; } else { elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType; } list.Add (elem); } if (list.Count == 0) { XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData (itemType)); if (isMultiArray) elem.MappedType = ImportListMapping (itemType, null, defaultNamespace, atts, nestingLevel + 1); else if (elem.TypeData.IsComplexType) elem.MappedType = ImportTypeMapping (itemType, null, defaultNamespace); if (elem.MappedType != null) { elem.ElementName = elem.MappedType.XmlType; } else { elem.ElementName = TypeTranslator.GetTypeData (itemType).XmlType; } elem.Namespace = (defaultNamespace != null) ? defaultNamespace : ""; elem.IsNullable = CanBeNull (elem.TypeData); list.Add (elem); } obmap.ItemInfo = list; // If there can be different element names (types) in the array, then its name cannot // be "ArrayOfXXX" it must be something like ArrayOfChoiceNNN string baseName; if (list.Count > 1) { baseName = "ArrayOfChoice" + (arrayChoiceCount++); } else { XmlTypeMapElementInfo elem = ((XmlTypeMapElementInfo) list[0]); if (elem.MappedType != null) { baseName = TypeTranslator.GetArrayName (elem.MappedType.XmlType); } else { baseName = TypeTranslator.GetArrayName (elem.ElementName); } } // Avoid name colisions int nameCount = 1; string name = baseName; do { XmlTypeMapping foundMap = helper.GetRegisteredSchemaType (name, defaultNamespace); if (foundMap == null) nameCount = -1; else if (obmap.Equals (foundMap.ObjectMap) && typeData.Type == foundMap.TypeData.Type) return foundMap; else name = baseName + (nameCount++); } while (nameCount != -1); XmlTypeMapping map = CreateTypeMapping (typeData, root, name, defaultNamespace); map.ObjectMap = obmap; // Register any of the including types as a derived class of object XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false); XmlTypeMapping objectMapping = ImportTypeMapping (typeof(object)); for (int i = 0; i < includes.Length; i++) { Type includedType = includes[i].Type; objectMapping.DerivedTypes.Add(ImportTypeMapping (includedType, null, defaultNamespace)); } // Register this map as a derived class of object helper.RegisterSchemaType (map, name, defaultNamespace); ImportTypeMapping (typeof(object)).DerivedTypes.Add (map); return map; } XmlTypeMapping ImportXmlNodeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace) { Type type = typeData.Type; XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace)); if (map != null) return map; map = CreateTypeMapping (typeData, root, null, defaultNamespace); helper.RegisterClrType (map, type, map.XmlTypeNamespace); if (type.BaseType != null) { XmlTypeMapping bmap = ImportTypeMapping (type.BaseType, root, defaultNamespace); if (type.BaseType != typeof (object)) map.BaseMap = bmap; RegisterDerivedMap (bmap, map); } return map; } XmlTypeMapping ImportPrimitiveMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace) { Type type = typeData.Type; XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace)); if (map != null) return map; map = CreateTypeMapping (typeData, root, null, defaultNamespace); helper.RegisterClrType (map, type, map.XmlTypeNamespace); return map; } #if MOONLIGHT // Enum.GetNames is not available in SL API public static System.Collections.Generic.IEnumerable GetEnumNames (Type type) { System.Collections.Generic.List names = new System.Collections.Generic.List (); foreach (FieldInfo fi in type.GetFields (BindingFlags.Static | BindingFlags.Public)) names.Add (fi.Name); return names; } #endif XmlTypeMapping ImportEnumMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace) { Type type = typeData.Type; XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace)); if (map != null) return map; if (!allowPrivateTypes) ReflectionHelper.CheckSerializableType (type, false); map = CreateTypeMapping (typeData, root, null, defaultNamespace); map.IsNullable = false; helper.RegisterClrType (map, type, map.XmlTypeNamespace); ArrayList members = new ArrayList(); #if MOONLIGHT foreach (string name in GetEnumNames (type)) { #else string [] names = Enum.GetNames (type); foreach (string name in names) { #endif FieldInfo field = type.GetField (name); string xmlName = null; if (field.IsDefined(typeof(XmlIgnoreAttribute), false)) continue; object[] atts = field.GetCustomAttributes (typeof(XmlEnumAttribute), false); if (atts.Length > 0) xmlName = ((XmlEnumAttribute)atts[0]).Name; if (xmlName == null) xmlName = name; long value = ((IConvertible) field.GetValue (null)).ToInt64 (CultureInfo.InvariantCulture); members.Add (new EnumMap.EnumMapMember (xmlName, name, value)); } bool isFlags = type.IsDefined (typeof (FlagsAttribute), false); map.ObjectMap = new EnumMap ((EnumMap.EnumMapMember[])members.ToArray (typeof(EnumMap.EnumMapMember)), isFlags); ImportTypeMapping (typeof(object)).DerivedTypes.Add (map); return map; } XmlTypeMapping ImportXmlSerializableMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace) { Type type = typeData.Type; XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace)); if (map != null) return map; if (!allowPrivateTypes) ReflectionHelper.CheckSerializableType (type, false); map = CreateTypeMapping (typeData, root, null, defaultNamespace); helper.RegisterClrType (map, type, map.XmlTypeNamespace); return map; } void ImportIncludedTypes (Type type, string defaultNamespace) { XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false); for (int n=0; n GetReflectionMembers (Type type) { // First we want to find the inheritance hierarchy in reverse order. Type currentType = type; ArrayList typeList = new ArrayList(); typeList.Add(currentType); while (currentType != typeof(object)) { currentType = currentType.BaseType; // Read the base type. typeList.Insert(0, currentType); // Insert at 0 to reverse the order. } // Read all Fields via reflection. ArrayList fieldList = new ArrayList(); FieldInfo[] tfields = type.GetFields (BindingFlags.Instance | BindingFlags.Public); #if TARGET_JVM // This statement ensures fields are ordered starting from the base type. for (int ti=0; ti 0) continue; if (prop.DeclaringType == typeList[ti]) propList.Add (prop); } } #else currentType = null; currentIndex = 0; foreach (PropertyInfo prop in tprops) { // This statement ensures properties are ordered starting from the base type. if (currentType != prop.DeclaringType) { currentType = prop.DeclaringType; currentIndex = 0; } if (!prop.CanRead) continue; if (prop.GetIndexParameters().Length > 0) continue; propList.Insert(currentIndex++, prop); } #endif var members = new List(); int fieldIndex=0; int propIndex=0; // We now step through the type hierarchy from the base (object) through // to the supplied class, as each step outputting all Fields, and then // all Properties. This is the exact same ordering as .NET 1.0/1.1. foreach (Type t in typeList) { // Add any fields matching the current DeclaringType. while (fieldIndex < fieldList.Count) { FieldInfo field = (FieldInfo)fieldList[fieldIndex]; if (field.DeclaringType==t) { fieldIndex++; XmlAttributes atts = attributeOverrides[type, field.Name]; if (atts == null) atts = new XmlAttributes (field); if (atts.XmlIgnore) continue; XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts); member.DeclaringType = field.DeclaringType; members.Add(member); } else break; } // Add any properties matching the current DeclaringType. while (propIndex < propList.Count) { PropertyInfo prop = (PropertyInfo)propList[propIndex]; if (prop.DeclaringType==t) { propIndex++; XmlAttributes atts = attributeOverrides[type, prop.Name]; if (atts == null) atts = new XmlAttributes (prop); if (atts.XmlIgnore) continue; if (!prop.CanWrite) { if (prop.PropertyType.IsGenericType && TypeData.GetGenericListItemType (prop.PropertyType) == null) continue; // check this before calling GetTypeData() which raises error for missing Add(). See bug #704813. if (TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array || prop.PropertyType.IsArray) continue; } XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts); member.DeclaringType = prop.DeclaringType; members.Add(member); } else break; } } return members; } private XmlTypeMapMember CreateMapMember (Type declaringType, XmlReflectionMember rmember, string defaultNamespace) { XmlTypeMapMember mapMember; XmlAttributes atts = rmember.XmlAttributes; TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType); if (atts.XmlArray != null) { if (atts.XmlArray.Namespace != null && atts.XmlArray.Form == XmlSchemaForm.Unqualified) throw new InvalidOperationException ("XmlArrayAttribute.Form must not be Unqualified when it has an explicit Namespace value."); if (typeData.SchemaType != SchemaTypes.Array && !(typeData.SchemaType == SchemaTypes.Primitive && typeData.Type == typeof (byte []))) throw new InvalidOperationException ("XmlArrayAttribute can be applied to members of array or collection type."); } #if !MOONLIGHT if (atts.XmlAnyAttribute != null) { if ( (rmember.MemberType.FullName == "System.Xml.XmlAttribute[]") || (rmember.MemberType.FullName == "System.Xml.XmlNode[]") ) { mapMember = new XmlTypeMapMemberAnyAttribute(); } else throw new InvalidOperationException ("XmlAnyAttributeAttribute can only be applied to members of type XmlAttribute[] or XmlNode[]"); } else #endif if (atts.XmlAnyElements != null && atts.XmlAnyElements.Count > 0) { // no XmlNode type check is done here (seealso: bug #553032). XmlTypeMapMemberAnyElement member = new XmlTypeMapMemberAnyElement(); member.ElementInfo = ImportAnyElementInfo (defaultNamespace, rmember, member, atts); mapMember = member; } else if (atts.Xmlns) { XmlTypeMapMemberNamespaces mapNamespaces = new XmlTypeMapMemberNamespaces (); mapMember = mapNamespaces; } else if (atts.XmlAttribute != null) { // An attribute if (atts.XmlElements != null && atts.XmlElements.Count > 0) throw new Exception ("XmlAttributeAttribute and XmlElementAttribute cannot be applied to the same member"); XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute (); if (atts.XmlAttribute.AttributeName.Length == 0) mapAttribute.AttributeName = rmember.MemberName; else mapAttribute.AttributeName = atts.XmlAttribute.AttributeName; mapAttribute.AttributeName = XmlConvert.EncodeLocalName (mapAttribute.AttributeName); if (typeData.IsComplexType) mapAttribute.MappedType = ImportTypeMapping (typeData.Type, null, defaultNamespace); if (atts.XmlAttribute.Namespace != null && atts.XmlAttribute.Namespace != defaultNamespace) { if (atts.XmlAttribute.Form == XmlSchemaForm.Unqualified) throw new InvalidOperationException ("The Form property may not be 'Unqualified' when an explicit Namespace property is present"); mapAttribute.Form = XmlSchemaForm.Qualified; mapAttribute.Namespace = atts.XmlAttribute.Namespace; } else { mapAttribute.Form = atts.XmlAttribute.Form; if (atts.XmlAttribute.Form == XmlSchemaForm.Qualified) mapAttribute.Namespace = defaultNamespace; else mapAttribute.Namespace = ""; } typeData = TypeTranslator.GetTypeData(rmember.MemberType, atts.XmlAttribute.DataType); mapMember = mapAttribute; } else if (typeData.SchemaType == SchemaTypes.Array) { // If the member has a single XmlElementAttribute and the type is the type of the member, // then it is not a flat list if (atts.XmlElements.Count > 1 || (atts.XmlElements.Count == 1 && atts.XmlElements[0].Type != typeData.Type) || (atts.XmlText != null)) { // A flat list // check that it does not have XmlArrayAttribute if (atts.XmlArray != null) throw new InvalidOperationException ("XmlArrayAttribute cannot be used with members which also attributed with XmlElementAttribute or XmlTextAttribute."); XmlTypeMapMemberFlatList member = new XmlTypeMapMemberFlatList (); member.ListMap = new ListMap (); member.ListMap.ItemInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName (rmember.MemberName), defaultNamespace, typeData.ListItemType, member, atts); member.ElementInfo = member.ListMap.ItemInfo; member.ListMap.ChoiceMember = member.ChoiceMember; mapMember = member; } else { // A list XmlTypeMapMemberList member = new XmlTypeMapMemberList (); // Creates an ElementInfo that identifies the array instance. member.ElementInfo = new XmlTypeMapElementInfoList(); XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, typeData); elem.ElementName = XmlConvert.EncodeLocalName((atts.XmlArray != null && atts.XmlArray.ElementName.Length != 0) ? atts.XmlArray.ElementName : rmember.MemberName); // note that it could be changed below (when Form is Unqualified) elem.Namespace = (atts.XmlArray != null && atts.XmlArray.Namespace != null) ? atts.XmlArray.Namespace : defaultNamespace; elem.MappedType = ImportListMapping (rmember.MemberType, null, elem.Namespace, atts, 0); elem.IsNullable = (atts.XmlArray != null) ? atts.XmlArray.IsNullable : false; elem.Form = (atts.XmlArray != null) ? atts.XmlArray.Form : XmlSchemaForm.Qualified; elem.ExplicitOrder = (atts.XmlArray != null) ? atts.XmlArray.Order : -1; // This is a bit tricky, but is done // after filling descendant members, so // that array items could be serialized // with proper namespace. if (atts.XmlArray != null && atts.XmlArray.Form == XmlSchemaForm.Unqualified) elem.Namespace = String.Empty; member.ElementInfo.Add (elem); mapMember = member; } } else { // An element XmlTypeMapMemberElement member = new XmlTypeMapMemberElement (); member.ElementInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName(rmember.MemberName), defaultNamespace, rmember.MemberType, member, atts); mapMember = member; } mapMember.DefaultValue = GetDefaultValue (typeData, atts.XmlDefaultValue); mapMember.TypeData = typeData; mapMember.Name = rmember.MemberName; mapMember.IsReturnValue = rmember.IsReturnValue; return mapMember; } XmlTypeMapElementInfoList ImportElementInfo (Type cls, string defaultName, string defaultNamespace, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts) { EnumMap choiceEnumMap = null; Type choiceEnumType = null; XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList(); ImportTextElementInfo (list, defaultType, member, atts, defaultNamespace); if (atts.XmlChoiceIdentifier != null) { if (cls == null) throw new InvalidOperationException ("XmlChoiceIdentifierAttribute not supported in this context."); member.ChoiceMember = atts.XmlChoiceIdentifier.MemberName; MemberInfo[] mems = cls.GetMember (member.ChoiceMember, BindingFlags.Instance|BindingFlags.Public); if (mems.Length == 0) throw new InvalidOperationException ("Choice member '" + member.ChoiceMember + "' not found in class '" + cls); if (mems[0] is PropertyInfo) { PropertyInfo pi = (PropertyInfo)mems[0]; if (!pi.CanWrite || !pi.CanRead) throw new InvalidOperationException ("Choice property '" + member.ChoiceMember + "' must be read/write."); choiceEnumType = pi.PropertyType; } else choiceEnumType = ((FieldInfo)mems[0]).FieldType; member.ChoiceTypeData = TypeTranslator.GetTypeData (choiceEnumType); if (choiceEnumType.IsArray) choiceEnumType = choiceEnumType.GetElementType (); choiceEnumMap = ImportTypeMapping (choiceEnumType).ObjectMap as EnumMap; if (choiceEnumMap == null) throw new InvalidOperationException ("The member '" + mems[0].Name + "' is not a valid target for XmlChoiceIdentifierAttribute."); } if (atts.XmlElements.Count == 0 && list.Count == 0) { XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType)); elem.ElementName = defaultName; elem.Namespace = defaultNamespace; if (elem.TypeData.IsComplexType) elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace); list.Add (elem); } bool multiType = (atts.XmlElements.Count > 1); foreach (XmlElementAttribute att in atts.XmlElements) { Type elemType = (att.Type != null) ? att.Type : defaultType; XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(elemType, att.DataType)); elem.Form = att.Form; if (elem.Form != XmlSchemaForm.Unqualified) elem.Namespace = (att.Namespace != null) ? att.Namespace : defaultNamespace; elem.IsNullable = att.IsNullable; elem.ExplicitOrder = att.Order; if (elem.IsNullable && !elem.TypeData.IsNullable) throw new InvalidOperationException ("IsNullable may not be 'true' for value type " + elem.TypeData.FullTypeName + " in member '" + defaultName + "'"); if (elem.TypeData.IsComplexType) { if (att.DataType.Length != 0) throw new InvalidOperationException ( string.Format(CultureInfo.InvariantCulture, "'{0}' is " + "an invalid value for '{1}.{2}' of type '{3}'. " + "The property may only be specified for primitive types.", att.DataType, cls.FullName, defaultName, elem.TypeData.FullTypeName)); elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace); } if (att.ElementName.Length != 0) { elem.ElementName = XmlConvert.EncodeLocalName(att.ElementName); } else if (multiType) { if (elem.MappedType != null) { elem.ElementName = elem.MappedType.ElementName; } else { elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType; } } else { elem.ElementName = defaultName; } if (choiceEnumMap != null) { string cname = choiceEnumMap.GetEnumName (choiceEnumType.FullName, elem.ElementName); if (cname == null) throw new InvalidOperationException (string.Format ( CultureInfo.InvariantCulture, "Type {0} is missing" + " enumeration value '{1}' for element '{1} from" + " namespace '{2}'.", choiceEnumType, elem.ElementName, elem.Namespace)); elem.ChoiceValue = Enum.Parse (choiceEnumType, cname, false); } list.Add (elem); } return list; } XmlTypeMapElementInfoList ImportAnyElementInfo (string defaultNamespace, XmlReflectionMember rmember, XmlTypeMapMemberElement member, XmlAttributes atts) { XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList(); ImportTextElementInfo (list, rmember.MemberType, member, atts, defaultNamespace); #if !MOONLIGHT // no practical anyElement support foreach (XmlAnyElementAttribute att in atts.XmlAnyElements) { XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(typeof(XmlElement))); if (att.Name.Length != 0) { elem.ElementName = XmlConvert.EncodeLocalName(att.Name); elem.Namespace = (att.Namespace != null) ? att.Namespace : ""; } else { elem.IsUnnamedAnyElement = true; elem.Namespace = defaultNamespace; if (att.Namespace != null) throw new InvalidOperationException ("The element " + rmember.MemberName + " has been attributed with an XmlAnyElementAttribute and a namespace '" + att.Namespace + "', but no name. When a namespace is supplied, a name is also required. Supply a name or remove the namespace."); } elem.ExplicitOrder = att.Order; list.Add (elem); } #endif return list; } void ImportTextElementInfo (XmlTypeMapElementInfoList list, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts, string defaultNamespace) { if (atts.XmlText != null) { member.IsXmlTextCollector = true; if (atts.XmlText.Type != null) { TypeData td = TypeTranslator.GetTypeData (defaultType); if ((td.SchemaType == SchemaTypes.Primitive || td.SchemaType == SchemaTypes.Enum) && atts.XmlText.Type != defaultType) { throw new InvalidOperationException ("The type for XmlText may not be specified for primitive types."); } defaultType = atts.XmlText.Type; } #if !MOONLIGHT if (defaultType == typeof(XmlNode)) defaultType = typeof(XmlText); // Nodes must be text nodes #endif XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType, atts.XmlText.DataType)); if (elem.TypeData.SchemaType != SchemaTypes.Primitive && elem.TypeData.SchemaType != SchemaTypes.Enum && elem.TypeData.SchemaType != SchemaTypes.XmlNode && !(elem.TypeData.SchemaType == SchemaTypes.Array && elem.TypeData.ListItemTypeData.SchemaType == SchemaTypes.XmlNode) ) throw new InvalidOperationException ("XmlText cannot be used to encode complex types"); if (elem.TypeData.IsComplexType) elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace); elem.IsTextElement = true; elem.WrappedElement = false; list.Add (elem); } } bool CanBeNull (TypeData type) { #if !NET_2_0 // idiotic compatibility if (type.Type == typeof (XmlQualifiedName)) return false; #endif return !type.Type.IsValueType || type.IsNullable; } public void IncludeType (Type type) { if (type == null) throw new ArgumentNullException ("type"); if (includedTypes == null) includedTypes = new ArrayList (); if (!includedTypes.Contains (type)) includedTypes.Add (type); if (relatedMaps.Count > 0) { foreach (XmlTypeMapping map in (ArrayList) relatedMaps.Clone ()) { if (map.TypeData.Type == typeof(object)) map.DerivedTypes.Add (ImportTypeMapping (type)); } } } public void IncludeTypes (ICustomAttributeProvider provider) { object[] ats = provider.GetCustomAttributes (typeof(XmlIncludeAttribute), true); foreach (XmlIncludeAttribute at in ats) IncludeType (at.Type); } private object GetDefaultValue (TypeData typeData, object defaultValue) { if (defaultValue == DBNull.Value || typeData.SchemaType != SchemaTypes.Enum) return defaultValue; #if MOONLIGHT string namedValue = (defaultValue as Enum).ToString ("g"); string decimalValue = (defaultValue as Enum).ToString ("d"); #else // get string representation of enum value string namedValue = Enum.Format (typeData.Type, defaultValue, "g"); // get decimal representation of enum value string decimalValue = Enum.Format (typeData.Type, defaultValue, "d"); #endif // if decimal representation matches string representation, then // the value is not defined in the enum type (as the "g" format // will return the decimal equivalent of the value if the value // is not equal to a combination of named enumerated constants if (namedValue == decimalValue) { string msg = string.Format (CultureInfo.InvariantCulture, "Value '{0}' cannot be converted to {1}.", defaultValue, defaultValue.GetType ().FullName); throw new InvalidOperationException (msg); } // XmlSerializer expects integral enum value //return namedValue.Replace (',', ' '); return defaultValue; } #endregion // Methods } }