#region Header /** * JsonData.cs * Generic type to hold JSON data (objects, arrays, and so on). This is * the default type returned by JsonMapper.ToObject(). * * The authors disclaim copyright to this source code. For more details, see * the COPYING file included with this distribution. **/ #endregion using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; namespace LitJson { public class JsonData : IJsonWrapper, IEquatable { #region Fields private IList inst_array; private bool inst_boolean; private double inst_double; private int inst_int; private long inst_long; private IDictionary inst_object; private string inst_string; private string json; private JsonType type; // Used to implement the IOrderedDictionary interface private IList> object_list; #endregion #region Properties public int Count { get { return EnsureCollection ().Count; } } public bool IsArray { get { return type == JsonType.Array; } } public bool IsBoolean { get { return type == JsonType.Boolean; } } public bool IsDouble { get { return type == JsonType.Double; } } public bool IsInt { get { return type == JsonType.Int; } } public bool IsLong { get { return type == JsonType.Long; } } public bool IsObject { get { return type == JsonType.Object; } } public bool IsString { get { return type == JsonType.String; } } public ICollection Keys { get { EnsureDictionary (); return inst_object.Keys; } } /// /// Determines whether the json contains an element that has the specified key. /// /// The key to locate in the json. /// true if the json contains an element that has the specified key; otherwise, false. public Boolean ContainsKey(String key) { EnsureDictionary(); return this.inst_object.Keys.Contains(key); } #endregion #region ICollection Properties int ICollection.Count { get { return Count; } } bool ICollection.IsSynchronized { get { return EnsureCollection ().IsSynchronized; } } object ICollection.SyncRoot { get { return EnsureCollection ().SyncRoot; } } #endregion #region IDictionary Properties bool IDictionary.IsFixedSize { get { return EnsureDictionary ().IsFixedSize; } } bool IDictionary.IsReadOnly { get { return EnsureDictionary ().IsReadOnly; } } ICollection IDictionary.Keys { get { EnsureDictionary (); IList keys = new List (); foreach (KeyValuePair entry in object_list) { keys.Add (entry.Key); } return (ICollection) keys; } } ICollection IDictionary.Values { get { EnsureDictionary (); IList values = new List (); foreach (KeyValuePair entry in object_list) { values.Add (entry.Value); } return (ICollection) values; } } #endregion #region IJsonWrapper Properties bool IJsonWrapper.IsArray { get { return IsArray; } } bool IJsonWrapper.IsBoolean { get { return IsBoolean; } } bool IJsonWrapper.IsDouble { get { return IsDouble; } } bool IJsonWrapper.IsInt { get { return IsInt; } } bool IJsonWrapper.IsLong { get { return IsLong; } } bool IJsonWrapper.IsObject { get { return IsObject; } } bool IJsonWrapper.IsString { get { return IsString; } } #endregion #region IList Properties bool IList.IsFixedSize { get { return EnsureList ().IsFixedSize; } } bool IList.IsReadOnly { get { return EnsureList ().IsReadOnly; } } #endregion #region IDictionary Indexer object IDictionary.this[object key] { get { return EnsureDictionary ()[key]; } set { if (! (key is String)) throw new ArgumentException ( "The key has to be a string"); JsonData data = ToJsonData (value); this[(string) key] = data; } } #endregion #region IOrderedDictionary Indexer object IOrderedDictionary.this[int idx] { get { EnsureDictionary (); return object_list[idx].Value; } set { EnsureDictionary (); JsonData data = ToJsonData (value); KeyValuePair old_entry = object_list[idx]; inst_object[old_entry.Key] = data; KeyValuePair entry = new KeyValuePair (old_entry.Key, data); object_list[idx] = entry; } } #endregion #region IList Indexer object IList.this[int index] { get { return EnsureList ()[index]; } set { EnsureList (); JsonData data = ToJsonData (value); this[index] = data; } } #endregion #region Public Indexers public JsonData this[string prop_name] { get { EnsureDictionary (); return inst_object[prop_name]; } set { EnsureDictionary (); KeyValuePair entry = new KeyValuePair (prop_name, value); if (inst_object.ContainsKey (prop_name)) { for (int i = 0; i < object_list.Count; i++) { if (object_list[i].Key == prop_name) { object_list[i] = entry; break; } } } else object_list.Add (entry); inst_object[prop_name] = value; json = null; } } public JsonData this[int index] { get { EnsureCollection (); if (type == JsonType.Array) return inst_array[index]; return object_list[index].Value; } set { EnsureCollection (); if (type == JsonType.Array) inst_array[index] = value; else { KeyValuePair entry = object_list[index]; KeyValuePair new_entry = new KeyValuePair (entry.Key, value); object_list[index] = new_entry; inst_object[entry.Key] = value; } json = null; } } #endregion #region Constructors public JsonData () { } public JsonData (bool boolean) { type = JsonType.Boolean; inst_boolean = boolean; } public JsonData (double number) { type = JsonType.Double; inst_double = number; } public JsonData (int number) { type = JsonType.Int; inst_int = number; } public JsonData (long number) { type = JsonType.Long; inst_long = number; } public JsonData (object obj) { if (obj is Boolean) { type = JsonType.Boolean; inst_boolean = (bool) obj; return; } if (obj is Double) { type = JsonType.Double; inst_double = (double) obj; return; } if (obj is Int32) { type = JsonType.Int; inst_int = (int) obj; return; } if (obj is Int64) { type = JsonType.Long; inst_long = (long) obj; return; } if (obj is String) { type = JsonType.String; inst_string = (string) obj; return; } throw new ArgumentException ( "Unable to wrap the given object with JsonData"); } public JsonData (string str) { type = JsonType.String; inst_string = str; } #endregion #region Implicit Conversions public static implicit operator JsonData (Boolean data) { return new JsonData (data); } public static implicit operator JsonData (Double data) { return new JsonData (data); } public static implicit operator JsonData (Int32 data) { return new JsonData (data); } public static implicit operator JsonData (Int64 data) { return new JsonData (data); } public static implicit operator JsonData (String data) { return new JsonData (data); } #endregion #region Explicit Conversions public static explicit operator Boolean (JsonData data) { if (data.type != JsonType.Boolean) throw new InvalidCastException ( "Instance of JsonData doesn't hold a double"); return data.inst_boolean; } public static explicit operator Double (JsonData data) { if (data.type != JsonType.Double) throw new InvalidCastException ( "Instance of JsonData doesn't hold a double"); return data.inst_double; } public static explicit operator Int32(JsonData data) { if (data.type != JsonType.Int && data.type != JsonType.Long) { throw new InvalidCastException( "Instance of JsonData doesn't hold an int"); } // cast may truncate data... but that's up to the user to consider return data.type == JsonType.Int ? data.inst_int : (int)data.inst_long; } public static explicit operator Int64(JsonData data) { if (data.type != JsonType.Long && data.type != JsonType.Int) { throw new InvalidCastException( "Instance of JsonData doesn't hold a long"); } return data.type == JsonType.Long ? data.inst_long : data.inst_int; } public static explicit operator String (JsonData data) { if (data.type != JsonType.String) throw new InvalidCastException ( "Instance of JsonData doesn't hold a string"); return data.inst_string; } #endregion #region ICollection Methods void ICollection.CopyTo (Array array, int index) { EnsureCollection ().CopyTo (array, index); } #endregion #region IDictionary Methods void IDictionary.Add (object key, object value) { JsonData data = ToJsonData (value); EnsureDictionary ().Add (key, data); KeyValuePair entry = new KeyValuePair ((string) key, data); object_list.Add (entry); json = null; } void IDictionary.Clear () { EnsureDictionary ().Clear (); object_list.Clear (); json = null; } bool IDictionary.Contains (object key) { return EnsureDictionary ().Contains (key); } IDictionaryEnumerator IDictionary.GetEnumerator () { return ((IOrderedDictionary) this).GetEnumerator (); } void IDictionary.Remove (object key) { EnsureDictionary ().Remove (key); for (int i = 0; i < object_list.Count; i++) { if (object_list[i].Key == (string) key) { object_list.RemoveAt (i); break; } } json = null; } #endregion #region IEnumerable Methods IEnumerator IEnumerable.GetEnumerator () { return EnsureCollection ().GetEnumerator (); } #endregion #region IJsonWrapper Methods bool IJsonWrapper.GetBoolean () { if (type != JsonType.Boolean) throw new InvalidOperationException ( "JsonData instance doesn't hold a boolean"); return inst_boolean; } double IJsonWrapper.GetDouble () { if (type != JsonType.Double) throw new InvalidOperationException ( "JsonData instance doesn't hold a double"); return inst_double; } int IJsonWrapper.GetInt () { if (type != JsonType.Int) throw new InvalidOperationException ( "JsonData instance doesn't hold an int"); return inst_int; } long IJsonWrapper.GetLong () { if (type != JsonType.Long) throw new InvalidOperationException ( "JsonData instance doesn't hold a long"); return inst_long; } string IJsonWrapper.GetString () { if (type != JsonType.String) throw new InvalidOperationException ( "JsonData instance doesn't hold a string"); return inst_string; } void IJsonWrapper.SetBoolean (bool val) { type = JsonType.Boolean; inst_boolean = val; json = null; } void IJsonWrapper.SetDouble (double val) { type = JsonType.Double; inst_double = val; json = null; } void IJsonWrapper.SetInt (int val) { type = JsonType.Int; inst_int = val; json = null; } void IJsonWrapper.SetLong (long val) { type = JsonType.Long; inst_long = val; json = null; } void IJsonWrapper.SetString (string val) { type = JsonType.String; inst_string = val; json = null; } string IJsonWrapper.ToJson () { return ToJson (); } void IJsonWrapper.ToJson (JsonWriter writer) { ToJson (writer); } #endregion #region IList Methods int IList.Add (object value) { return Add (value); } void IList.Clear () { EnsureList ().Clear (); json = null; } bool IList.Contains (object value) { return EnsureList ().Contains (value); } int IList.IndexOf (object value) { return EnsureList ().IndexOf (value); } void IList.Insert (int index, object value) { EnsureList ().Insert (index, value); json = null; } void IList.Remove (object value) { EnsureList ().Remove (value); json = null; } void IList.RemoveAt (int index) { EnsureList ().RemoveAt (index); json = null; } #endregion #region IOrderedDictionary Methods IDictionaryEnumerator IOrderedDictionary.GetEnumerator () { EnsureDictionary (); return new OrderedDictionaryEnumerator ( object_list.GetEnumerator ()); } void IOrderedDictionary.Insert (int idx, object key, object value) { string property = (string) key; JsonData data = ToJsonData (value); this[property] = data; KeyValuePair entry = new KeyValuePair (property, data); object_list.Insert (idx, entry); } void IOrderedDictionary.RemoveAt (int idx) { EnsureDictionary (); inst_object.Remove (object_list[idx].Key); object_list.RemoveAt (idx); } #endregion #region Private Methods private ICollection EnsureCollection () { if (type == JsonType.Array) return (ICollection) inst_array; if (type == JsonType.Object) return (ICollection) inst_object; throw new InvalidOperationException ( "The JsonData instance has to be initialized first"); } private IDictionary EnsureDictionary () { if (type == JsonType.Object) return (IDictionary) inst_object; if (type != JsonType.None) throw new InvalidOperationException ( "Instance of JsonData is not a dictionary"); type = JsonType.Object; inst_object = new Dictionary (); object_list = new List> (); return (IDictionary) inst_object; } private IList EnsureList () { if (type == JsonType.Array) return (IList) inst_array; if (type != JsonType.None) throw new InvalidOperationException ( "Instance of JsonData is not a list"); type = JsonType.Array; inst_array = new List (); return (IList) inst_array; } private JsonData ToJsonData (object obj) { if (obj == null) return null; if (obj is JsonData) return (JsonData) obj; return new JsonData (obj); } private static void WriteJson (IJsonWrapper obj, JsonWriter writer) { if (obj == null) { writer.Write (null); return; } if (obj.IsString) { writer.Write (obj.GetString ()); return; } if (obj.IsBoolean) { writer.Write (obj.GetBoolean ()); return; } if (obj.IsDouble) { writer.Write (obj.GetDouble ()); return; } if (obj.IsInt) { writer.Write (obj.GetInt ()); return; } if (obj.IsLong) { writer.Write (obj.GetLong ()); return; } if (obj.IsArray) { writer.WriteArrayStart (); foreach (object elem in (IList) obj) WriteJson ((JsonData) elem, writer); writer.WriteArrayEnd (); return; } if (obj.IsObject) { writer.WriteObjectStart (); foreach (DictionaryEntry entry in ((IDictionary) obj)) { writer.WritePropertyName ((string) entry.Key); WriteJson ((JsonData) entry.Value, writer); } writer.WriteObjectEnd (); return; } } #endregion public int Add (object value) { JsonData data = ToJsonData (value); json = null; return EnsureList ().Add (data); } public bool Remove(object obj) { json = null; if(IsObject) { JsonData value = null; if (inst_object.TryGetValue((string)obj, out value)) return inst_object.Remove((string)obj) && object_list.Remove(new KeyValuePair((string)obj, value)); else throw new KeyNotFoundException("The specified key was not found in the JsonData object."); } if(IsArray) { return inst_array.Remove(ToJsonData(obj)); } throw new InvalidOperationException ( "Instance of JsonData is not an object or a list."); } public void Clear () { if (IsObject) { ((IDictionary) this).Clear (); return; } if (IsArray) { ((IList) this).Clear (); return; } } public bool Equals (JsonData x) { if (x == null) return false; if (x.type != this.type) { // further check to see if this is a long to int comparison if ((x.type != JsonType.Int && x.type != JsonType.Long) || (this.type != JsonType.Int && this.type != JsonType.Long)) { return false; } } switch (this.type) { case JsonType.None: return true; case JsonType.Object: return this.inst_object.Equals (x.inst_object); case JsonType.Array: return this.inst_array.Equals (x.inst_array); case JsonType.String: return this.inst_string.Equals (x.inst_string); case JsonType.Int: { if (x.IsLong) { if (x.inst_long < Int32.MinValue || x.inst_long > Int32.MaxValue) return false; return this.inst_int.Equals((int)x.inst_long); } return this.inst_int.Equals(x.inst_int); } case JsonType.Long: { if (x.IsInt) { if (this.inst_long < Int32.MinValue || this.inst_long > Int32.MaxValue) return false; return x.inst_int.Equals((int)this.inst_long); } return this.inst_long.Equals(x.inst_long); } case JsonType.Double: return this.inst_double.Equals (x.inst_double); case JsonType.Boolean: return this.inst_boolean.Equals (x.inst_boolean); } return false; } public JsonType GetJsonType () { return type; } public void SetJsonType (JsonType type) { if (this.type == type) return; switch (type) { case JsonType.None: break; case JsonType.Object: inst_object = new Dictionary (); object_list = new List> (); break; case JsonType.Array: inst_array = new List (); break; case JsonType.String: inst_string = default (String); break; case JsonType.Int: inst_int = default (Int32); break; case JsonType.Long: inst_long = default (Int64); break; case JsonType.Double: inst_double = default (Double); break; case JsonType.Boolean: inst_boolean = default (Boolean); break; } this.type = type; } public string ToJson () { if (json != null) return json; StringWriter sw = new StringWriter (); JsonWriter writer = new JsonWriter (sw); writer.Validate = false; WriteJson (this, writer); json = sw.ToString (); return json; } public void ToJson (JsonWriter writer) { bool old_validate = writer.Validate; writer.Validate = false; WriteJson (this, writer); writer.Validate = old_validate; } public override string ToString () { switch (type) { case JsonType.Array: return "JsonData array"; case JsonType.Boolean: return inst_boolean.ToString (); case JsonType.Double: return inst_double.ToString (); case JsonType.Int: return inst_int.ToString (); case JsonType.Long: return inst_long.ToString (); case JsonType.Object: return "JsonData object"; case JsonType.String: return inst_string; } return "Uninitialized JsonData"; } } internal class OrderedDictionaryEnumerator : IDictionaryEnumerator { IEnumerator> list_enumerator; public object Current { get { return Entry; } } public DictionaryEntry Entry { get { KeyValuePair curr = list_enumerator.Current; return new DictionaryEntry (curr.Key, curr.Value); } } public object Key { get { return list_enumerator.Current.Key; } } public object Value { get { return list_enumerator.Current.Value; } } public OrderedDictionaryEnumerator ( IEnumerator> enumerator) { list_enumerator = enumerator; } public bool MoveNext () { return list_enumerator.MoveNext (); } public void Reset () { list_enumerator.Reset (); } } }