#region License // Copyright (c) 2007 James Newton-King // Copyright 2007 Konstantin Triger // // 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. #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 { sealed internal class DeserializerLazyDictionary : JavaScriptSerializer.LazyDictionary { readonly JsonReader _reader; readonly JsonSerializer _serializer; IEnumerator> _innerEnum; object _firstValue; public DeserializerLazyDictionary (JsonReader reader, JsonSerializer serializer) { _reader = reader; _serializer = serializer; } public object PeekFirst () { if (_innerEnum != null) throw new InvalidOperationException ("first already taken"); _innerEnum = _serializer.PopulateObject (_reader); if (_innerEnum.MoveNext ()) _firstValue = _innerEnum.Current; return _firstValue; } protected override IEnumerator> GetEnumerator () { if (_innerEnum == null) _innerEnum = _serializer.PopulateObject (_reader); if (_firstValue != null) yield return (KeyValuePair) _firstValue; while (_innerEnum.MoveNext ()) yield return _innerEnum.Current; } } sealed class SerializerLazyDictionary : JavaScriptSerializer.LazyDictionary { readonly object _source; public SerializerLazyDictionary (object source) { _source = source; } protected override IEnumerator> GetEnumerator () { KeyValuePair kvpret; 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)) { // A temporary hack to prevent situations // when a type member cannot be serialized // for some reason (e.g. when serializing a // CultureInfo for 'en-US', processing its // Parent property which returns CultureInfo // for 'en' - asking for the Calendar // property value for that one will throw an // exception). Until a better solution is // devised, this has to stay in. try { kvpret = new KeyValuePair (member.Name, ReflectionUtils.GetMemberValue (member, _source)); } catch (Exception ex) { Console.Error.WriteLine ("HACK WARNING! NOT YIELDING THE VALUE! Serializing {0}.{1} threw an exception:", _source.GetType (), member.Name); Console.Error.WriteLine (ex); continue; } yield return kvpret; } } } } sealed class GenericDictionaryLazyDictionary : JavaScriptSerializer.LazyDictionary { readonly object _source; readonly PropertyInfo _piKeys; readonly PropertyInfo _piValues; public GenericDictionaryLazyDictionary (object source, Type dictType) { _source = source; _piKeys = dictType.GetProperty ("Keys"); _piValues = dictType.GetProperty ("Values"); } protected override IEnumerator> GetEnumerator () { IEnumerable eKeys = (IEnumerable) _piKeys.GetValue (_source, null); IEnumerator eValues = ((IEnumerable) _piValues.GetValue (_source, null)).GetEnumerator(); foreach (object key in eKeys) { string keyString = key == null ? null : key.ToString (); if (!eValues.MoveNext ()) throw new IndexOutOfRangeException (keyString); yield return new KeyValuePair (keyString, eValues.Current); } if (eValues.MoveNext ()) throw new IndexOutOfRangeException (eValues.Current != null ? eValues.Current.ToString () : String.Empty); } } private int _maxJsonLength; private int _recursionLimit; private int _currentRecursionCounter; private ReferenceLoopHandling _referenceLoopHandling; readonly JavaScriptSerializer _context; readonly JavaScriptTypeResolver _typeResolver; public int MaxJsonLength { get { return _maxJsonLength; } set { _maxJsonLength = value; } } public int RecursionLimit { get { return _recursionLimit; } set { _recursionLimit = value; } } /// /// 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, JavaScriptTypeResolver resolver) { _context = context; _typeResolver = resolver; _referenceLoopHandling = ReferenceLoopHandling.Error; } #region Deserialize public object Deserialize (TextReader reader) { return Deserialize (new JsonReader (reader, MaxJsonLength, RecursionLimit)); } /// /// 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*/) { if (RecursionLimit > 0 && reader.CurrentRecursionLevel >= RecursionLimit) { throw new ArgumentException ("RecursionLimit exceeded."); } 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); } 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*/) { reader.IncrementRecursionLevel (); 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: reader.DecrementRecursionLevel (); 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, MaxJsonLength), 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, true, null); } private void SerializeValue(JsonWriter writer, object value) { SerializeValue (writer, value, false, null); } private void WritePropertyName (JsonWriter writer, string name) { if (String.IsNullOrEmpty (name)) return; writer.WritePropertyName (name); } private void SerializeValue (JsonWriter writer, object value, bool topLevelObject, string propertyName) { //JsonConverter converter; _currentRecursionCounter++; if (RecursionLimit > 0 && _currentRecursionCounter > RecursionLimit) { throw new ArgumentException ("RecursionLimit exceeded."); } if (value == null) { WritePropertyName (writer, propertyName); writer.WriteNull (); } else { Type valueType = value.GetType (); JavaScriptConverter jsconverter = _context.GetConverter (valueType); if (jsconverter != null) { value = jsconverter.Serialize (value, _context); if (value == null) { WritePropertyName (writer, propertyName); writer.WriteNull (); _currentRecursionCounter--; return; } } switch (Type.GetTypeCode (valueType)) { case TypeCode.String: WritePropertyName (writer, propertyName); writer.WriteValue ((string) value); break; case TypeCode.Char: WritePropertyName (writer, propertyName); writer.WriteValue ((char) value); break; case TypeCode.Boolean: WritePropertyName (writer, propertyName); writer.WriteValue ((bool) value); break; case TypeCode.SByte: WritePropertyName (writer, propertyName); writer.WriteValue ((sbyte) value); break; case TypeCode.Int16: WritePropertyName (writer, propertyName); writer.WriteValue ((short) value); break; case TypeCode.UInt16: WritePropertyName (writer, propertyName); writer.WriteValue ((ushort) value); break; case TypeCode.Int32: WritePropertyName (writer, propertyName); writer.WriteValue ((int) value); break; case TypeCode.Byte: WritePropertyName (writer, propertyName); writer.WriteValue ((byte) value); break; case TypeCode.UInt32: WritePropertyName (writer, propertyName); writer.WriteValue ((uint) value); break; case TypeCode.Int64: WritePropertyName (writer, propertyName); writer.WriteValue ((long) value); break; case TypeCode.UInt64: WritePropertyName (writer, propertyName); writer.WriteValue ((ulong) value); break; case TypeCode.Single: WritePropertyName (writer, propertyName); writer.WriteValue ((float) value); break; case TypeCode.Double: WritePropertyName (writer, propertyName); writer.WriteValue ((double) value); break; case TypeCode.DateTime: WritePropertyName (writer, propertyName); writer.WriteValue ((DateTime) value); break; case TypeCode.Decimal: WritePropertyName (writer, propertyName); writer.WriteValue ((decimal) value); break; default: ThrowOnReferenceLoop (writer, value); writer.SerializeStack.Push (value); try { Type genDictType; if (value is IDictionary) { WritePropertyName (writer, propertyName); SerializeDictionary (writer, (IDictionary) value); } else if (value is IDictionary) { WritePropertyName (writer, propertyName); SerializeDictionary (writer, (IDictionary) value, null); } else if ((genDictType = ReflectionUtils.GetGenericDictionary (valueType)) != null) { WritePropertyName (writer, propertyName); SerializeDictionary (writer, new GenericDictionaryLazyDictionary (value, genDictType), null); } else if (value is IEnumerable) { WritePropertyName (writer, propertyName); SerializeEnumerable (writer, (IEnumerable) value); } else if (topLevelObject) { SerializeCustomObject (writer, value, valueType); } } finally { object x = writer.SerializeStack.Pop (); if (x != value) throw new InvalidOperationException ("Serialization stack is corrupted"); } break; } } _currentRecursionCounter--; } 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, true, null); 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, string typeID) { writer.WriteStartObject (); if (typeID != null) { SerializePair (writer, JavaScriptSerializer.SerializedTypeNameKey, typeID); } foreach (KeyValuePair entry in values) SerializePair (writer, entry.Key, entry.Value); writer.WriteEndObject (); } private void SerializeCustomObject (JsonWriter writer, object value, Type valueType) { if (value is Uri) { Uri uri = value as Uri; writer.WriteValue (uri.GetComponents (UriComponents.AbsoluteUri, UriFormat.UriEscaped)); return; } if (valueType == typeof (Guid)) { writer.WriteValue (((Guid) value).ToString ()); return; } string typeID = null; if (_typeResolver != null) { typeID = _typeResolver.ResolveTypeId (valueType); } SerializeDictionary (writer, new SerializerLazyDictionary (value), typeID); } private void SerializePair (JsonWriter writer, string key, object value) { SerializeValue (writer, value, false, key); } #endregion } }