System.Runtime.Serialization/CollectionDataContractAttribute.cs
System.Runtime.Serialization/ContractNamespaceAttribute.cs
System.Runtime.Serialization/DataContractAttribute.cs
+System.Runtime.Serialization/DataContractResolver.cs
System.Runtime.Serialization/DataContractSerializer.cs
System.Runtime.Serialization/DataMemberAttribute.cs
System.Runtime.Serialization/EnumMemberAttribute.cs
--- /dev/null
+//
+// DataContractResolver.cs
+//
+// Author:
+// Atsushi Enomoto <atsushi@ximian.com>
+//
+// Copyright (C) 2010 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.
+//
+
+using System;
+using System.Xml;
+
+namespace System.Runtime.Serialization
+{
+ // See http://msdn.microsoft.com/en-us/library/ee358759.aspx
+#if NET_4_0
+ [MonoTODO ("not in use yet")]
+ public
+#else
+ internal
+#endif
+ abstract class DataContractResolver
+ {
+ public abstract Type ResolveName (string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver);
+
+ public abstract bool TryResolveType (Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace);
+ }
+}
dataContractSurrogate);
}
+#if NET_4_0
+ public DataContractSerializer (Type type,
+ IEnumerable<Type> knownTypes,
+ int maxObjectsInGraph,
+ bool ignoreExtensionDataObject,
+ bool preserveObjectReferences,
+ IDataContractSurrogate dataContractSurrogate,
+ DataContractResolver dataContractResolver)
+ : this (type, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
+ {
+ DataContractResolver = dataContractResolver;
+ }
+
+ public DataContractSerializer (Type type,
+ string rootName,
+ string rootNamespace,
+ IEnumerable<Type> knownTypes,
+ int maxObjectsInGraph,
+ bool ignoreExtensionDataObject,
+ bool preserveObjectReferences,
+ IDataContractSurrogate dataContractSurrogate,
+ DataContractResolver dataContractResolver)
+ : this (type, rootName, rootNamespace, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
+ {
+ DataContractResolver = dataContractResolver;
+ }
+
+ public DataContractSerializer (Type type,
+ XmlDictionaryString rootName,
+ XmlDictionaryString rootNamespace,
+ IEnumerable<Type> knownTypes,
+ int maxObjectsInGraph,
+ bool ignoreExtensionDataObject,
+ bool preserveObjectReferences,
+ IDataContractSurrogate dataContractSurrogate,
+ DataContractResolver dataContractResolver)
+ : this (type, rootName, rootNamespace, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
+ {
+ DataContractResolver = dataContractResolver;
+ }
+#endif
+
void PopulateTypes (IEnumerable<Type> knownTypes)
{
if (known_types == null)
PopulateTypes (Type.EmptyTypes);
}
+#if NET_4_0
+ public
+#else
+ internal
+#endif
+ DataContractResolver DataContractResolver { get; private set; }
+
public bool IgnoreExtensionDataObject {
get { return ignore_ext; }
}
return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader), verifyObjectName);
}
- [MonoTODO]
public override object ReadObject (XmlDictionaryReader reader, bool verifyObjectName)
{
int startTypeCount = known_types.Count;
bool isEmpty = reader.IsEmptyElement;
object ret = XmlFormatterDeserializer.Deserialize (reader, type,
- known_types, surrogate, root_name.Value, root_ns.Value, verifyObjectName);
+ known_types, surrogate, DataContractResolver, root_name.Value, root_ns.Value, verifyObjectName);
// remove temporarily-added known types for
// rootType and object graph type.
return ret;
}
+#if NET_4_0
+ public object ReadObject (XmlDictionaryReader reader, bool verifyObjectName, DataContractResolver resolver)
+ {
+ var bak = DataContractResolver;
+ try {
+ DataContractResolver = resolver;
+ return ReadObject (reader, verifyObjectName);
+ } finally {
+ DataContractResolver = bak;
+ }
+ }
+#endif
+
private void ReadRootStartElement (XmlReader reader, Type type)
{
SerializationMap map =
WriteObject (w, graph);
}
+#if NET_4_0
+ public void WriteObject (XmlDictionaryWriter writer, object graph, DataContractResolver resolver)
+ {
+ var bak = DataContractResolver;
+ try {
+ DataContractResolver = resolver;
+ WriteObject (writer, graph);
+ } finally {
+ DataContractResolver = bak;
+ }
+ }
+#endif
+
[MonoTODO ("support arrays; support Serializable; support SharedType; use DataContractSurrogate")]
/*
when writeContentOnly is true, then the input XmlWriter
public QName XmlName { get; set; }
+ protected void HandleId (XmlReader reader, XmlFormatterDeserializer deserializer, object instance)
+ {
+ HandleId (reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace), deserializer, instance);
+ }
+
+ protected void HandleId (string id, XmlFormatterDeserializer deserializer, object instance)
+ {
+ if (id != null)
+ deserializer.References.Add (id, instance);
+ }
+
public CollectionDataContractAttribute GetCollectionDataContractAttribute (Type type)
{
object [] atts = type.GetCustomAttributes (
public virtual object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
{
bool isEmpty = reader.IsEmptyElement;
+ string id = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
reader.ReadStartElement ();
reader.MoveToContent ();
object res;
if (isEmpty)
- res = DeserializeEmptyContent (reader, deserializer);
+ res = DeserializeEmptyContent (reader, deserializer, id);
else
- res = DeserializeContent (reader, deserializer);
+ res = DeserializeContent (reader, deserializer, id);
reader.MoveToContent ();
if (!isEmpty && reader.NodeType == XmlNodeType.EndElement)
// This is sort of hack. The argument reader already moved ahead of
// the actual empty element.It's just for historical consistency.
public virtual object DeserializeEmptyContent (XmlReader reader,
- XmlFormatterDeserializer deserializer)
+ XmlFormatterDeserializer deserializer, string id)
{
- return DeserializeContent (reader, deserializer, true);
+ return DeserializeContent (reader, deserializer, id, true);
}
public virtual object DeserializeContent (XmlReader reader,
- XmlFormatterDeserializer deserializer)
+ XmlFormatterDeserializer deserializer, string id)
{
- return DeserializeContent (reader, deserializer, false);
+ return DeserializeContent (reader, deserializer, id, false);
}
- object DeserializeContent (XmlReader reader,
- XmlFormatterDeserializer deserializer, bool empty)
+ object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id, bool empty)
{
object instance = FormatterServices.GetUninitializedObject (RuntimeType);
+ HandleId (id, deserializer, instance);
if (OnDeserializing != null)
OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
#else
IXmlSerializable ixs = (IXmlSerializable) Activator.CreateInstance (RuntimeType, true);
#endif
+
+ HandleId (reader, deserializer, ixs);
+
ixs.ReadXml (reader);
return ixs;
}
#endif
}
- public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
+ public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
{
var instance = CreateInstance ();
+ HandleId (id, deserializer, instance);
if (OnDeserializing != null)
OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
try {
}
}
- public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer)
+ public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
{
object instance = CreateInstance ();
+ HandleId (id, deserializer, instance);
if (OnDeserializing != null)
OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
#endif
}
- public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
+ public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
{
- return DeserializeContent (reader, deserializer);
+ return DeserializeContent (reader, deserializer, id);
}
- public override object DeserializeContent(XmlReader reader, XmlFormatterDeserializer deserializer)
+ public override object DeserializeContent(XmlReader reader, XmlFormatterDeserializer deserializer, string id)
{
object instance = CreateInstance ();
+ HandleId (id, deserializer, instance);
int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
if (reader.IsEmptyElement)
"Enum value '{0}' is invalid for type '{1}' and cannot be serialized.", graph, RuntimeType));
}
- public override object DeserializeEmptyContent (XmlReader reader,
- XmlFormatterDeserializer deserializer)
+ public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
{
if (!flag_attr)
throw new SerializationException (String.Format ("Enum value '' is invalid for type '{0}' and cannot be deserialized.", RuntimeType));
- return Enum.ToObject (RuntimeType, 0);
+ var instance = Enum.ToObject (RuntimeType, 0);
+ HandleId (id, deserializer, instance);
+ return instance;
}
- public override object DeserializeContent (XmlReader reader,
- XmlFormatterDeserializer deserializer)
+ public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
{
string value = reader.NodeType != XmlNodeType.Text ? String.Empty : reader.ReadContentAsString ();
+ HandleId (id, deserializer, value);
if (value != String.Empty) {
foreach (EnumMemberInfo emi in enum_members)
{
KnownTypeCollection types;
IDataContractSurrogate surrogate;
+ DataContractResolver resolver; // new in 4.0.
// 3.5 SP1 supports deserialization by reference (id->obj).
// Though unlike XmlSerializer, it does not support forward-
// reference resolution i.e. a referenced object must appear
Hashtable references = new Hashtable ();
public static object Deserialize (XmlReader reader, Type type,
- KnownTypeCollection knownTypes, IDataContractSurrogate surrogate,
+ KnownTypeCollection knownTypes, IDataContractSurrogate surrogate, DataContractResolver resolver,
string name, string ns, bool verifyObjectName)
{
reader.MoveToContent ();
reader.NamespaceURI != ns)
throw new SerializationException (String.Format ("Expected element '{0}' in namespace '{1}', but found {2} node '{3}' in namespace '{4}'", name, ns, reader.NodeType, reader.LocalName, reader.NamespaceURI));
// Verify (knownTypes, type, name, ns, reader);
- return new XmlFormatterDeserializer (knownTypes, surrogate).Deserialize (type, reader);
+ return new XmlFormatterDeserializer (knownTypes, surrogate, resolver).Deserialize (type, reader);
}
// Verify the top element name and namespace.
private XmlFormatterDeserializer (
KnownTypeCollection knownTypes,
- IDataContractSurrogate surrogate)
+ IDataContractSurrogate surrogate,
+ DataContractResolver resolver)
{
this.types = knownTypes;
this.surrogate = surrogate;
+ this.resolver = resolver;
}
public Hashtable References {
get { return references; }
}
- // At the beginning phase, we still have to instantiate a new
- // target object even if fromContent is true.
+ // This method handles z:Ref, xsi:nil and primitive types, and then delegates to DeserializeByMap() for anything else.
public object Deserialize (Type type, XmlReader reader)
- {
- string label = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
- object o = DeserializeCore (type, reader);
-
- if (label != null)
- references.Add (label, o);
-
- return o;
- }
-
- public object DeserializeCore (Type type, XmlReader reader)
{
QName graph_qname = types.GetQName (type);
string itype = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
}
if (KnownTypeCollection.GetPrimitiveTypeFromName (graph_qname.Name) != null) {
+ string id = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
+
string value;
if (reader.IsEmptyElement) {
reader.Read (); // advance
}
else
value = reader.ReadElementContentAsString ();
- return KnownTypeCollection.PredefinedTypeStringToObject (value, graph_qname.Name, reader);
+ object ret = KnownTypeCollection.PredefinedTypeStringToObject (value, graph_qname.Name, reader);
+ if (id != null) {
+ if (references.ContainsKey (id))
+ throw new InvalidOperationException (String.Format ("Object with Id '{0}' already exists as '{1}'", id, references [id]));
+ references.Add (id, ret);
+ }
+ return ret;
}
return DeserializeByMap (graph_qname, type, reader);
Assert.IsTrue (idx2 < 0, "idx2 should not occur at " + idx2);
}
+ [Test]
+ public void AncestralReference ()
+ {
+ // Reference to Parent comes inside the Parent itself.
+ // In this case, adding reference after complete deserialization won't work (but it should).
+ var ms = new MemoryStream ();
+ Type [] knownTypes = new Type [] { typeof (ParentClass), typeof (Foo), typeof (Bar) };
+
+ var ds = new DataContractSerializer (typeof (Parent));
+
+ var org = new Parent ();
+ ds.WriteObject (ms, org);
+ string result = Encoding.UTF8.GetString (ms.ToArray ());
+ ms.Position = 0;
+
+ var parent = (Parent) ds.ReadObject (ms);
+
+ Assert.IsNotNull (parent.Child, "#1");
+ Assert.AreEqual (parent, parent.Child.Parent, "#2");
+ }
+
[Test]
public void IXmlSerializableCallConstructor ()
{
}
}
+
+[DataContract (IsReference = true)]
+public class Parent
+{
+ //constructor
+ public Parent ()
+ {
+ Child = new Child (this);
+ }
+
+ [DataMember]
+ public Child Child;
+}
+
+[DataContract]
+public class Child
+{
+ public Child ()
+ {
+ }
+
+ public Child (Parent parent)
+ {
+ this.Parent = parent;
+ }
+
+ [DataMember]
+ public Parent Parent;
+}
+
#endregion
System.Runtime.Serialization/CollectionDataContractAttribute.cs
System.Runtime.Serialization/ContractNamespaceAttribute.cs
System.Runtime.Serialization/DataContractAttribute.cs
+System.Runtime.Serialization/DataContractResolver.cs
System.Runtime.Serialization/DataContractSerializer.cs
System.Runtime.Serialization/DataMemberAttribute.cs
System.Runtime.Serialization/EnumMemberAttribute.cs
get { return format; }
}
+#if NET_4_0
+ public DataContractResolver DataContractResolver { get; set; }
+#endif
+
public bool IgnoreExtensionDataObject { get; set; }
public int MaxItemsInObjectGraph { get; set; }