// // DataContractSerializer.cs // // Author: // Atsushi Enomoto // // Copyright (C) 2005-2007 Novell, Inc. http://www.novell.com // // 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. // #if NET_2_0 using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Reflection; using System.Runtime.Serialization.Formatters.Binary; using System.Xml; using System.Xml.Schema; using QName = System.Xml.XmlQualifiedName; namespace System.Runtime.Serialization { public sealed class DataContractSerializer : XmlObjectSerializer { const string xmlns = "http://www.w3.org/2000/xmlns/"; Type type; bool ignore_ext, preserve_refs; // This is only for compatible mode. StreamingContext context; ReadOnlyCollection known_runtime_types; KnownTypeCollection known_types; IDataContractSurrogate surrogate; int max_items = 0x10000; // FIXME: could be from config. bool names_filled; XmlDictionaryString root_name, root_ns; public DataContractSerializer (Type type) : this (type, Type.EmptyTypes) { // nothing to do here. } public DataContractSerializer (Type type, IEnumerable knownTypes) { if (type == null) throw new ArgumentNullException ("type"); this.type = type; known_types = new KnownTypeCollection (); PopulateTypes (knownTypes); known_types.TryRegister (type); QName qname = known_types.GetQName (type); FillDictionaryString (qname.Name, qname.Namespace); } public DataContractSerializer (Type type, string rootName, string rootNamespace) : this (type, rootName, rootNamespace, Type.EmptyTypes) { // nothing to do here. } public DataContractSerializer (Type type, XmlDictionaryString rootName, XmlDictionaryString rootNamespace) : this (type, rootName, rootNamespace, Type.EmptyTypes) { // nothing to do here. } public DataContractSerializer (Type type, string rootName, string rootNamespace, IEnumerable knownTypes) { if (type == null) throw new ArgumentNullException ("type"); if (rootName == null) throw new ArgumentNullException ("rootName"); if (rootNamespace == null) throw new ArgumentNullException ("rootNamespace"); this.type = type; PopulateTypes (knownTypes); FillDictionaryString (rootName, rootNamespace); } public DataContractSerializer (Type type, XmlDictionaryString rootName, XmlDictionaryString rootNamespace, IEnumerable knownTypes) { if (type == null) throw new ArgumentNullException ("type"); if (rootName == null) throw new ArgumentNullException ("rootName"); if (rootNamespace == null) throw new ArgumentNullException ("rootNamespace"); this.type = type; PopulateTypes (knownTypes); root_name = rootName; root_ns = rootNamespace; } public DataContractSerializer (Type type, IEnumerable knownTypes, int maxObjectsInGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences, IDataContractSurrogate dataContractSurrogate) : this (type, knownTypes) { Initialize (maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate); } public DataContractSerializer (Type type, string rootName, string rootNamespace, IEnumerable knownTypes, int maxObjectsInGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences, IDataContractSurrogate dataContractSurrogate) : this (type, rootName, rootNamespace, knownTypes) { Initialize (maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate); } public DataContractSerializer (Type type, XmlDictionaryString rootName, XmlDictionaryString rootNamespace, IEnumerable knownTypes, int maxObjectsInGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences, IDataContractSurrogate dataContractSurrogate) : this (type, rootName, rootNamespace, knownTypes) { Initialize (maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate); } void PopulateTypes (IEnumerable knownTypes) { if (known_types == null) known_types= new KnownTypeCollection (); if (knownTypes != null) { foreach (Type t in knownTypes) known_types.TryRegister (t); } Type elementType = type; if (type.HasElementType) elementType = type.GetElementType (); /* Get all KnownTypeAttribute-s, including inherited ones */ object [] attrs = elementType.GetCustomAttributes (typeof (KnownTypeAttribute), true); for (int i = 0; i < attrs.Length; i ++) { KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i]; known_types.TryRegister (kt.Type); } } void FillDictionaryString (string name, string ns) { XmlDictionary d = new XmlDictionary (); root_name = d.Add (name); root_ns = d.Add (ns); names_filled = true; } void Initialize ( int maxObjectsInGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences, IDataContractSurrogate dataContractSurrogate) { if (maxObjectsInGraph < 0) throw new ArgumentOutOfRangeException ("maxObjectsInGraph must not be negative."); max_items = maxObjectsInGraph; ignore_ext = ignoreExtensionDataObject; preserve_refs = preserveObjectReferences; surrogate = dataContractSurrogate; PopulateTypes (Type.EmptyTypes); } public bool IgnoreExtensionDataObject { get { return ignore_ext; } } public ReadOnlyCollection KnownTypes { get { return known_runtime_types; } } public IDataContractSurrogate DataContractSurrogate { get { return surrogate; } } public int MaxItemsInObjectGraph { get { return max_items; } } public bool PreserveObjectReferences { get { return preserve_refs; } } [MonoTODO] public override bool IsStartObject (XmlDictionaryReader reader) { throw new NotImplementedException (); } // SP1 public override bool IsStartObject (XmlReader reader) { return IsStartObject (XmlDictionaryReader.CreateDictionaryReader (reader)); } // SP1 public override object ReadObject (XmlReader reader) { return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader)); } public override object ReadObject (XmlReader reader, bool verifyObjectName) { return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader), verifyObjectName); } [MonoTODO] public override object ReadObject (XmlDictionaryReader reader, bool verifyObjectName) { int startTypeCount = known_types.Count; known_types.Add (type); bool isEmpty = reader.IsEmptyElement; object ret = XmlFormatterDeserializer.Deserialize (reader, type, known_types, surrogate, root_name.Value, root_ns.Value, verifyObjectName); // remove temporarily-added known types for // rootType and object graph type. while (known_types.Count > startTypeCount) known_types.RemoveAt (startTypeCount); return ret; } private void ReadRootStartElement (XmlReader reader, Type type) { SerializationMap map = known_types.FindUserMap (type); QName name = map != null ? map.XmlName : KnownTypeCollection.GetPredefinedTypeName (type); reader.MoveToContent (); reader.ReadStartElement (name.Name, name.Namespace); // FIXME: could there be any attributes to handle here? reader.Read (); } // SP1 public override void WriteObject (XmlWriter writer, object graph) { XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer); WriteObject (w, graph); } [MonoTODO ("support arrays; support Serializable; support SharedType; use DataContractSurrogate")] /* when writeContentOnly is true, then the input XmlWriter must be at element state. This is to write possible xsi:nil. rootType determines the top-level element QName (thus it is ignored when writeContentOnly is true). preserveObjectReferences indicates that whether the output should contain ms:Id or not. (http://schemas.microsoft.com/2003/10/Serialization/) */ public override void WriteObjectContent (XmlDictionaryWriter writer, object graph) { if (graph == null) return; int startTypeCount = known_types.Count; XmlFormatterSerializer.Serialize (writer, graph, known_types, ignore_ext, max_items, root_ns.Value); // remove temporarily-added known types for // rootType and object graph type. while (known_types.Count > startTypeCount) known_types.RemoveAt (startTypeCount); } public override void WriteObjectContent (XmlWriter writer, object graph) { XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer); WriteObjectContent (w, graph); } // SP1 public override void WriteStartObject ( XmlWriter writer, object graph) { WriteStartObject (XmlDictionaryWriter.CreateDictionaryWriter (writer), graph); } public override void WriteStartObject ( XmlDictionaryWriter writer, object graph) { Type rootType = type; if (root_name.Value == "") throw new InvalidDataContractException ("Type '" + type.ToString () + "' cannot have a DataContract attribute Name set to null or empty string."); if (graph == null) { if (names_filled) writer.WriteStartElement (root_name.Value, root_ns.Value); else writer.WriteStartElement (root_name, root_ns); writer.WriteAttributeString ("i", "nil", XmlSchema.InstanceNamespace, "true"); return; } QName instName = null; QName root_qname = known_types.GetQName (rootType); QName graph_qname = known_types.GetQName (graph.GetType ()); known_types.Add (graph.GetType ()); if (names_filled) writer.WriteStartElement (root_name.Value, root_ns.Value); else writer.WriteStartElement (root_name, root_ns); if (root_ns.Value != root_qname.Namespace) if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace) writer.WriteXmlnsAttribute (null, root_qname.Namespace); if (root_qname == graph_qname) { if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace && !rootType.IsEnum) //FIXME: Hack, when should the "i:type" be written? //Not used in case of enums writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace); return; } /* Different names */ known_types.Add (rootType); instName = KnownTypeCollection.GetPredefinedTypeName (graph.GetType ()); if (instName == QName.Empty) /* Not a primitive type */ instName = graph_qname; else /* FIXME: Hack, .. see test WriteObject7 () */ instName = new QName (instName.Name, XmlSchema.Namespace); // output xsi:type as rootType is not equivalent to the graph's type. writer.WriteStartAttribute ("i", "type", XmlSchema.InstanceNamespace); writer.WriteQualifiedName (instName.Name, instName.Namespace); writer.WriteEndAttribute (); } public override void WriteEndObject (XmlDictionaryWriter writer) { writer.WriteEndElement (); } // SP1 public override void WriteEndObject (XmlWriter writer) { WriteEndObject (XmlDictionaryWriter.CreateDictionaryWriter (writer)); } } } #endif