Author: atsushi
Date: 2008-01-24 05:14:47 -0500 (Thu, 24 Jan 2008)
New Revision: 93778
Added:
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationReader.cs
Modified:
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/ChangeLog
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/DataContractJsonSerializer.cs
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/TypeMap.cs
trunk/olive/class/System.ServiceModel.Web/System.ServiceModel.Web.dll.sources
trunk/olive/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/ChangeLog
trunk/olive/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/DataContractJsonSerializerTest.cs
Log:
2008-01-24 Atsushi Enomoto <[EMAIL PROTECTED]>
* JsonSerializationReader.cs : new, for JSON deserialization support.
* DataContractJsonSerializer.cs, TypeMap.cs :
basic support for deserialization.
* DataContractJsonSerializerTest.cs : some more deserialization tests.
Modified:
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/ChangeLog
===================================================================
---
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/ChangeLog
2008-01-24 10:00:01 UTC (rev 93777)
+++
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/ChangeLog
2008-01-24 10:14:47 UTC (rev 93778)
@@ -1,5 +1,11 @@
2008-01-24 Atsushi Enomoto <[EMAIL PROTECTED]>
+ * JsonSerializationReader.cs : new, for JSON deserialization support.
+ * DataContractJsonSerializer.cs, TypeMap.cs :
+ basic support for deserialization.
+
+2008-01-24 Atsushi Enomoto <[EMAIL PROTECTED]>
+
* DataContractJsonSerializer.cs,
TypeMap.cs,
JsonSerializationWriter.cs : split the first to those three files.
Modified:
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/DataContractJsonSerializer.cs
===================================================================
---
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/DataContractJsonSerializer.cs
2008-01-24 10:00:01 UTC (rev 93777)
+++
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/DataContractJsonSerializer.cs
2008-01-24 10:14:47 UTC (rev 93778)
@@ -78,7 +78,6 @@
{
}
- [MonoTODO]
public DataContractJsonSerializer (Type type, string rootName,
IEnumerable<Type> knownTypes, int maxItemsInObjectGraph, bool
ignoreExtensionDataObject, IDataContractSurrogate dataContractSurrogate, bool
alwaysEmitTypeInformation)
{
if (type == null)
@@ -169,7 +168,6 @@
return ReadObject ((XmlReader) reader,
verifyObjectName);
}
- [MonoTODO]
public override object ReadObject (XmlReader reader, bool
verifyObjectName)
{
if (reader == null)
@@ -177,7 +175,13 @@
if (verifyObjectName && !IsStartObject (reader))
throw new SerializationException (String.Format
("Expected element was '{0}', but the actual input element was '{1}' in
namespace '{2}'", root, reader.LocalName, reader.NamespaceURI));
- throw new NotImplementedException ();
+ try {
+ return new JsonSerializationReader (this,
reader, type, verifyObjectName).ReadRoot ();
+ } catch (SerializationException) {
+ throw;
+ } catch (Exception ex) {
+ throw new SerializationException
("Deserialization has failed", ex);
+ }
}
public override void WriteObject (Stream stream, object graph)
@@ -222,7 +226,6 @@
WriteObjectContent ((XmlWriter) writer, graph);
}
- [MonoTODO]
public override void WriteObjectContent (XmlWriter writer,
object graph)
{
new JsonSerializationWriter (this, writer, type,
always_emit_type).WriteObjectContent (graph, true, false);
Added:
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationReader.cs
===================================================================
---
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationReader.cs
2008-01-24 10:00:01 UTC (rev 93777)
+++
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationReader.cs
2008-01-24 10:14:47 UTC (rev 93778)
@@ -0,0 +1,244 @@
+//
+// JsonSerializationReader.cs
+//
+// Author:
+// Atsushi Enomoto <[EMAIL PROTECTED]>
+//
+// Copyright (C) 2008 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.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Globalization;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using System.Xml;
+
+namespace System.Runtime.Serialization.Json
+{
+ class JsonSerializationReader
+ {
+ DataContractJsonSerializer serializer;
+ XmlReader reader;
+ int serialized_object_count;
+ bool verify_object_name;
+ Dictionary<Type, TypeMap> typemaps = new Dictionary<Type,
TypeMap> ();
+ Type root_type;
+
+ public JsonSerializationReader (DataContractJsonSerializer
serializer, XmlReader reader, Type rootType, bool verifyObjectName)
+ {
+ this.serializer = serializer;
+ this.reader = reader;
+ this.root_type = rootType;
+ this.verify_object_name = verifyObjectName;
+ }
+
+ public XmlReader Reader {
+ get { return reader; }
+ }
+
+ public object ReadRoot ()
+ {
+ TypeMap rootMap = GetTypeMap (root_type);
+
+ object v = ReadObject (root_type);
+ return v;
+ }
+
+ public object ReadObject (Type type)
+ {
+ if (serialized_object_count ++ ==
serializer.MaxItemsInObjectGraph)
+ throw new SerializationException (String.Format
("The object graph exceeded the maximum object count '{0}' specified in the
serializer", serializer.MaxItemsInObjectGraph));
+
+ switch (Type.GetTypeCode (type)) {
+ case TypeCode.String:
+ return reader.ReadElementContentAsString ();
+ case TypeCode.Single:
+ return reader.ReadElementContentAsFloat ();
+ case TypeCode.Double:
+ return reader.ReadElementContentAsDouble ();
+ case TypeCode.Decimal:
+ return reader.ReadElementContentAsDecimal ();
+ case TypeCode.Byte:
+ case TypeCode.SByte:
+ case TypeCode.Int16:
+ case TypeCode.Int32:
+ case TypeCode.UInt16:
+ case TypeCode.UInt32:
+ int i = reader.ReadElementContentAsInt ();
+ if (type.IsEnum)
+ return Enum.ToObject (type, i);
+ else
+ return Convert.ChangeType (i, type);
+ case TypeCode.Int64:
+ case TypeCode.UInt64:
+ long l = reader.ReadElementContentAsLong ();
+ if (type.IsEnum)
+ return Enum.ToObject (type, l);
+ else
+ return Convert.ChangeType (l, type);
+ case TypeCode.Boolean:
+ return reader.ReadElementContentAsBoolean ();
+ default:
+ if (type == typeof (Guid)) {
+ return new Guid
(reader.ReadElementContentAsString ());
+ } else if (type == typeof (Uri)) {
+ return new Uri
(reader.ReadElementContentAsString ());
+ } else if (type == typeof (XmlQualifiedName)) {
+ string s =
reader.ReadElementContentAsString ();
+ int idx = s.IndexOf (':');
+ return idx < 0 ? new XmlQualifiedName
(s) : new XmlQualifiedName (s.Substring (0, idx), s.Substring (idx + 1));
+ } else if (type != typeof (object)) {
+ // strongly-typed object
+ if (reader.IsEmptyElement) {
+ // empty -> null array or object
+ reader.Read ();
+ return null;
+ }
+
+ Type ct = GetCollectionType (type);
+ if (ct != null) {
+ return
DeserializeGenericCollection (type, ct);
+ } else {
+ TypeMap map = GetTypeMap (type);
+ return map.Deserialize (this);
+ }
+ }
+ else
+ return ReadInstanceDrivenObject ();
+ }
+ }
+
+ object ReadInstanceDrivenObject ()
+ {
+ string v = reader.ReadElementContentAsString ();
+ if (v.Length == 0)
+ return String.Empty;
+ if (v [0] == '"') {
+ if (v [v.Length - 1] != '"')
+ throw new SerializationException ("JSON
string should terminate with '\"'");
+ return v.Substring (1, v.Length - 2);
+ }
+ switch (v) {
+ case "true":
+ return true;
+ case "false":
+ return false;
+ case "null":
+ return null;
+ }
+ int i;
+ if (int.TryParse (v, NumberStyles.None,
CultureInfo.InvariantCulture, out i))
+ return i;
+ long l;
+ if (long.TryParse (v, NumberStyles.None,
CultureInfo.InvariantCulture, out l))
+ return l;
+ ulong ul;
+ if (ulong.TryParse (v, NumberStyles.None,
CultureInfo.InvariantCulture, out ul))
+ return ul;
+ double dbl;
+ if (double.TryParse (v, NumberStyles.None,
CultureInfo.InvariantCulture, out dbl))
+ return dbl;
+ decimal dec;
+ if (decimal.TryParse (v, NumberStyles.None,
CultureInfo.InvariantCulture, out dec))
+ return dec;
+ throw new SerializationException (String.Format
("Invalid JSON input: {0}", v));
+ }
+
+ string FormatTypeName (Type type)
+ {
+ return type.Namespace == null ? type.Name :
String.Format ("{0}:#{1}", type.Name, type.Namespace);
+ }
+
+ string ToRuntimeTypeName (string s)
+ {
+ int idx = s.IndexOf (":#", StringComparison.Ordinal);
+ return idx < 0 ? s : String.Concat (s.Substring (idx +
2), ".", s.Substring (0, idx));
+ }
+
+ Type GetCollectionType (Type type)
+ {
+ if (type.IsArray)
+ return type.GetElementType ();
+ if (type.IsGenericType) {
+ // returns T for ICollection<T>
+ Type gt = type.GetGenericTypeDefinition ();
+ if (gt == typeof (ICollection<>))
+ return type.GetGenericArguments () [0];
+ }
+ if (typeof (IList).IsAssignableFrom (type))
+ // return typeof(object) for mere collection.
+ return typeof (object);
+ else
+ return null;
+ }
+
+ object DeserializeGenericCollection (Type collectionType, Type
elementType)
+ {
+ reader.ReadStartElement ();
+ object ret;
+ if (typeof (IList).IsAssignableFrom (collectionType)) {
+ IList c = collectionType.IsArray ?
+ new ArrayList () :
+ (IList) Activator.CreateInstance
(collectionType);
+ for (reader.MoveToContent (); reader.NodeType
!= XmlNodeType.EndElement; reader.MoveToContent ()) {
+ if (!reader.IsStartElement ("item"))
+ throw new
SerializationException (String.Format ("Expected element 'item', but found
'{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
+ Type et = elementType == typeof
(object) || elementType.IsAbstract ? null : elementType;
+ if (et == null) {
+ string typeName =
reader.GetAttribute ("__type");
+ if (typeName != null)
+ et = Type.GetType
(ToRuntimeTypeName (typeName), false);
+ }
+ object elem = ReadObject (et ?? typeof
(object));
+ c.Add (elem);
+ }
+ ret = collectionType.IsArray ? ((ArrayList)
c).ToArray (elementType) : c;
+ } else {
+ object c = Activator.CreateInstance
(collectionType);
+ MethodInfo add = collectionType.GetMethod
("Add", new Type [] {elementType});
+ for (reader.MoveToContent (); reader.NodeType
!= XmlNodeType.EndElement; reader.MoveToContent ()) {
+ if (!reader.IsStartElement ("item"))
+ throw new
SerializationException (String.Format ("Expected element 'item', but found
'{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
+ object elem = ReadObject (elementType);
+ add.Invoke (c, new object [] {elem});
+ }
+ ret = c;
+ }
+
+ reader.ReadEndElement ();
+ return ret;
+ }
+
+ TypeMap GetTypeMap (Type type)
+ {
+ TypeMap map;
+ if (!typemaps.TryGetValue (type, out map)) {
+ map = TypeMap.CreateTypeMap (type);
+ typemaps [type] = map;
+ }
+ return map;
+ }
+ }
+}
Property changes on:
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JsonSerializationReader.cs
___________________________________________________________________
Name: svn:eol-style
+ native
Modified:
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/TypeMap.cs
===================================================================
---
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/TypeMap.cs
2008-01-24 10:00:01 UTC (rev 93777)
+++
trunk/olive/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/TypeMap.cs
2008-01-24 10:14:47 UTC (rev 93778)
@@ -124,6 +124,34 @@
outputter.Writer.WriteEndElement ();
}
}
+
+ public object Deserialize (JsonSerializationReader jsr)
+ {
+ XmlReader reader = jsr.Reader;
+
+ object ret = Activator.CreateInstance (type, true);
+ Dictionary<TypeMapMember,bool> filled = new
Dictionary<TypeMapMember,bool> ();
+
+ reader.ReadStartElement ();
+ for (reader.MoveToContent (); reader.NodeType !=
XmlNodeType.EndElement; reader.MoveToContent ()) {
+ bool consumed = false;
+ for (int i = 0; i < members.Length; i++) {
+ TypeMapMember mm = members [i];
+ if (mm.Name == reader.LocalName &&
reader.NamespaceURI == String.Empty) {
+ if (filled.ContainsKey (mm))
+ throw new
SerializationException (String.Format ("Object content '{0}' for '{1}' already
appeared in the reader", reader.LocalName, type));
+ mm.SetMemberValue (ret,
jsr.ReadObject (mm.Type));
+ filled [mm] = true;
+ consumed = true;
+ break;
+ }
+ }
+ if (!consumed)
+ reader.Skip ();
+ }
+ reader.ReadEndElement ();
+ return ret;
+ }
}
abstract class TypeMapMember
@@ -154,7 +182,11 @@
get { return dma != null ? dma.Order : -1; }
}
+ public abstract Type Type { get; }
+
public abstract object GetMemberOf (object owner);
+
+ public abstract void SetMemberValue (object owner, object
value);
}
class TypeMapField : TypeMapMember
@@ -167,10 +199,19 @@
this.field = fi;
}
+ public override Type Type {
+ get { return field.FieldType; }
+ }
+
public override object GetMemberOf (object owner)
{
return field.GetValue (owner);
}
+
+ public override void SetMemberValue (object owner, object value)
+ {
+ field.SetValue (owner, value);
+ }
}
class TypeMapProperty : TypeMapMember
@@ -183,9 +224,18 @@
this.property = pi;
}
+ public override Type Type {
+ get { return property.PropertyType; }
+ }
+
public override object GetMemberOf (object owner)
{
return property.GetValue (owner, null);
}
+
+ public override void SetMemberValue (object owner, object value)
+ {
+ property.SetValue (owner, value, null);
+ }
}
}
Modified:
trunk/olive/class/System.ServiceModel.Web/System.ServiceModel.Web.dll.sources
===================================================================
---
trunk/olive/class/System.ServiceModel.Web/System.ServiceModel.Web.dll.sources
2008-01-24 10:00:01 UTC (rev 93777)
+++
trunk/olive/class/System.ServiceModel.Web/System.ServiceModel.Web.dll.sources
2008-01-24 10:14:47 UTC (rev 93778)
@@ -7,6 +7,7 @@
System.Runtime.Serialization.Json/IXmlJsonWriterInitializer.cs
System.Runtime.Serialization.Json/JsonReader.cs
System.Runtime.Serialization.Json/JsonReaderWriterFactory.cs
+System.Runtime.Serialization.Json/JsonSerializationReader.cs
System.Runtime.Serialization.Json/JsonSerializationWriter.cs
System.Runtime.Serialization.Json/JsonWriter.cs
System.Runtime.Serialization.Json/TypeMap.cs
Modified:
trunk/olive/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/ChangeLog
===================================================================
---
trunk/olive/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/ChangeLog
2008-01-24 10:00:01 UTC (rev 93777)
+++
trunk/olive/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/ChangeLog
2008-01-24 10:14:47 UTC (rev 93778)
@@ -1,3 +1,7 @@
+2008-01-24 Atsushi Enomoto <[EMAIL PROTECTED]>
+
+ * DataContractJsonSerializerTest.cs : some more deserialization tests.
+
2008-01-22 Atsushi Enomoto <[EMAIL PROTECTED]>
* JsonWriterTest.cs : added test for writing __type attribute.
Modified:
trunk/olive/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/DataContractJsonSerializerTest.cs
===================================================================
---
trunk/olive/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/DataContractJsonSerializerTest.cs
2008-01-24 10:00:01 UTC (rev 93777)
+++
trunk/olive/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/DataContractJsonSerializerTest.cs
2008-01-24 10:14:47 UTC (rev 93778)
@@ -923,7 +923,76 @@
}
[Test]
+ public void DeserializeString ()
+ {
+ Assert.AreEqual ("ABC", Deserialize ("\"ABC\"", typeof
(string)));
+ }
+
+ [Test]
+ public void DeserializeInt ()
+ {
+ Assert.AreEqual (5, Deserialize ("5", typeof (int)));
+ }
+
+ [Test]
+ public void DeserializeArray ()
+ {
+ int [] ret = (int []) Deserialize ("[5,6,7]", typeof
(int []));
+ Assert.AreEqual (5, ret [0], "#1");
+ Assert.AreEqual (6, ret [1], "#2");
+ Assert.AreEqual (7, ret [2], "#3");
+ }
+
+ [Test]
+ public void DeserializeArrayUntyped ()
+ {
+ object [] ret = (object []) Deserialize ("[5,6,7]",
typeof (object []));
+ Assert.AreEqual (5, ret [0], "#1");
+ Assert.AreEqual (6, ret [1], "#2");
+ Assert.AreEqual (7, ret [2], "#3");
+ }
+
+ [Test]
+ public void DeserializeMixedArray ()
+ {
+ object [] ret = (object []) Deserialize
("[5,\"6\",false]", typeof (object []));
+ Assert.AreEqual (5, ret [0], "#1");
+ Assert.AreEqual ("6", ret [1], "#2");
+ Assert.AreEqual (false, ret [2], "#3");
+ }
+
+ [Test]
[ExpectedException (typeof (SerializationException))]
+ public void DeserializeEmptyAsString ()
+ {
+ // it somehow expects "root" which should have been
already consumed.
+ Deserialize ("", typeof (string));
+ }
+
+ [Test]
+ [ExpectedException (typeof (SerializationException))]
+ public void DeserializeEmptyAsInt ()
+ {
+ // it somehow expects "root" which should have been
already consumed.
+ Deserialize ("", typeof (int));
+ }
+
+ [Test]
+ public void DeserializeEmptyObjectAsString ()
+ {
+ // looks like it is converted to ""
+ Assert.AreEqual (String.Empty, Deserialize ("{}",
typeof (string)));
+ }
+
+ [Test]
+ [ExpectedException (typeof (SerializationException))]
+ public void DeserializeEmptyObjectAsInt ()
+ {
+ Assert.AreEqual (0, Deserialize ("{}", typeof (int)));
+ }
+
+ [Test]
+ [ExpectedException (typeof (SerializationException))]
public void DeserializeEnumByName ()
{
// enum is parsed into long
_______________________________________________
Mono-patches maillist - [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches