#region License // Copyright 2006 James Newton-King // http://www.newtonsoft.com // // Copyright 2007 Konstantin Triger // // This work is licensed under the Creative Commons Attribution 2.5 License // http://creativecommons.org/licenses/by/2.5/ // // You are free: // * to copy, distribute, display, and perform the work // * to make derivative works // * to make commercial use of the work // // Under the following conditions: // * For any reuse or distribution, you must make clear to others the license terms of this work. // * Any of these conditions can be waived if you get permission from the copyright holder. #endregion using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Collections; using System.Reflection; using System.ComponentModel; using Newtonsoft.Json.Utilities; using System.Web.Script.Serialization; namespace Newtonsoft.Json { /// /// Specifies reference loop handling options for the . /// enum ReferenceLoopHandling { /// /// Throw a when a loop is encountered. /// Error = 0, /// /// Ignore loop references and do not serialize. /// Ignore = 1, /// /// Serialize loop references. /// Serialize = 2 } /// /// Serializes and deserializes objects into and from the Json format. /// The enables you to control how objects are encoded into Json. /// sealed class JsonSerializer { abstract class LazyDictionary : IDictionary { #region IDictionary Members void IDictionary.Add (string key, object value) { throw new NotSupportedException (); } bool IDictionary.ContainsKey (string key) { throw new NotSupportedException (); } ICollection IDictionary.Keys { get { throw new NotSupportedException (); } } bool IDictionary.Remove (string key) { throw new NotSupportedException (); } bool IDictionary.TryGetValue (string key, out object value) { throw new NotSupportedException (); } ICollection IDictionary.Values { get { throw new NotSupportedException (); } } object IDictionary.this [string key] { get { throw new NotSupportedException (); } set { throw new NotSupportedException (); } } #endregion #region ICollection> Members void ICollection>.Add (KeyValuePair item) { throw new NotSupportedException (); } void ICollection>.Clear () { throw new NotSupportedException (); } bool ICollection>.Contains (KeyValuePair item) { throw new NotSupportedException (); } void ICollection>.CopyTo (KeyValuePair [] array, int arrayIndex) { throw new NotSupportedException (); } int ICollection>.Count { get { throw new NotSupportedException (); } } bool ICollection>.IsReadOnly { get { throw new NotSupportedException (); } } bool ICollection>.Remove (KeyValuePair item) { throw new NotSupportedException (); } #endregion #region IEnumerable> Members IEnumerator> IEnumerable>.GetEnumerator () { return GetEnumerator (); } protected abstract IEnumerator> GetEnumerator (); #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () { return ((IEnumerable>) this).GetEnumerator (); } #endregion } sealed class DeserializerLazyDictionary : LazyDictionary { readonly JsonReader _reader; readonly JsonSerializer _serializer; public DeserializerLazyDictionary (JsonReader reader, JsonSerializer serializer) { _reader = reader; _serializer = serializer; } protected override IEnumerator> GetEnumerator () { return _serializer.PopulateObject (_reader); } } sealed class SerializerLazyDictionary : LazyDictionary { readonly object _source; public SerializerLazyDictionary (object source) { _source = source; } protected override IEnumerator> GetEnumerator () { foreach (MemberInfo member in ReflectionUtils.GetFieldsAndProperties (_source.GetType (), BindingFlags.Public | BindingFlags.Instance)) { if (ReflectionUtils.CanReadMemberValue (member) && !member.IsDefined (typeof (ScriptIgnoreAttribute), true)) if (!ReflectionUtils.IsIndexedProperty (member)) yield return new KeyValuePair (member.Name, ReflectionUtils.GetMemberValue (member, _source)); } } } private ReferenceLoopHandling _referenceLoopHandling; private int _level; readonly JavaScriptSerializer _context; /// /// Get or set how reference loops (e.g. a class referencing itself) is handled. /// public ReferenceLoopHandling ReferenceLoopHandling { get { return _referenceLoopHandling; } set { if (value < ReferenceLoopHandling.Error || value > ReferenceLoopHandling.Serialize) { throw new ArgumentOutOfRangeException("value"); } _referenceLoopHandling = value; } } /// /// Initializes a new instance of the class. /// public JsonSerializer(JavaScriptSerializer context) { _context = context; _referenceLoopHandling = ReferenceLoopHandling.Error; } #region Deserialize public object Deserialize (TextReader reader) { return Deserialize (new JsonReader (reader)); } /// /// Deserializes the Json structure contained by the specified /// into an instance of the specified type. /// /// The type of object to create. /// The of object being deserialized. /// The instance of being deserialized. object Deserialize (JsonReader reader) { if (!reader.Read()) return null; return GetObject(reader); } private object GetObject (JsonReader reader/*, Type objectType*/) { _level++; object value; switch (reader.TokenType) { // populate a typed object or generic dictionary/array // depending upon whether an objectType was supplied case JsonToken.StartObject: //value = PopulateObject(reader/*, objectType*/); value = new DeserializerLazyDictionary (reader, this); break; case JsonToken.StartArray: value = PopulateList (reader/*, objectType*/); break; case JsonToken.Integer: case JsonToken.Float: case JsonToken.String: case JsonToken.Boolean: case JsonToken.Date: //value = EnsureType(reader.Value, objectType); value = reader.Value; break; case JsonToken.Constructor: value = reader.Value.ToString (); break; case JsonToken.Null: case JsonToken.Undefined: value = null; break; default: throw new JsonSerializationException ("Unexpected token whil deserializing object: " + reader.TokenType); } _level--; return value; } private IEnumerable PopulateList(JsonReader reader/*, Type objectType*/) { while (reader.Read()) { switch (reader.TokenType) { case JsonToken.EndArray: yield break; case JsonToken.Comment: break; default: yield return GetObject(reader/*, elementType*/); break; } } throw new JsonSerializationException("Unexpected end when deserializing array."); } private IEnumerator> PopulateObject (JsonReader reader/*, Type objectType*/) { while (reader.Read()) { switch (reader.TokenType) { case JsonToken.PropertyName: string memberName = reader.Value.ToString(); if (!reader.Read ()) throw new JsonSerializationException (string.Format ("Unexpected end when setting {0}'s value.", memberName)); yield return new KeyValuePair (memberName, GetObject (reader)); break; case JsonToken.EndObject: yield break; default: throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); } } throw new JsonSerializationException("Unexpected end when deserializing object."); } #endregion #region Serialize /// /// Serializes the specified and writes the Json structure /// to a Stream using the specified . /// /// The used to write the Json structure. /// The to serialize. public void Serialize(TextWriter textWriter, object value) { Serialize(new JsonWriter(textWriter), value); } /// /// Serializes the specified and writes the Json structure /// to a Stream using the specified . /// /// The used to write the Json structure. /// The to serialize. void Serialize(JsonWriter jsonWriter, object value) { SerializeValue(jsonWriter, value); } private void SerializeValue(JsonWriter writer, object value) { //JsonConverter converter; if (value == null) { writer.WriteNull (); } else { JavaScriptConverter jsconverter = _context.GetConverter (value.GetType ()); if (jsconverter != null) { value = jsconverter.Serialize (value, _context); if (value == null) { writer.WriteNull (); return; } } Type valueType = value.GetType (); switch (Type.GetTypeCode (valueType)) { case TypeCode.String: writer.WriteValue ((string) value); break; case TypeCode.Char: writer.WriteValue ((char) value); break; case TypeCode.Boolean: writer.WriteValue ((bool) value); break; case TypeCode.SByte: writer.WriteValue ((sbyte) value); break; case TypeCode.Int16: writer.WriteValue ((short) value); break; case TypeCode.UInt16: writer.WriteValue ((ushort) value); break; case TypeCode.Int32: writer.WriteValue ((int) value); break; case TypeCode.Byte: writer.WriteValue ((byte) value); break; case TypeCode.UInt32: writer.WriteValue ((uint) value); break; case TypeCode.Int64: writer.WriteValue ((long) value); break; case TypeCode.UInt64: writer.WriteValue ((ulong) value); break; case TypeCode.Single: writer.WriteValue ((float) value); break; case TypeCode.Double: writer.WriteValue ((double) value); break; case TypeCode.DateTime: writer.WriteValue ((DateTime) value); break; case TypeCode.Decimal: writer.WriteValue ((decimal) value); break; default: ThrowOnReferenceLoop (writer, value); writer.SerializeStack.Push (value); try { if (value is IDictionary) { SerializeDictionary (writer, (IDictionary) value); } else if (value is IEnumerable) { SerializeEnumerable (writer, (IEnumerable) value); } else { TypeConverter converter = TypeDescriptor.GetConverter (valueType); // use the objectType's TypeConverter if it has one and can convert to a string if (converter != null) { if (!(converter is ComponentConverter) && converter.GetType () != typeof (TypeConverter)) { if (converter.CanConvertTo (typeof (string))) { writer.WriteValue (converter.ConvertToInvariantString (value)); return; } } } SerializeDictionary (writer, new SerializerLazyDictionary (value)); } } finally { object x = writer.SerializeStack.Pop (); if (x != value) throw new InvalidOperationException ("Serialization stack is corrupted"); } break; } } } private void ThrowOnReferenceLoop (JsonWriter writer, object value) { switch (_referenceLoopHandling) { case ReferenceLoopHandling.Error: if (writer.SerializeStack.Contains (value)) throw new JsonSerializationException ("Self referencing loop"); break; case ReferenceLoopHandling.Ignore: // return from method return; case ReferenceLoopHandling.Serialize: // continue break; default: throw new InvalidOperationException (string.Format ("Unexpected ReferenceLoopHandling value: '{0}'", _referenceLoopHandling)); } } private void SerializeEnumerable (JsonWriter writer, IEnumerable values) { writer.WriteStartArray (); foreach (object value in values) SerializeValue (writer, value); writer.WriteEndArray (); } private void SerializeDictionary(JsonWriter writer, IDictionary values) { writer.WriteStartObject(); foreach (DictionaryEntry entry in values) SerializePair (writer, entry.Key.ToString (), entry.Value); writer.WriteEndObject(); } private void SerializeDictionary (JsonWriter writer, IDictionary values) { writer.WriteStartObject (); foreach (KeyValuePair entry in values) SerializePair (writer, entry.Key, entry.Value); writer.WriteEndObject (); } private void SerializePair (JsonWriter writer, string key, object value) { writer.WritePropertyName (key); SerializeValue (writer, value); } #endregion } }