#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; using System.Linq; 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 => EnsureCollection().Count; public bool IsArray => type == JsonType.Array; public bool IsBoolean => type == JsonType.Boolean; public bool IsDouble => type == JsonType.Double; public bool IsInt => type == JsonType.Int; public bool IsLong => type == JsonType.Long; public bool IsObject => type == JsonType.Object; public bool IsString => type == JsonType.String; #endregion #region ICollection Properties int ICollection.Count => Count; bool ICollection.IsSynchronized => EnsureCollection().IsSynchronized; object ICollection.SyncRoot => EnsureCollection().SyncRoot; #endregion #region IDictionary Properties bool IDictionary.IsFixedSize => EnsureDictionary().IsFixedSize; bool IDictionary.IsReadOnly => EnsureDictionary().IsReadOnly; ICollection IDictionary.Keys { get { EnsureDictionary(); IList keys = object_list.Select(entry => entry.Key).ToList(); return (ICollection)keys; } } ICollection IDictionary.Values { get { EnsureDictionary(); IList values = object_list.Select(entry => entry.Value).ToList(); return (ICollection)values; } } #endregion #region IJsonWrapper Properties bool IJsonWrapper.IsArray => IsArray; bool IJsonWrapper.IsBoolean => IsBoolean; bool IJsonWrapper.IsDouble => IsDouble; bool IJsonWrapper.IsInt => IsInt; bool IJsonWrapper.IsLong => IsLong; bool IJsonWrapper.IsObject => IsObject; bool IJsonWrapper.IsString => IsString; #endregion #region IList Properties bool IList.IsFixedSize => EnsureList().IsFixedSize; bool IList.IsReadOnly => 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); var old_entry = object_list[idx]; inst_object[old_entry.Key] = data; var 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(); var 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(); return type == JsonType.Array ? inst_array[index] : object_list[index].Value; } set { EnsureCollection(); if (type == JsonType.Array) inst_array[index] = value; else { var entry = object_list[index]; var 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 bool b) { type = JsonType.Boolean; inst_boolean = b; return; } if (obj is double d) { type = JsonType.Double; inst_double = d; return; } if (obj is int i) { type = JsonType.Int; inst_int = i; return; } if (obj is long l) { type = JsonType.Long; inst_long = l; return; } if (obj is string s) { type = JsonType.String; inst_string = s; 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) throw new InvalidCastException( "Instance of JsonData doesn't hold an int"); return data.inst_int; } public static explicit operator Int64(JsonData data) { if (data.type != JsonType.Long) throw new InvalidCastException( "Instance of JsonData doesn't hold an int"); return data.inst_long; } 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); var 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 data) return data; return new JsonData(obj); } private static void WriteJson(IJsonWrapper obj, JsonWriter writer) { 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(); } } #endregion public int Add(object value) { JsonData data = ToJsonData(value); json = null; return EnsureList().Add(data); } public void Clear() { if (IsObject) { ((IDictionary)this).Clear(); } else if (IsArray) { ((IList)this).Clear(); } } public bool Equals(JsonData x) { if (x?.type != type) 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: return this.inst_int.Equals(x.inst_int); case JsonType.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) { 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 => Entry; public DictionaryEntry Entry { get { var curr = list_enumerator.Current; return new DictionaryEntry(curr.Key, curr.Value); } } public object Key => list_enumerator.Current.Key; public object Value => list_enumerator.Current.Value; public OrderedDictionaryEnumerator( IEnumerator> enumerator) { list_enumerator = enumerator; } public bool MoveNext() { return list_enumerator.MoveNext(); } public void Reset() { list_enumerator.Reset(); } } }