// // System.Xml.Serialization.SoapReflectionImporter // // Author: // Tim Coleman (tim@timcoleman.com) // // Copyright (C) Tim Coleman, 2002 // // // 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.Globalization; using System.Reflection; using System.Xml; using System.Xml.Schema; namespace System.Xml.Serialization { public class SoapReflectionImporter { SoapAttributeOverrides attributeOverrides; string initialDefaultNamespace; ArrayList includedTypes; ArrayList relatedMaps = new ArrayList (); ReflectionHelper helper = new ReflectionHelper(); #region Constructors public SoapReflectionImporter (): this (null, null) { } public SoapReflectionImporter (SoapAttributeOverrides attributeOverrides): this (attributeOverrides, null) { } public SoapReflectionImporter (string defaultNamespace): this (null, defaultNamespace) { } public SoapReflectionImporter (SoapAttributeOverrides attributeOverrides, string defaultNamespace) { if (defaultNamespace == null) initialDefaultNamespace = String.Empty; else initialDefaultNamespace = defaultNamespace; if (attributeOverrides == null) this.attributeOverrides = new SoapAttributeOverrides(); else this.attributeOverrides = attributeOverrides; } #endregion // Constructors #region Methods public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members) { return ImportMembersMapping (elementName, ns, members, true, true, false); } public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors) { return ImportMembersMapping (elementName, ns, members, hasWrapperElement, writeAccessors, false); } public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors, bool validate) { return ImportMembersMapping (elementName, ns, members, hasWrapperElement, writeAccessors, validate, XmlMappingAccess.Read | XmlMappingAccess.Write); } #if NET_2_0 [MonoTODO] public #endif XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors, bool validate, XmlMappingAccess access) { elementName = XmlConvert.EncodeLocalName (elementName); XmlMemberMapping[] mapping = new XmlMemberMapping[members.Length]; for (int n=0; n 0) xmlName = ((SoapEnumAttribute)atts[0]).Name; long value = ((IConvertible) field.GetValue (null)).ToInt64 (CultureInfo.InvariantCulture); members[n] = new EnumMap.EnumMapMember (XmlConvert.EncodeLocalName (xmlName), names[n], value); } bool isFlags = type.IsDefined (typeof (FlagsAttribute), false); map.ObjectMap = new EnumMap (members, isFlags); ImportTypeMapping (typeof(object), defaultNamespace).DerivedTypes.Add (map); return map; } ICollection GetReflectionMembers (Type type) { ArrayList members = new ArrayList(); PropertyInfo[] properties = type.GetProperties (BindingFlags.Instance | BindingFlags.Public); foreach (PropertyInfo prop in properties) { if (!prop.CanRead) continue; if (!prop.CanWrite && (TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array || prop.PropertyType.IsArray)) continue; SoapAttributes atts = attributeOverrides[type, prop.Name]; if (atts == null) atts = new SoapAttributes (prop); if (atts.SoapIgnore) continue; XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts); members.Add (member); } FieldInfo[] fields = type.GetFields (BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo field in fields) { SoapAttributes atts = attributeOverrides[type, field.Name]; if (atts == null) atts = new SoapAttributes (field); if (atts.SoapIgnore) continue; XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts); members.Add (member); } return members; } private XmlTypeMapMember CreateMapMember (XmlReflectionMember rmember, string defaultNamespace) { XmlTypeMapMember mapMember; SoapAttributes atts = rmember.SoapAttributes; TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType); if (atts.SoapAttribute != null) { // An attribute if (typeData.SchemaType != SchemaTypes.Enum && typeData.SchemaType != SchemaTypes.Primitive) { throw new InvalidOperationException (string.Format (CultureInfo.InvariantCulture, "Cannot serialize member '{0}' of type {1}. " + "SoapAttribute cannot be used to encode complex types.", rmember.MemberName, typeData.FullTypeName)); } if (atts.SoapElement != null) throw new Exception ("SoapAttributeAttribute and SoapElementAttribute cannot be applied to the same member"); XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute (); if (atts.SoapAttribute.AttributeName.Length == 0) mapAttribute.AttributeName = XmlConvert.EncodeLocalName (rmember.MemberName); else mapAttribute.AttributeName = XmlConvert.EncodeLocalName (atts.SoapAttribute.AttributeName); mapAttribute.Namespace = (atts.SoapAttribute.Namespace != null) ? atts.SoapAttribute.Namespace : ""; if (typeData.IsComplexType) mapAttribute.MappedType = ImportTypeMapping (typeData.Type, defaultNamespace); typeData = TypeTranslator.GetTypeData (rmember.MemberType, atts.SoapAttribute.DataType); mapMember = mapAttribute; mapMember.DefaultValue = GetDefaultValue (typeData, atts.SoapDefaultValue); } else { if (typeData.SchemaType == SchemaTypes.Array) mapMember = new XmlTypeMapMemberList (); else mapMember = new XmlTypeMapMemberElement (); if (atts.SoapElement != null && atts.SoapElement.DataType.Length != 0) typeData = TypeTranslator.GetTypeData (rmember.MemberType, atts.SoapElement.DataType); // Creates an ElementInfo that identifies the element XmlTypeMapElementInfoList infoList = new XmlTypeMapElementInfoList(); XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (mapMember, typeData); elem.ElementName = XmlConvert.EncodeLocalName ((atts.SoapElement != null && atts.SoapElement.ElementName.Length != 0) ? atts.SoapElement.ElementName : rmember.MemberName); elem.Namespace = string.Empty; elem.IsNullable = (atts.SoapElement != null) ? atts.SoapElement.IsNullable : false; if (typeData.IsComplexType) elem.MappedType = ImportTypeMapping (typeData.Type, defaultNamespace); infoList.Add (elem); ((XmlTypeMapMemberElement)mapMember).ElementInfo = infoList; } mapMember.TypeData = typeData; mapMember.Name = rmember.MemberName; mapMember.IsReturnValue = rmember.IsReturnValue; return mapMember; } 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); } public void IncludeTypes (ICustomAttributeProvider provider) { object[] ats = provider.GetCustomAttributes (typeof(SoapIncludeAttribute), true); foreach (SoapIncludeAttribute at in ats) IncludeType (at.Type); } Exception CreateTypeException (Type type) { return new NotSupportedException ("The type " + type.FullName + " may not be serialized with SOAP-encoded messages. Set the Use for your message to Literal"); } Exception CreateStructException (Type type) { return new NotSupportedException ("Cannot serialize " + type.FullName + ". Nested structs are not supported with encoded SOAP"); } private object GetDefaultValue (TypeData typeData, object defaultValue) { if (defaultValue == DBNull.Value || typeData.SchemaType != SchemaTypes.Enum) return defaultValue; if (typeData.Type != defaultValue.GetType ()) { string msg = string.Format (CultureInfo.InvariantCulture, "Enum {0} cannot be converted to {1}.", defaultValue.GetType ().FullName, typeData.FullTypeName); throw new InvalidOperationException (msg); } // 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"); // 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 } }