// // XmlSerializationReaderInterpreter.cs: // // Author: // Lluis Sanchez Gual (lluis@ximian.com) // // (C) 2002, 2003 Ximian, Inc. http://www.ximian.com // using System; using System.Reflection; using System.Collections; namespace System.Xml.Serialization { internal class XmlSerializationReaderInterpreter: XmlSerializationReader { XmlMapping _typeMap; SerializationFormat _format; public XmlSerializationReaderInterpreter(XmlMapping typeMap) { _typeMap = typeMap; _format = typeMap.Format; } protected override void InitCallbacks () { ArrayList maps = _typeMap.RelatedMaps; if (maps != null) { foreach (XmlTypeMapping map in maps) { if (map.TypeData.SchemaType == SchemaTypes.Class || map.TypeData.SchemaType == SchemaTypes.Enum) { ReaderCallbackInfo info = new ReaderCallbackInfo (this, map); AddReadCallback (map.XmlType, map.Namespace, map.TypeData.Type, new XmlSerializationReadCallback (info.ReadObject)); } } } } protected override void InitIDs () { } protected XmlTypeMapping GetTypeMap (Type type) { ArrayList maps = _typeMap.RelatedMaps; if (maps != null) { foreach (XmlTypeMapping map in maps) if (map.TypeData.Type == type) return map; } throw new InvalidOperationException ("Type " + type + " not mapped"); } public object ReadObject () { Reader.MoveToContent(); if (_typeMap is XmlTypeMapping) { if (_format == SerializationFormat.Literal) return ReadRoot ((XmlTypeMapping)_typeMap); else return ReadEncodedObject ((XmlTypeMapping)_typeMap); } else return ReadMessage ((XmlMembersMapping)_typeMap); } object ReadEncodedObject (XmlTypeMapping typeMap) { object ob = null; Reader.MoveToContent(); if (Reader.NodeType == System.Xml.XmlNodeType.Element) { if (Reader.LocalName == typeMap.ElementName && Reader.NamespaceURI == typeMap.Namespace) ob = ReadReferencedElement(); else throw CreateUnknownNodeException(); } else UnknownNode(null); ReadReferencedElements(); return ob; } protected virtual object ReadMessage (XmlMembersMapping typeMap) { object[] parameters = new object[typeMap.Count]; if (typeMap.HasWrapperElement) { if (_format == SerializationFormat.Encoded) { while (Reader.NodeType == System.Xml.XmlNodeType.Element) { string root = Reader.GetAttribute ("root", XmlSerializer.EncodingNamespace); if (root == null || System.Xml.XmlConvert.ToBoolean(root)) break; ReadReferencedElement (); Reader.MoveToContent (); } } while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) { if (Reader.IsStartElement(typeMap.ElementName, typeMap.Namespace) || _format == SerializationFormat.Encoded) { if (Reader.IsEmptyElement) { Reader.Skip(); Reader.MoveToContent(); continue; } Reader.ReadStartElement(); ReadMembers ((ClassMap)typeMap.ObjectMap, parameters, true, false); ReadEndElement(); break; } else UnknownNode(null); Reader.MoveToContent(); } } else ReadMembers ((ClassMap)typeMap.ObjectMap, parameters, true, _format == SerializationFormat.Encoded); if (_format == SerializationFormat.Encoded) ReadReferencedElements(); return parameters; } object ReadRoot (XmlTypeMapping rootMap) { if (rootMap.TypeData.SchemaType == SchemaTypes.XmlNode) { return ReadXmlNodeElement (rootMap, true); } else { if (Reader.LocalName != rootMap.ElementName || Reader.NamespaceURI != rootMap.Namespace) throw CreateUnknownNodeException(); return ReadObject (rootMap, true, true); } } protected virtual object ReadObject (XmlTypeMapping typeMap, bool isNullable, bool checkType) { switch (typeMap.TypeData.SchemaType) { case SchemaTypes.Class: return ReadClassInstance (typeMap, isNullable, checkType); case SchemaTypes.Array: return ReadListElement (typeMap, isNullable, null, true); case SchemaTypes.XmlNode: return ReadXmlNodeElement (typeMap, isNullable); case SchemaTypes.Primitive: return ReadPrimitiveElement (typeMap, isNullable); case SchemaTypes.Enum: return ReadEnumElement (typeMap, isNullable); case SchemaTypes.XmlSerializable: return ReadXmlSerializableElement (typeMap, isNullable); default: throw new Exception ("Unsupported map type"); } } protected virtual object ReadClassInstance (XmlTypeMapping typeMap, bool isNullable, bool checkType) { if (isNullable && ReadNull()) return null; if (checkType) { System.Xml.XmlQualifiedName t = GetXsiType(); if (t != null) { XmlTypeMapping realMap = typeMap.GetRealElementMap (t.Name, t.Namespace); if (realMap == null) { if (typeMap.TypeData.Type == typeof(object)) return ReadTypedPrimitive (t); else throw CreateUnknownTypeException((System.Xml.XmlQualifiedName)t); } if (realMap != typeMap) return ReadObject (realMap, false, false); } } object ob = Activator.CreateInstance (typeMap.TypeData.Type); Reader.MoveToElement(); bool isEmpty = Reader.IsEmptyElement; ReadClassInstanceMembers (typeMap, ob); if (isEmpty) Reader.Skip(); else ReadEndElement(); return ob; } protected virtual void ReadClassInstanceMembers (XmlTypeMapping typeMap, object ob) { ReadMembers ((ClassMap) typeMap.ObjectMap, ob, false, false); } void ReadMembers (ClassMap map, object ob, bool isValueList, bool readByOrder) { // A value list cannot have attributes if (!isValueList) { // Reads attributes XmlTypeMapMember anyAttrMember = map.DefaultAnyAttributeMember; int anyAttributeIndex = 0; object anyAttributeArray = null; while (Reader.MoveToNextAttribute()) { XmlTypeMapMemberAttribute member = map.GetAttribute (Reader.LocalName, Reader.NamespaceURI); if (member != null) { SetMemberValue (member, ob, GetValueFromXmlString (Reader.Value, member.TypeData, member.MappedType), isValueList); } else if (IsXmlnsAttribute(Reader.Name)) { // If the map has NamespaceDeclarations, // then store this xmlns to the given member. // If the instance doesn't exist, then create. if (map.NamespaceDeclarations != null) { XmlSerializerNamespaces nss = this.GetMemberValue (map.NamespaceDeclarations, ob, isValueList) as XmlSerializerNamespaces; if (nss == null) { nss = new XmlSerializerNamespaces (); SetMemberValue (map.NamespaceDeclarations, ob, nss, isValueList); } if (Reader.Prefix == "xmlns") nss.Add (Reader.LocalName, Reader.Value); else nss.Add ("", Reader.Value); } } else if (anyAttrMember != null) { XmlAttribute attr = (XmlAttribute) Document.ReadNode(Reader); ParseWsdlArrayType (attr); AddListValue (anyAttrMember.TypeData, ref anyAttributeArray, anyAttributeIndex++, attr, true); } else ProcessUnknownAttribute(ob); } if (anyAttrMember != null) { anyAttributeArray = ShrinkArray ((Array)anyAttributeArray, anyAttributeIndex, anyAttrMember.TypeData.Type.GetElementType(), true); SetMemberValue (anyAttrMember, ob, anyAttributeArray, isValueList); } Reader.MoveToElement(); if (Reader.IsEmptyElement) return; Reader.ReadStartElement(); } // Reads elements bool[] readFlag = new bool[(map.ElementMembers != null) ? map.ElementMembers.Count : 0]; Reader.MoveToContent(); int[] indexes = null; object[] flatLists = null; Fixup fixup = null; int ind = 0; int maxInd; if (readByOrder) { if (map.ElementMembers != null) maxInd = map.ElementMembers.Count; else maxInd = 0; } else maxInd = int.MaxValue; if (map.FlatLists != null) { indexes = new int[map.FlatLists.Count]; flatLists = new object[map.FlatLists.Count]; foreach (XmlTypeMapMemberExpandable mem in map.FlatLists) if (IsReadOnly (mem, ob, isValueList)) flatLists[mem.FlatArrayIndex] = mem.GetValue (ob); } if (_format == SerializationFormat.Encoded && map.ElementMembers != null) { FixupCallbackInfo info = new FixupCallbackInfo (this, map, isValueList); fixup = new Fixup(ob, new XmlSerializationFixupCallback(info.FixupMembers), map.ElementMembers.Count); AddFixup (fixup); if (readByOrder) maxInd = map.ElementMembers.Count; } while (Reader.NodeType != System.Xml.XmlNodeType.EndElement && (ind < maxInd)) { if (Reader.NodeType == System.Xml.XmlNodeType.Element) { XmlTypeMapElementInfo info = !readByOrder ? map.GetElement (Reader.LocalName, Reader.NamespaceURI) : map.GetElement (ind++); if (info != null && !readFlag[info.Member.Index] ) { if (info.Member.GetType() == typeof (XmlTypeMapMemberList)) { if (_format == SerializationFormat.Encoded && info.MultiReferenceType) { object list = ReadReferencingElement (out fixup.Ids[info.Member.Index]); if (fixup.Ids[info.Member.Index] == null) // Already read { if (IsReadOnly (info.Member, ob, isValueList)) throw CreateReadOnlyCollectionException (info.TypeData.FullTypeName); else SetMemberValue (info.Member, ob, list, isValueList); } else if (!info.MappedType.TypeData.Type.IsArray) { if (IsReadOnly (info.Member, ob, isValueList)) list = GetMemberValue (info.Member, ob, isValueList); else { list = CreateList (info.MappedType.TypeData.Type); SetMemberValue (info.Member, ob, list, isValueList); } AddFixup (new CollectionFixup (list, new XmlSerializationCollectionFixupCallback (FillList), fixup.Ids[info.Member.Index])); fixup.Ids[info.Member.Index] = null; // The member already has the value, no further fix needed. } } else { if (IsReadOnly (info.Member, ob, isValueList)) ReadListElement (info.MappedType, info.IsNullable, GetMemberValue (info.Member, ob, isValueList), false); else SetMemberValue (info.Member, ob, ReadListElement (info.MappedType, info.IsNullable, null, true), isValueList); } readFlag[info.Member.Index] = true; } else if (info.Member.GetType() == typeof (XmlTypeMapMemberFlatList)) { XmlTypeMapMemberFlatList mem = (XmlTypeMapMemberFlatList)info.Member; AddListValue (mem.TypeData, ref flatLists[mem.FlatArrayIndex], indexes[mem.FlatArrayIndex]++, ReadObjectElement (info), !IsReadOnly (info.Member, ob, isValueList)); } else if (info.Member.GetType() == typeof (XmlTypeMapMemberAnyElement)) { XmlTypeMapMemberAnyElement mem = (XmlTypeMapMemberAnyElement)info.Member; if (mem.TypeData.IsListType) AddListValue (mem.TypeData, ref flatLists[mem.FlatArrayIndex], indexes[mem.FlatArrayIndex]++, ReadXmlNode (false), true); else SetMemberValue (mem, ob, ReadXmlNode (false), isValueList); } else if (info.Member.GetType() == typeof(XmlTypeMapMemberElement)) { object val; readFlag[info.Member.Index] = true; if (_format == SerializationFormat.Encoded && info.MultiReferenceType) { val = ReadReferencingElement (out fixup.Ids[info.Member.Index]); if (fixup.Ids[info.Member.Index] == null) // already read SetMemberValue (info.Member, ob, val, isValueList); } else SetMemberValue (info.Member, ob, ReadObjectElement (info), isValueList); } else throw new InvalidOperationException ("Unknown member type"); } else if (map.DefaultAnyElementMember != null) { XmlTypeMapMemberAnyElement mem = map.DefaultAnyElementMember; if (mem.TypeData.IsListType) AddListValue (mem.TypeData, ref flatLists[mem.FlatArrayIndex], indexes[mem.FlatArrayIndex]++, ReadXmlNode (false), true); else SetMemberValue (mem, ob, ReadXmlNode (false), isValueList); } else ProcessUnknownElement(ob); } else if (Reader.NodeType == System.Xml.XmlNodeType.Text && map.XmlTextCollector != null) { if (map.XmlTextCollector is XmlTypeMapMemberExpandable) { XmlTypeMapMemberExpandable mem = (XmlTypeMapMemberExpandable)map.XmlTextCollector; XmlTypeMapMemberFlatList flatl = mem as XmlTypeMapMemberFlatList; Type itype = (flatl == null) ? mem.TypeData.ListItemType : flatl.ListMap.FindTextElement().TypeData.Type; object val = (itype == typeof (string)) ? (object) Reader.ReadString() : (object) ReadXmlNode (false); AddListValue (mem.TypeData, ref flatLists[mem.FlatArrayIndex], indexes[mem.FlatArrayIndex]++, val, true); } else { XmlTypeMapMemberElement mem = (XmlTypeMapMemberElement) map.XmlTextCollector; XmlTypeMapElementInfo info = (XmlTypeMapElementInfo) mem.ElementInfo [0]; if (info.TypeData.Type == typeof (string)) SetMemberValue (mem, ob, ReadString ((string) GetMemberValue (mem, ob, isValueList)), isValueList); else SetMemberValue (mem, ob, GetValueFromXmlString (Reader.ReadString(), info.TypeData, info.MappedType), isValueList); } } else UnknownNode(ob); Reader.MoveToContent(); } if (flatLists != null) { foreach (XmlTypeMapMemberExpandable mem in map.FlatLists) { Object list = flatLists[mem.FlatArrayIndex]; if (mem.TypeData.Type.IsArray) list = ShrinkArray ((Array)list, indexes[mem.FlatArrayIndex], mem.TypeData.Type.GetElementType(), true); if (!IsReadOnly (mem, ob, isValueList)) SetMemberValue (mem, ob, list, isValueList); } } } internal void FixupMembers (ClassMap map, object obfixup, bool isValueList) { Fixup fixup = (Fixup)obfixup; ICollection members = map.ElementMembers; string[] ids = fixup.Ids; foreach (XmlTypeMapMember member in members) { if (ids[member.Index] != null) SetMemberValue (member, fixup.Source, GetTarget(ids[member.Index]), isValueList); } } protected virtual void ProcessUnknownAttribute (object target) { UnknownNode (target); } protected virtual void ProcessUnknownElement (object target) { UnknownNode (target); } bool IsReadOnly (XmlTypeMapMember member, object ob, bool isValueList) { if (isValueList) return false; else return member.IsReadOnly (ob.GetType()); } void SetMemberValue (XmlTypeMapMember member, object ob, object value, bool isValueList) { if (isValueList) ((object[])ob)[member.Index] = value; else { member.SetValue (ob, value); if (member.IsOptionalValueType) member.SetValueSpecified (ob, true); } } object GetMemberValue (XmlTypeMapMember member, object ob, bool isValueList) { if (isValueList) return ((object[])ob)[member.Index]; else return member.GetValue (ob); } object ReadObjectElement (XmlTypeMapElementInfo elem) { switch (elem.TypeData.SchemaType) { case SchemaTypes.XmlNode: return ReadXmlNode (true); case SchemaTypes.Primitive: case SchemaTypes.Enum: return ReadPrimitiveValue (elem); case SchemaTypes.Array: return ReadListElement (elem.MappedType, elem.IsNullable, null, true); case SchemaTypes.Class: return ReadObject (elem.MappedType, elem.IsNullable, true); case SchemaTypes.XmlSerializable: object ob = Activator.CreateInstance (elem.TypeData.Type); return ReadSerializable ((IXmlSerializable)ob); default: throw new NotSupportedException ("Invalid value type"); } } object ReadPrimitiveValue (XmlTypeMapElementInfo elem) { if (elem.TypeData.Type == typeof (XmlQualifiedName)) { if (elem.IsNullable) return ReadNullableQualifiedName (); else return ReadElementQualifiedName (); } else if (elem.IsNullable) return GetValueFromXmlString (ReadNullableString (), elem.TypeData, elem.MappedType); else return GetValueFromXmlString (Reader.ReadElementString (), elem.TypeData, elem.MappedType); } object GetValueFromXmlString (string value, TypeData typeData, XmlTypeMapping typeMap) { if (typeData.SchemaType == SchemaTypes.Array) return ReadListString (typeMap, value); else if (typeData.SchemaType == SchemaTypes.Enum) return GetEnumValue (typeMap, value); else if (typeData.Type == typeof (XmlQualifiedName)) return ToXmlQualifiedName (value); else return XmlCustomFormatter.FromXmlString (typeData, value); } object ReadListElement (XmlTypeMapping typeMap, bool isNullable, object list, bool canCreateInstance) { Type listType = typeMap.TypeData.Type; ListMap listMap = (ListMap)typeMap.ObjectMap; if (ReadNull()) return null; if (list == null) { if (canCreateInstance) list = CreateList (listType); else throw CreateReadOnlyCollectionException (typeMap.TypeFullName); } if (Reader.IsEmptyElement) { Reader.Skip(); if (listType.IsArray) list = ShrinkArray ((Array)list, 0, listType.GetElementType(), isNullable); return list; } int index = 0; Reader.ReadStartElement(); Reader.MoveToContent(); while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) { if (Reader.NodeType == System.Xml.XmlNodeType.Element) { XmlTypeMapElementInfo elemInfo = listMap.FindElement (Reader.LocalName, Reader.NamespaceURI); if (elemInfo != null) AddListValue (typeMap.TypeData, ref list, index++, ReadObjectElement (elemInfo), false); else UnknownNode(null); } else UnknownNode(null); Reader.MoveToContent(); } ReadEndElement(); if (listType.IsArray) list = ShrinkArray ((Array)list, index, listType.GetElementType(), isNullable); return list; } object ReadListString (XmlTypeMapping typeMap, string values) { Type listType = typeMap.TypeData.Type; ListMap listMap = (ListMap)typeMap.ObjectMap; values = values.Trim (); if (values == string.Empty) { return Array.CreateInstance (listType.GetElementType(), 0); } string[] valueArray = values.Split (' '); Array list = Array.CreateInstance (listType.GetElementType(), valueArray.Length); XmlTypeMapElementInfo info = (XmlTypeMapElementInfo)listMap.ItemInfo[0]; for (int index = 0; index < valueArray.Length; index++) list.SetValue (GetValueFromXmlString (valueArray[index], info.TypeData, info.MappedType), index); return list; } void AddListValue (TypeData listType, ref object list, int index, object value, bool canCreateInstance) { Type type = listType.Type; if (type.IsArray) { list = EnsureArrayIndex ((Array)list, index, type.GetElementType()); ((Array)list).SetValue (value, index); } else // Must be IEnumerable { if (list == null) { if (canCreateInstance) list = Activator.CreateInstance (type); else throw CreateReadOnlyCollectionException (type.FullName); } MethodInfo mi = type.GetMethod ("Add", new Type[] {listType.ListItemType} ); mi.Invoke (list, new object[] { value }); } } object CreateList (Type listType) { if (listType.IsArray) return EnsureArrayIndex (null, 0, listType.GetElementType()); else return Activator.CreateInstance (listType); } void FillList (object list, object items) { CopyEnumerableList (items, list); } void CopyEnumerableList (object source, object dest) { if (dest == null) throw CreateReadOnlyCollectionException (source.GetType().FullName); object[] param = new object[1]; MethodInfo mi = dest.GetType().GetMethod ("Add"); foreach (object ob in (IEnumerable)source) { param[0] = ob; mi.Invoke (dest, param); } } int GetListCount (TypeData listType, object ob) { if (listType.Type.IsArray) return ((Array)ob).Length; else return (int) listType.Type.GetProperty ("Count").GetValue(ob,null); } object ReadXmlNodeElement (XmlTypeMapping typeMap, bool isNullable) { return ReadXmlNode (false); } object ReadPrimitiveElement (XmlTypeMapping typeMap, bool isNullable) { XmlQualifiedName t = GetXsiType(); if (t == null) t = new XmlQualifiedName (typeMap.XmlType, typeMap.Namespace); return ReadTypedPrimitive (t); } object ReadEnumElement (XmlTypeMapping typeMap, bool isNullable) { Reader.ReadStartElement (); object o = GetEnumValue (typeMap, Reader.ReadString()); Reader.ReadEndElement (); return o; } object GetEnumValue (XmlTypeMapping typeMap, string val) { EnumMap map = (EnumMap) typeMap.ObjectMap; string ev = map.GetEnumName (val); if (ev == null) throw CreateUnknownConstantException (val, typeMap.TypeData.Type); return Enum.Parse (typeMap.TypeData.Type, ev); } object ReadXmlSerializableElement (XmlTypeMapping typeMap, bool isNullable) { Reader.MoveToContent (); if (Reader.NodeType == XmlNodeType.Element) { if (Reader.LocalName == typeMap.ElementName && Reader.NamespaceURI == typeMap.Namespace) { object ob = Activator.CreateInstance (typeMap.TypeData.Type); return ReadSerializable ((IXmlSerializable)ob); } else throw CreateUnknownNodeException (); } else { UnknownNode (null); return null; } } class FixupCallbackInfo { XmlSerializationReaderInterpreter _sri; ClassMap _map; bool _isValueList; public FixupCallbackInfo (XmlSerializationReaderInterpreter sri, ClassMap map, bool isValueList) { _sri = sri; _map = map; _isValueList = isValueList; } public void FixupMembers (object fixup) { _sri.FixupMembers (_map, fixup, _isValueList); } } class ReaderCallbackInfo { XmlSerializationReaderInterpreter _sri; XmlTypeMapping _typeMap; public ReaderCallbackInfo (XmlSerializationReaderInterpreter sri, XmlTypeMapping typeMap) { _sri = sri; _typeMap = typeMap; } internal object ReadObject () { return _sri.ReadObject (_typeMap, true, true); } } } }