From e0735881a12d65ebe5b98453a4d21d34ba463cfa Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Tue, 8 Sep 2020 08:41:47 -0700 Subject: [PATCH 01/23] Making the repository compatible with Unity Package Manager. --- Changelog.txt => CHANGELOG.md | 0 LICENSE => LICENSE.md | 0 README => README.md | 0 SimpleJSON.cs => Runtime/SimpleJSON.cs | 22 +++++++++---------- .../SimpleJSONBinary.cs | 0 .../SimpleJSONUnity.cs | 0 Runtime/Unity.SimpleJSON.asmdef | 3 +++ package.json | 16 ++++++++++++++ 8 files changed, 30 insertions(+), 11 deletions(-) rename Changelog.txt => CHANGELOG.md (100%) rename LICENSE => LICENSE.md (100%) rename README => README.md (100%) rename SimpleJSON.cs => Runtime/SimpleJSON.cs (99%) rename SimpleJSONBinary.cs => Runtime/SimpleJSONBinary.cs (100%) rename SimpleJSONUnity.cs => Runtime/SimpleJSONUnity.cs (100%) create mode 100644 Runtime/Unity.SimpleJSON.asmdef create mode 100755 package.json diff --git a/Changelog.txt b/CHANGELOG.md similarity index 100% rename from Changelog.txt rename to CHANGELOG.md diff --git a/LICENSE b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md diff --git a/README b/README.md similarity index 100% rename from README rename to README.md diff --git a/SimpleJSON.cs b/Runtime/SimpleJSON.cs similarity index 99% rename from SimpleJSON.cs rename to Runtime/SimpleJSON.cs index 0eee026..46d444a 100644 --- a/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -1,29 +1,29 @@ /* * * * * * A simple JSON Parser / builder * ------------------------------ - * + * * It mainly has been written as a simple JSON parser. It can build a JSON string * from the node-tree, or generate a node tree from any valid JSON string. - * - * Written by Bunny83 + * + * Written by Bunny83 * 2012-06-09 - * - * Changelog now external. See Changelog.txt - * + * + * Changelog now external. See CHANGELOG.md + * * The MIT License (MIT) - * + * * Copyright (c) 2012-2019 Markus Göbel (Bunny83) - * + * * 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 @@ -31,7 +31,7 @@ * 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; diff --git a/SimpleJSONBinary.cs b/Runtime/SimpleJSONBinary.cs similarity index 100% rename from SimpleJSONBinary.cs rename to Runtime/SimpleJSONBinary.cs diff --git a/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs similarity index 100% rename from SimpleJSONUnity.cs rename to Runtime/SimpleJSONUnity.cs diff --git a/Runtime/Unity.SimpleJSON.asmdef b/Runtime/Unity.SimpleJSON.asmdef new file mode 100644 index 0000000..70914bb --- /dev/null +++ b/Runtime/Unity.SimpleJSON.asmdef @@ -0,0 +1,3 @@ +{ + "name": "SimpleJSON" +} diff --git a/package.json b/package.json new file mode 100755 index 0000000..3f74402 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "com.github.bunny83.simplejson", + "version": "0.1.0", + "displayName": "SimpleJSON", + "description": "JSON Parser (A simple one)", + "unity": "2018.4", + "keywords": [ + "utilities", + "JSON" + ], + "author": { + "name": "Markus Göbel", + "email": "", + "url": "https://github.com/Bunny83" + } +} \ No newline at end of file From edcc44326c2d8b400eedd3bee5a21b11d94479b8 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Tue, 8 Sep 2020 19:04:13 -0700 Subject: [PATCH 02/23] Add TryParse method. --- Runtime/SimpleJSON.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index 46d444a..2f67258 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -756,7 +756,7 @@ public override JSONNode Clone() { var node = new JSONArray(); node.m_List.Capacity = m_List.Capacity; - foreach(var n in m_List) + foreach (var n in m_List) { if (n != null) node.Add(n.Clone()); @@ -1348,5 +1348,22 @@ public static JSONNode Parse(string aJSON) { return JSONNode.Parse(aJSON); } + + public static bool TryParse(string aJSON, out JSONNode aResult) + { + try + { + aResult = JSON.Parse(aJSON); + return true; + } + catch (Exception e) + { +#if UNITY_5_3_OR_NEWER + UnityEngine.Debug.LogException(e); +#endif + aResult = null; + return false; + } + } } } From 6502954e8d58e338f518cfe2f96e9329bd9d24dc Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Tue, 8 Sep 2020 19:54:02 -0700 Subject: [PATCH 03/23] Make JSONNode navigation stricter. Get/Set/Add/Remove methods that shouldn't work for a type throw exceptions. --- Runtime/SimpleJSON.cs | 2785 ++++++++++++++++++----------------- Runtime/SimpleJSONBinary.cs | 26 +- Runtime/SimpleJSONUnity.cs | 58 +- 3 files changed, 1514 insertions(+), 1355 deletions(-) diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index 2f67258..d91d77e 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -37,1333 +37,1472 @@ using System.Collections; using System.Collections.Generic; using System.Globalization; -using System.Linq; using System.Text; namespace SimpleJSON { - public enum JSONNodeType - { - Array = 1, - Object = 2, - String = 3, - Number = 4, - NullValue = 5, - Boolean = 6, - None = 7, - Custom = 0xFF, - } - public enum JSONTextMode - { - Compact, - Indent - } - - public abstract partial class JSONNode - { - #region Enumerators - public struct Enumerator - { - private enum Type { None, Array, Object } - private Type type; - private Dictionary.Enumerator m_Object; - private List.Enumerator m_Array; - public bool IsValid { get { return type != Type.None; } } - public Enumerator(List.Enumerator aArrayEnum) - { - type = Type.Array; - m_Object = default(Dictionary.Enumerator); - m_Array = aArrayEnum; - } - public Enumerator(Dictionary.Enumerator aDictEnum) - { - type = Type.Object; - m_Object = aDictEnum; - m_Array = default(List.Enumerator); - } - public KeyValuePair Current - { - get - { - if (type == Type.Array) - return new KeyValuePair(string.Empty, m_Array.Current); - else if (type == Type.Object) - return m_Object.Current; - return new KeyValuePair(string.Empty, null); - } - } - public bool MoveNext() - { - if (type == Type.Array) - return m_Array.MoveNext(); - else if (type == Type.Object) - return m_Object.MoveNext(); - return false; - } - } - public struct ValueEnumerator - { - private Enumerator m_Enumerator; - public ValueEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } - public ValueEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } - public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } - public JSONNode Current { get { return m_Enumerator.Current.Value; } } - public bool MoveNext() { return m_Enumerator.MoveNext(); } - public ValueEnumerator GetEnumerator() { return this; } - } - public struct KeyEnumerator - { - private Enumerator m_Enumerator; - public KeyEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } - public KeyEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } - public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } - public string Current { get { return m_Enumerator.Current.Key; } } - public bool MoveNext() { return m_Enumerator.MoveNext(); } - public KeyEnumerator GetEnumerator() { return this; } - } - - public class LinqEnumerator : IEnumerator>, IEnumerable> - { - private JSONNode m_Node; - private Enumerator m_Enumerator; - internal LinqEnumerator(JSONNode aNode) - { - m_Node = aNode; - if (m_Node != null) - m_Enumerator = m_Node.GetEnumerator(); - } - public KeyValuePair Current { get { return m_Enumerator.Current; } } - object IEnumerator.Current { get { return m_Enumerator.Current; } } - public bool MoveNext() { return m_Enumerator.MoveNext(); } - - public void Dispose() - { - m_Node = null; - m_Enumerator = new Enumerator(); - } - - public IEnumerator> GetEnumerator() - { - return new LinqEnumerator(m_Node); - } - - public void Reset() - { - if (m_Node != null) - m_Enumerator = m_Node.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return new LinqEnumerator(m_Node); - } - } - - #endregion Enumerators - - #region common interface - - public static bool forceASCII = false; // Use Unicode by default - public static bool longAsString = false; // lazy creator creates a JSONString instead of JSONNumber - public static bool allowLineComments = true; // allow "//"-style comments at the end of a line - - public abstract JSONNodeType Tag { get; } - - public virtual JSONNode this[int aIndex] { get { return null; } set { } } - - public virtual JSONNode this[string aKey] { get { return null; } set { } } - - public virtual string Value { get { return ""; } set { } } - - public virtual int Count { get { return 0; } } - - public virtual bool IsNumber { get { return false; } } - public virtual bool IsString { get { return false; } } - public virtual bool IsBoolean { get { return false; } } - public virtual bool IsNull { get { return false; } } - public virtual bool IsArray { get { return false; } } - public virtual bool IsObject { get { return false; } } - - public virtual bool Inline { get { return false; } set { } } - - public virtual void Add(string aKey, JSONNode aItem) - { - } - public virtual void Add(JSONNode aItem) - { - Add("", aItem); - } - - public virtual JSONNode Remove(string aKey) - { - return null; - } - - public virtual JSONNode Remove(int aIndex) - { - return null; - } - - public virtual JSONNode Remove(JSONNode aNode) - { - return aNode; - } - - public virtual JSONNode Clone() - { - return null; - } - - public virtual IEnumerable Children - { - get - { - yield break; - } - } - - public IEnumerable DeepChildren - { - get - { - foreach (var C in Children) - foreach (var D in C.DeepChildren) - yield return D; - } - } - - public virtual bool HasKey(string aKey) - { - return false; - } - - public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) - { - return aDefault; - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - WriteToStringBuilder(sb, 0, 0, JSONTextMode.Compact); - return sb.ToString(); - } - - public virtual string ToString(int aIndent) - { - StringBuilder sb = new StringBuilder(); - WriteToStringBuilder(sb, 0, aIndent, JSONTextMode.Indent); - return sb.ToString(); - } - internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); - - public abstract Enumerator GetEnumerator(); - public IEnumerable> Linq { get { return new LinqEnumerator(this); } } - public KeyEnumerator Keys { get { return new KeyEnumerator(GetEnumerator()); } } - public ValueEnumerator Values { get { return new ValueEnumerator(GetEnumerator()); } } - - #endregion common interface - - #region typecasting properties - - - public virtual double AsDouble - { - get - { - double v = 0.0; - if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) - return v; - return 0.0; - } - set - { - Value = value.ToString(CultureInfo.InvariantCulture); - } - } - - public virtual int AsInt - { - get { return (int)AsDouble; } - set { AsDouble = value; } - } - - public virtual float AsFloat - { - get { return (float)AsDouble; } - set { AsDouble = value; } - } - - public virtual bool AsBool - { - get - { - bool v = false; - if (bool.TryParse(Value, out v)) - return v; - return !string.IsNullOrEmpty(Value); - } - set - { - Value = (value) ? "true" : "false"; - } - } - - public virtual long AsLong - { - get - { - long val = 0; - if (long.TryParse(Value, out val)) - return val; - return 0L; - } - set - { - Value = value.ToString(); - } - } - - public virtual JSONArray AsArray - { - get - { - return this as JSONArray; - } - } - - public virtual JSONObject AsObject - { - get - { - return this as JSONObject; - } - } - - - #endregion typecasting properties - - #region operators - - public static implicit operator JSONNode(string s) - { - return new JSONString(s); - } - public static implicit operator string(JSONNode d) - { - return (d == null) ? null : d.Value; - } - - public static implicit operator JSONNode(double n) - { - return new JSONNumber(n); - } - public static implicit operator double(JSONNode d) - { - return (d == null) ? 0 : d.AsDouble; - } - - public static implicit operator JSONNode(float n) - { - return new JSONNumber(n); - } - public static implicit operator float(JSONNode d) - { - return (d == null) ? 0 : d.AsFloat; - } - - public static implicit operator JSONNode(int n) - { - return new JSONNumber(n); - } - public static implicit operator int(JSONNode d) - { - return (d == null) ? 0 : d.AsInt; - } - - public static implicit operator JSONNode(long n) - { - if (longAsString) - return new JSONString(n.ToString()); - return new JSONNumber(n); - } - public static implicit operator long(JSONNode d) - { - return (d == null) ? 0L : d.AsLong; - } - - public static implicit operator JSONNode(bool b) - { - return new JSONBool(b); - } - public static implicit operator bool(JSONNode d) - { - return (d == null) ? false : d.AsBool; - } - - public static implicit operator JSONNode(KeyValuePair aKeyValue) - { - return aKeyValue.Value; - } - - public static bool operator ==(JSONNode a, object b) - { - if (ReferenceEquals(a, b)) - return true; - bool aIsNull = a is JSONNull || ReferenceEquals(a, null) || a is JSONLazyCreator; - bool bIsNull = b is JSONNull || ReferenceEquals(b, null) || b is JSONLazyCreator; - if (aIsNull && bIsNull) - return true; - return !aIsNull && a.Equals(b); - } - - public static bool operator !=(JSONNode a, object b) - { - return !(a == b); - } - - public override bool Equals(object obj) - { - return ReferenceEquals(this, obj); - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - - #endregion operators - - [ThreadStatic] - private static StringBuilder m_EscapeBuilder; - internal static StringBuilder EscapeBuilder - { - get - { - if (m_EscapeBuilder == null) - m_EscapeBuilder = new StringBuilder(); - return m_EscapeBuilder; - } - } - internal static string Escape(string aText) - { - var sb = EscapeBuilder; - sb.Length = 0; - if (sb.Capacity < aText.Length + aText.Length / 10) - sb.Capacity = aText.Length + aText.Length / 10; - foreach (char c in aText) - { - switch (c) - { - case '\\': - sb.Append("\\\\"); - break; - case '\"': - sb.Append("\\\""); - break; - case '\n': - sb.Append("\\n"); - break; - case '\r': - sb.Append("\\r"); - break; - case '\t': - sb.Append("\\t"); - break; - case '\b': - sb.Append("\\b"); - break; - case '\f': - sb.Append("\\f"); - break; - default: - if (c < ' ' || (forceASCII && c > 127)) - { - ushort val = c; - sb.Append("\\u").Append(val.ToString("X4")); - } - else - sb.Append(c); - break; - } - } - string result = sb.ToString(); - sb.Length = 0; - return result; - } - - private static JSONNode ParseElement(string token, bool quoted) - { - if (quoted) - return token; - string tmp = token.ToLower(); - if (tmp == "false" || tmp == "true") - return tmp == "true"; - if (tmp == "null") - return JSONNull.CreateOrGet(); - double val; - if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out val)) - return val; - else - return token; - } - - public static JSONNode Parse(string aJSON) - { - Stack stack = new Stack(); - JSONNode ctx = null; - int i = 0; - StringBuilder Token = new StringBuilder(); - string TokenName = ""; - bool QuoteMode = false; - bool TokenIsQuoted = false; - while (i < aJSON.Length) - { - switch (aJSON[i]) - { - case '{': - if (QuoteMode) - { - Token.Append(aJSON[i]); - break; - } - stack.Push(new JSONObject()); - if (ctx != null) - { - ctx.Add(TokenName, stack.Peek()); - } - TokenName = ""; - Token.Length = 0; - ctx = stack.Peek(); - break; - - case '[': - if (QuoteMode) - { - Token.Append(aJSON[i]); - break; - } - - stack.Push(new JSONArray()); - if (ctx != null) - { - ctx.Add(TokenName, stack.Peek()); - } - TokenName = ""; - Token.Length = 0; - ctx = stack.Peek(); - break; - - case '}': - case ']': - if (QuoteMode) - { - - Token.Append(aJSON[i]); - break; - } - if (stack.Count == 0) - throw new Exception("JSON Parse: Too many closing brackets"); - - stack.Pop(); - if (Token.Length > 0 || TokenIsQuoted) - ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); - TokenIsQuoted = false; - TokenName = ""; - Token.Length = 0; - if (stack.Count > 0) - ctx = stack.Peek(); - break; - - case ':': - if (QuoteMode) - { - Token.Append(aJSON[i]); - break; - } - TokenName = Token.ToString(); - Token.Length = 0; - TokenIsQuoted = false; - break; - - case '"': - QuoteMode ^= true; - TokenIsQuoted |= QuoteMode; - break; - - case ',': - if (QuoteMode) - { - Token.Append(aJSON[i]); - break; - } - if (Token.Length > 0 || TokenIsQuoted) - ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); - TokenIsQuoted = false; - TokenName = ""; - Token.Length = 0; - TokenIsQuoted = false; - break; - - case '\r': - case '\n': - break; - - case ' ': - case '\t': - if (QuoteMode) - Token.Append(aJSON[i]); - break; - - case '\\': - ++i; - if (QuoteMode) - { - char C = aJSON[i]; - switch (C) - { - case 't': - Token.Append('\t'); - break; - case 'r': - Token.Append('\r'); - break; - case 'n': - Token.Append('\n'); - break; - case 'b': - Token.Append('\b'); - break; - case 'f': - Token.Append('\f'); - break; - case 'u': - { - string s = aJSON.Substring(i + 1, 4); - Token.Append((char)int.Parse( - s, - System.Globalization.NumberStyles.AllowHexSpecifier)); - i += 4; - break; - } - default: - Token.Append(C); - break; - } - } - break; - case '/': - if (allowLineComments && !QuoteMode && i + 1 < aJSON.Length && aJSON[i + 1] == '/') - { - while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') ; - break; - } - Token.Append(aJSON[i]); - break; - case '\uFEFF': // remove / ignore BOM (Byte Order Mark) - break; - - default: - Token.Append(aJSON[i]); - break; - } - ++i; - } - if (QuoteMode) - { - throw new Exception("JSON Parse: Quotation marks seems to be messed up."); - } - if (ctx == null) - return ParseElement(Token.ToString(), TokenIsQuoted); - return ctx; - } - - } - // End of JSONNode - - public partial class JSONArray : JSONNode - { - private List m_List = new List(); - private bool inline = false; - public override bool Inline - { - get { return inline; } - set { inline = value; } - } - - public override JSONNodeType Tag { get { return JSONNodeType.Array; } } - public override bool IsArray { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } - - public override JSONNode this[int aIndex] - { - get - { - if (aIndex < 0 || aIndex >= m_List.Count) - return new JSONLazyCreator(this); - return m_List[aIndex]; - } - set - { - if (value == null) - value = JSONNull.CreateOrGet(); - if (aIndex < 0 || aIndex >= m_List.Count) - m_List.Add(value); - else - m_List[aIndex] = value; - } - } - - public override JSONNode this[string aKey] - { - get { return new JSONLazyCreator(this); } - set - { - if (value == null) - value = JSONNull.CreateOrGet(); - m_List.Add(value); - } - } - - public override int Count - { - get { return m_List.Count; } - } - - public override void Add(string aKey, JSONNode aItem) - { - if (aItem == null) - aItem = JSONNull.CreateOrGet(); - m_List.Add(aItem); - } - - public override JSONNode Remove(int aIndex) - { - if (aIndex < 0 || aIndex >= m_List.Count) - return null; - JSONNode tmp = m_List[aIndex]; - m_List.RemoveAt(aIndex); - return tmp; - } - - public override JSONNode Remove(JSONNode aNode) - { - m_List.Remove(aNode); - return aNode; - } - - public override JSONNode Clone() - { - var node = new JSONArray(); - node.m_List.Capacity = m_List.Capacity; - foreach (var n in m_List) - { - if (n != null) - node.Add(n.Clone()); - else - node.Add(null); - } - return node; - } - - public override IEnumerable Children - { - get - { - foreach (JSONNode N in m_List) - yield return N; - } - } - - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append('['); - int count = m_List.Count; - if (inline) - aMode = JSONTextMode.Compact; - for (int i = 0; i < count; i++) - { - if (i > 0) - aSB.Append(','); - if (aMode == JSONTextMode.Indent) - aSB.AppendLine(); - - if (aMode == JSONTextMode.Indent) - aSB.Append(' ', aIndent + aIndentInc); - m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); - } - if (aMode == JSONTextMode.Indent) - aSB.AppendLine().Append(' ', aIndent); - aSB.Append(']'); - } - } - // End of JSONArray - - public partial class JSONObject : JSONNode - { - private Dictionary m_Dict = new Dictionary(); - - private bool inline = false; - public override bool Inline - { - get { return inline; } - set { inline = value; } - } - - public override JSONNodeType Tag { get { return JSONNodeType.Object; } } - public override bool IsObject { get { return true; } } - - public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); } - - - public override JSONNode this[string aKey] - { - get - { - if (m_Dict.ContainsKey(aKey)) - return m_Dict[aKey]; - else - return new JSONLazyCreator(this, aKey); - } - set - { - if (value == null) - value = JSONNull.CreateOrGet(); - if (m_Dict.ContainsKey(aKey)) - m_Dict[aKey] = value; - else - m_Dict.Add(aKey, value); - } - } - - public override JSONNode this[int aIndex] - { - get - { - if (aIndex < 0 || aIndex >= m_Dict.Count) - return null; - return m_Dict.ElementAt(aIndex).Value; - } - set - { - if (value == null) - value = JSONNull.CreateOrGet(); - if (aIndex < 0 || aIndex >= m_Dict.Count) - return; - string key = m_Dict.ElementAt(aIndex).Key; - m_Dict[key] = value; - } - } - - public override int Count - { - get { return m_Dict.Count; } - } - - public override void Add(string aKey, JSONNode aItem) - { - if (aItem == null) - aItem = JSONNull.CreateOrGet(); - - if (aKey != null) - { - if (m_Dict.ContainsKey(aKey)) - m_Dict[aKey] = aItem; - else - m_Dict.Add(aKey, aItem); - } - else - m_Dict.Add(Guid.NewGuid().ToString(), aItem); - } - - public override JSONNode Remove(string aKey) - { - if (!m_Dict.ContainsKey(aKey)) - return null; - JSONNode tmp = m_Dict[aKey]; - m_Dict.Remove(aKey); - return tmp; - } - - public override JSONNode Remove(int aIndex) - { - if (aIndex < 0 || aIndex >= m_Dict.Count) - return null; - var item = m_Dict.ElementAt(aIndex); - m_Dict.Remove(item.Key); - return item.Value; - } - - public override JSONNode Remove(JSONNode aNode) - { - try - { - var item = m_Dict.Where(k => k.Value == aNode).First(); - m_Dict.Remove(item.Key); - return aNode; - } - catch - { - return null; - } - } - - public override JSONNode Clone() - { - var node = new JSONObject(); - foreach (var n in m_Dict) - { - node.Add(n.Key, n.Value.Clone()); - } - return node; - } - - public override bool HasKey(string aKey) - { - return m_Dict.ContainsKey(aKey); - } - - public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) - { - JSONNode res; - if (m_Dict.TryGetValue(aKey, out res)) - return res; - return aDefault; - } - - public override IEnumerable Children - { - get - { - foreach (KeyValuePair N in m_Dict) - yield return N.Value; - } - } - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append('{'); - bool first = true; - if (inline) - aMode = JSONTextMode.Compact; - foreach (var k in m_Dict) - { - if (!first) - aSB.Append(','); - first = false; - if (aMode == JSONTextMode.Indent) - aSB.AppendLine(); - if (aMode == JSONTextMode.Indent) - aSB.Append(' ', aIndent + aIndentInc); - aSB.Append('\"').Append(Escape(k.Key)).Append('\"'); - if (aMode == JSONTextMode.Compact) - aSB.Append(':'); - else - aSB.Append(" : "); - k.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); - } - if (aMode == JSONTextMode.Indent) - aSB.AppendLine().Append(' ', aIndent); - aSB.Append('}'); - } - - } - // End of JSONObject - - public partial class JSONString : JSONNode - { - private string m_Data; - - public override JSONNodeType Tag { get { return JSONNodeType.String; } } - public override bool IsString { get { return true; } } - - public override Enumerator GetEnumerator() { return new Enumerator(); } - - - public override string Value - { - get { return m_Data; } - set - { - m_Data = value; - } - } - - public JSONString(string aData) - { - m_Data = aData; - } - public override JSONNode Clone() - { - return new JSONString(m_Data); - } - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append('\"').Append(Escape(m_Data)).Append('\"'); - } - public override bool Equals(object obj) - { - if (base.Equals(obj)) - return true; - string s = obj as string; - if (s != null) - return m_Data == s; - JSONString s2 = obj as JSONString; - if (s2 != null) - return m_Data == s2.m_Data; - return false; - } - public override int GetHashCode() - { - return m_Data.GetHashCode(); - } - } - // End of JSONString - - public partial class JSONNumber : JSONNode - { - private double m_Data; - - public override JSONNodeType Tag { get { return JSONNodeType.Number; } } - public override bool IsNumber { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } - - public override string Value - { - get { return m_Data.ToString(CultureInfo.InvariantCulture); } - set - { - double v; - if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) - m_Data = v; - } - } - - public override double AsDouble - { - get { return m_Data; } - set { m_Data = value; } - } - public override long AsLong - { - get { return (long)m_Data; } - set { m_Data = value; } - } - - public JSONNumber(double aData) - { - m_Data = aData; - } - - public JSONNumber(string aData) - { - Value = aData; - } - - public override JSONNode Clone() - { - return new JSONNumber(m_Data); - } - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append(Value); - } - private static bool IsNumeric(object value) - { - return value is int || value is uint - || value is float || value is double - || value is decimal - || value is long || value is ulong - || value is short || value is ushort - || value is sbyte || value is byte; - } - public override bool Equals(object obj) - { - if (obj == null) - return false; - if (base.Equals(obj)) - return true; - JSONNumber s2 = obj as JSONNumber; - if (s2 != null) - return m_Data == s2.m_Data; - if (IsNumeric(obj)) - return Convert.ToDouble(obj) == m_Data; - return false; - } - public override int GetHashCode() - { - return m_Data.GetHashCode(); - } - } - // End of JSONNumber - - public partial class JSONBool : JSONNode - { - private bool m_Data; - - public override JSONNodeType Tag { get { return JSONNodeType.Boolean; } } - public override bool IsBoolean { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } - - public override string Value - { - get { return m_Data.ToString(); } - set - { - bool v; - if (bool.TryParse(value, out v)) - m_Data = v; - } - } - public override bool AsBool - { - get { return m_Data; } - set { m_Data = value; } - } - - public JSONBool(bool aData) - { - m_Data = aData; - } - - public JSONBool(string aData) - { - Value = aData; - } - - public override JSONNode Clone() - { - return new JSONBool(m_Data); - } - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append((m_Data) ? "true" : "false"); - } - public override bool Equals(object obj) - { - if (obj == null) - return false; - if (obj is bool) - return m_Data == (bool)obj; - return false; - } - public override int GetHashCode() - { - return m_Data.GetHashCode(); - } - } - // End of JSONBool - - public partial class JSONNull : JSONNode - { - static JSONNull m_StaticInstance = new JSONNull(); - public static bool reuseSameInstance = true; - public static JSONNull CreateOrGet() - { - if (reuseSameInstance) - return m_StaticInstance; - return new JSONNull(); - } - private JSONNull() { } - - public override JSONNodeType Tag { get { return JSONNodeType.NullValue; } } - public override bool IsNull { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } - - public override string Value - { - get { return "null"; } - set { } - } - public override bool AsBool - { - get { return false; } - set { } - } - - public override JSONNode Clone() - { - return CreateOrGet(); - } - - public override bool Equals(object obj) - { - if (object.ReferenceEquals(this, obj)) - return true; - return (obj is JSONNull); - } - public override int GetHashCode() - { - return 0; - } - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append("null"); - } - } - // End of JSONNull - - internal partial class JSONLazyCreator : JSONNode - { - private JSONNode m_Node = null; - private string m_Key = null; - public override JSONNodeType Tag { get { return JSONNodeType.None; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } - - public JSONLazyCreator(JSONNode aNode) - { - m_Node = aNode; - m_Key = null; - } - - public JSONLazyCreator(JSONNode aNode, string aKey) - { - m_Node = aNode; - m_Key = aKey; - } - - private T Set(T aVal) where T : JSONNode - { - if (m_Key == null) - m_Node.Add(aVal); - else - m_Node.Add(m_Key, aVal); - m_Node = null; // Be GC friendly. - return aVal; - } - - public override JSONNode this[int aIndex] - { - get { return new JSONLazyCreator(this); } - set { Set(new JSONArray()).Add(value); } - } - - public override JSONNode this[string aKey] - { - get { return new JSONLazyCreator(this, aKey); } - set { Set(new JSONObject()).Add(aKey, value); } - } - - public override void Add(JSONNode aItem) - { - Set(new JSONArray()).Add(aItem); - } - - public override void Add(string aKey, JSONNode aItem) - { - Set(new JSONObject()).Add(aKey, aItem); - } - - public static bool operator ==(JSONLazyCreator a, object b) - { - if (b == null) - return true; - return System.Object.ReferenceEquals(a, b); - } - - public static bool operator !=(JSONLazyCreator a, object b) - { - return !(a == b); - } - - public override bool Equals(object obj) - { - if (obj == null) - return true; - return System.Object.ReferenceEquals(this, obj); - } - - public override int GetHashCode() - { - return 0; - } - - public override int AsInt - { - get { Set(new JSONNumber(0)); return 0; } - set { Set(new JSONNumber(value)); } - } - - public override float AsFloat - { - get { Set(new JSONNumber(0.0f)); return 0.0f; } - set { Set(new JSONNumber(value)); } - } - - public override double AsDouble - { - get { Set(new JSONNumber(0.0)); return 0.0; } - set { Set(new JSONNumber(value)); } - } - - public override long AsLong - { - get - { - if (longAsString) - Set(new JSONString("0")); - else - Set(new JSONNumber(0.0)); - return 0L; - } - set - { - if (longAsString) - Set(new JSONString(value.ToString())); - else - Set(new JSONNumber(value)); - } - } - - public override bool AsBool - { - get { Set(new JSONBool(false)); return false; } - set { Set(new JSONBool(value)); } - } - - public override JSONArray AsArray - { - get { return Set(new JSONArray()); } - } - - public override JSONObject AsObject - { - get { return Set(new JSONObject()); } - } - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append("null"); - } - } - // End of JSONLazyCreator - - public static class JSON - { - public static JSONNode Parse(string aJSON) - { - return JSONNode.Parse(aJSON); - } - - public static bool TryParse(string aJSON, out JSONNode aResult) - { - try - { - aResult = JSON.Parse(aJSON); - return true; - } - catch (Exception e) - { + public enum JSONNodeType + { + Array = 1, + Object = 2, + String = 3, + Number = 4, + NullValue = 5, + Boolean = 6, + None = 7, + Custom = 0xFF, + } + public enum JSONTextMode + { + Compact, + Indent + } + + public abstract partial class JSONNode + { + protected const string TOKEN_NULL = "null"; + protected const string TOKEN_TRUE = "true"; + protected const string TOKEN_FALSE = "false"; + + #region Enumerators + public struct Enumerator + { + private enum Type { None, Array, Object } + private Type type; + private Dictionary.Enumerator m_Object; + private List.Enumerator m_Array; + public bool IsValid { get { return type != Type.None; } } + + public Enumerator(List.Enumerator aArrayEnum) + { + type = Type.Array; + m_Object = default(Dictionary.Enumerator); + m_Array = aArrayEnum; + } + + public Enumerator(Dictionary.Enumerator aDictEnum) + { + type = Type.Object; + m_Object = aDictEnum; + m_Array = default(List.Enumerator); + } + + public KeyValuePair Current + { + get + { + if (type == Type.Array) + return new KeyValuePair(string.Empty, m_Array.Current); + else if (type == Type.Object) + return m_Object.Current; + return new KeyValuePair(string.Empty, null); + } + } + + public bool MoveNext() + { + if (type == Type.Array) + return m_Array.MoveNext(); + else if (type == Type.Object) + return m_Object.MoveNext(); + return false; + } + } + + public struct ValueEnumerator + { + private Enumerator m_Enumerator; + public ValueEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } + public ValueEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } + public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } + public JSONNode Current { get { return m_Enumerator.Current.Value; } } + public bool MoveNext() { return m_Enumerator.MoveNext(); } + public ValueEnumerator GetEnumerator() { return this; } + } + + public struct KeyEnumerator + { + private Enumerator m_Enumerator; + public KeyEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } + public KeyEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } + public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } + public string Current { get { return m_Enumerator.Current.Key; } } + public bool MoveNext() { return m_Enumerator.MoveNext(); } + public KeyEnumerator GetEnumerator() { return this; } + } + + public class LinqEnumerator : IEnumerator>, IEnumerable> + { + private JSONNode m_Node; + private Enumerator m_Enumerator; + + internal LinqEnumerator(JSONNode aNode) + { + m_Node = aNode; + if (m_Node != null) + m_Enumerator = m_Node.GetEnumerator(); + } + + public KeyValuePair Current { get { return m_Enumerator.Current; } } + object IEnumerator.Current { get { return m_Enumerator.Current; } } + public bool MoveNext() { return m_Enumerator.MoveNext(); } + + public void Dispose() + { + m_Node = null; + m_Enumerator = new Enumerator(); + } + + public IEnumerator> GetEnumerator() + { + return new LinqEnumerator(m_Node); + } + + public void Reset() + { + if (m_Node != null) + m_Enumerator = m_Node.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new LinqEnumerator(m_Node); + } + } + + #endregion Enumerators + + #region common interface + + public static bool forceASCII = false; // Use Unicode by default + public static bool longAsString = false; // lazy creator creates a JSONString instead of JSONNumber + public static bool allowLineComments = true; // allow "//"-style comments at the end of a line + + public abstract JSONNodeType Tag { get; } + + public virtual JSONNode this[int aIndex] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } + public virtual JSONNode this[string aKey] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } + + public virtual string Value { get { return null; } protected set { } } + + public virtual int Count { get { return 0; } } + + public virtual bool IsNumber { get { return false; } } + public virtual bool IsString { get { return false; } } + public virtual bool IsBoolean { get { return false; } } + public virtual bool IsNull { get { return false; } } + public virtual bool IsArray { get { return false; } } + public virtual bool IsObject { get { return false; } } + + public virtual bool Inline { get { return false; } set { } } + + public virtual void Add(string aKey, JSONNode aItem) + { + throw new NotImplementedException(); + } + + public virtual void Add(JSONNode aItem) + { + Add(null, aItem); + } + + public virtual JSONNode Remove(string aKey) + { + throw new NotImplementedException(); + } + + public virtual JSONNode Remove(int aIndex) + { + throw new NotImplementedException(); + } + + public virtual JSONNode Remove(JSONNode aNode) + { + throw new NotImplementedException(); + } + + public virtual JSONNode Clone() + { + throw new NotImplementedException(); + } + + public virtual IEnumerable Children + { + get + { + yield break; + } + } + + public IEnumerable DeepChildren + { + get + { + foreach (var C in Children) + foreach (var D in C.DeepChildren) + yield return D; + } + } + + public virtual bool HasKey(string aKey) + { + return false; + } + + public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) + { + return aDefault; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + WriteToStringBuilder(sb, 0, 0, JSONTextMode.Compact); + return sb.ToString(); + } + + public virtual string ToString(int aIndent) + { + StringBuilder sb = new StringBuilder(); + WriteToStringBuilder(sb, 0, aIndent, aIndent > 0 ? JSONTextMode.Indent : JSONTextMode.Compact); + return sb.ToString(); + } + + internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); + + public abstract Enumerator GetEnumerator(); + public IEnumerable> Linq { get { return new LinqEnumerator(this); } } + public KeyEnumerator Keys { get { return new KeyEnumerator(GetEnumerator()); } } + public ValueEnumerator Values { get { return new ValueEnumerator(GetEnumerator()); } } + + #endregion common interface + + #region typecasting properties + + public virtual double AsDouble + { + get + { + double v = 0.0; + if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) + return v; + return 0.0; + } + set + { + Value = value.ToString(CultureInfo.InvariantCulture); + } + } + + public virtual int AsInt + { + get { return (int)AsDouble; } + set { AsDouble = value; } + } + + public virtual float AsFloat + { + get { return (float)AsDouble; } + set { AsDouble = value; } + } + + public virtual bool AsBool + { + get + { + bool v = false; + if (bool.TryParse(Value, out v)) + return v; + return !string.IsNullOrEmpty(Value); + } + set + { + Value = (value) ? TOKEN_TRUE : TOKEN_FALSE; + } + } + + public virtual long AsLong + { + get + { + long val = 0; + if (long.TryParse(Value, out val)) + return val; + return 0L; + } + set + { + Value = value.ToString(); + } + } + + public virtual JSONArray AsArray + { + get + { + return this as JSONArray; + } + } + + public virtual JSONObject AsObject + { + get + { + return this as JSONObject; + } + } + + #endregion typecasting properties + + #region operators + + public static implicit operator JSONNode(string s) + { + return new JSONString(s); + } + + public static implicit operator string(JSONNode d) + { + return (d == null) ? null : d.Value; + } + + public static implicit operator JSONNode(double n) + { + return new JSONNumber(n); + } + + public static implicit operator double(JSONNode d) + { + return (d == null) ? 0 : d.AsDouble; + } + + public static implicit operator JSONNode(float n) + { + return new JSONNumber(n); + } + + public static implicit operator float(JSONNode d) + { + return (d == null) ? 0 : d.AsFloat; + } + + public static implicit operator JSONNode(int n) + { + return new JSONNumber(n); + } + + public static implicit operator int(JSONNode d) + { + return (d == null) ? 0 : d.AsInt; + } + + public static implicit operator JSONNode(long n) + { + if (longAsString) + return new JSONString(n.ToString()); + return new JSONNumber(n); + } + + public static implicit operator long(JSONNode d) + { + return (d == null) ? 0L : d.AsLong; + } + + public static implicit operator JSONNode(bool b) + { + return new JSONBool(b); + } + + public static implicit operator bool(JSONNode d) + { + return (d == null) ? false : d.AsBool; + } + + public static implicit operator JSONNode(KeyValuePair aKeyValue) + { + return aKeyValue.Value; + } + + public static bool operator ==(JSONNode a, object b) + { + if (ReferenceEquals(a, b)) + return true; + bool aIsNull = a is JSONNull || ReferenceEquals(a, null) || a is JSONLazyCreator; + bool bIsNull = b is JSONNull || ReferenceEquals(b, null) || b is JSONLazyCreator; + if (aIsNull && bIsNull) + return true; + return !aIsNull && a.Equals(b); + } + + public static bool operator !=(JSONNode a, object b) + { + return !(a == b); + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + #endregion operators + + [ThreadStatic] + private static StringBuilder m_EscapeBuilder; + + internal static StringBuilder EscapeBuilder + { + get + { + if (m_EscapeBuilder == null) + m_EscapeBuilder = new StringBuilder(); + return m_EscapeBuilder; + } + } + + internal static string Escape(string aText) + { + var sb = EscapeBuilder; + sb.Length = 0; + if (sb.Capacity < aText.Length + aText.Length / 10) + sb.Capacity = aText.Length + aText.Length / 10; + foreach (char c in aText) + { + switch (c) + { + case '\\': + sb.Append("\\\\"); + break; + case '\"': + sb.Append("\\\""); + break; + case '\n': + sb.Append("\\n"); + break; + case '\r': + sb.Append("\\r"); + break; + case '\t': + sb.Append("\\t"); + break; + case '\b': + sb.Append("\\b"); + break; + case '\f': + sb.Append("\\f"); + break; + default: + if (c < ' ' || (forceASCII && c > 127)) + { + ushort val = c; + sb.Append("\\u").Append(val.ToString("X4")); + } + else + sb.Append(c); + break; + } + } + string result = sb.ToString(); + sb.Length = 0; + return result; + } + + private static JSONNode ParseElement(string token, bool quoted) + { + if (quoted) + return token; + if (token.Equals(TOKEN_FALSE, StringComparison.InvariantCultureIgnoreCase) + || token.Equals(TOKEN_TRUE, StringComparison.InvariantCultureIgnoreCase)) + return token.Equals(TOKEN_TRUE, StringComparison.InvariantCultureIgnoreCase); + if (token.Equals(TOKEN_NULL, StringComparison.InvariantCultureIgnoreCase)) + return JSONNull.CreateOrGet(); + + if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out double val)) + return val; + else + return token; + } + + public static JSONNode Parse(string aJSON) + { + Stack stack = new Stack(); + JSONNode ctx = null; + int i = 0; + StringBuilder Token = new StringBuilder(); + string TokenName = null; + bool QuoteMode = false; + bool TokenIsQuoted = false; + while (i < aJSON.Length) + { + switch (aJSON[i]) + { + case '{': + if (QuoteMode) + { + Token.Append(aJSON[i]); + break; + } + stack.Push(new JSONObject()); + if (ctx != null) + { + ctx.Add(TokenName, stack.Peek()); + } + TokenName = null; + Token.Length = 0; + ctx = stack.Peek(); + break; + + case '[': + if (QuoteMode) + { + Token.Append(aJSON[i]); + break; + } + + stack.Push(new JSONArray()); + if (ctx != null) + { + ctx.Add(TokenName, stack.Peek()); + } + TokenName = null; + Token.Length = 0; + ctx = stack.Peek(); + break; + + case '}': + case ']': + if (QuoteMode) + { + + Token.Append(aJSON[i]); + break; + } + if (stack.Count == 0) + throw new Exception("JSON Parse: Too many closing brackets"); + + stack.Pop(); + if (Token.Length > 0 || TokenIsQuoted) + ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); + TokenIsQuoted = false; + TokenName = null; + Token.Length = 0; + if (stack.Count > 0) + ctx = stack.Peek(); + break; + + case ':': + if (QuoteMode) + { + Token.Append(aJSON[i]); + break; + } + TokenName = Token.ToString(); + Token.Length = 0; + TokenIsQuoted = false; + break; + + case '"': + QuoteMode ^= true; + TokenIsQuoted |= QuoteMode; + break; + + case ',': + if (QuoteMode) + { + Token.Append(aJSON[i]); + break; + } + if (Token.Length > 0 || TokenIsQuoted) + ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); + TokenIsQuoted = false; + TokenName = null; + Token.Length = 0; + TokenIsQuoted = false; + break; + + case '\r': + case '\n': + break; + + case ' ': + case '\t': + if (QuoteMode) + Token.Append(aJSON[i]); + break; + + case '\\': + ++i; + if (QuoteMode) + { + char C = aJSON[i]; + switch (C) + { + case 't': + Token.Append('\t'); + break; + case 'r': + Token.Append('\r'); + break; + case 'n': + Token.Append('\n'); + break; + case 'b': + Token.Append('\b'); + break; + case 'f': + Token.Append('\f'); + break; + case 'u': + { + string s = aJSON.Substring(i + 1, 4); + Token.Append((char)int.Parse( + s, + System.Globalization.NumberStyles.AllowHexSpecifier)); + i += 4; + break; + } + default: + Token.Append(C); + break; + } + } + break; + case '/': + if (allowLineComments && !QuoteMode && i + 1 < aJSON.Length && aJSON[i + 1] == '/') + { + while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') ; + break; + } + Token.Append(aJSON[i]); + break; + case '\uFEFF': // remove / ignore BOM (Byte Order Mark) + break; + + default: + Token.Append(aJSON[i]); + break; + } + ++i; + } + if (QuoteMode) + { + throw new Exception("JSON Parse: Quotation marks seems to be messed up."); + } + if (ctx == null) + return ParseElement(Token.ToString(), TokenIsQuoted); + return ctx; + } + } + // End of JSONNode + + public partial class JSONArray : JSONNode, IList + { + private List m_List = new List(); + private bool inline = false; + public override bool Inline + { + get { return inline; } + set { inline = value; } + } + + public override JSONNodeType Tag { get { return JSONNodeType.Array; } } + public override bool IsArray { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } + public bool IsReadOnly => false; + + public override JSONNode this[int aIndex] + { + get + { + if (aIndex < 0 || aIndex >= m_List.Count) + throw new IndexOutOfRangeException(); + + return m_List[aIndex]; + } + set + { + if (aIndex < 0 || aIndex >= m_List.Count) + throw new IndexOutOfRangeException(); + + if (value == null) + value = JSONNull.CreateOrGet(); + + m_List[aIndex] = value; + } + } + + public override int Count + { + get { return m_List.Count; } + } + + public override void Add(string aKey, JSONNode aItem) + { + if (aKey != null) + { + throw new NotImplementedException(); + } + + if (aItem == null) + aItem = JSONNull.CreateOrGet(); + m_List.Add(aItem); + } + + public override JSONNode Remove(string aKey) + { + throw new NotImplementedException(); + } + + public override JSONNode Remove(int aIndex) + { + if (aIndex < 0 || aIndex >= m_List.Count) + { + throw new IndexOutOfRangeException(); + } + + JSONNode tmp = m_List[aIndex]; + m_List.RemoveAt(aIndex); + return tmp; + } + + public override JSONNode Remove(JSONNode aNode) + { + m_List.Remove(aNode); + return aNode; + } + + public override JSONNode Clone() + { + var node = new JSONArray(); + node.m_List.Capacity = m_List.Capacity; + foreach (var n in m_List) + { + if (n != null) + node.Add(n.Clone()); + else + node.Add(null); + } + return node; + } + + public override IEnumerable Children + { + get + { + foreach (JSONNode N in m_List) + yield return N; + } + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append('['); + int count = m_List.Count; + if (inline) + aMode = JSONTextMode.Compact; + for (int i = 0; i < count; i++) + { + if (i > 0) + aSB.Append(','); + if (aMode == JSONTextMode.Indent) + aSB.AppendLine(); + + if (aMode == JSONTextMode.Indent) + aSB.Append(' ', aIndent + aIndentInc); + m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); + } + if (aMode == JSONTextMode.Indent) + aSB.AppendLine().Append(' ', aIndent); + aSB.Append(']'); + } + + public int IndexOf(JSONNode item) + { + return m_List.IndexOf(item); + } + + public void Insert(int index, JSONNode item) + { + m_List.Insert(index, item); + } + + public void RemoveAt(int index) + { + m_List.RemoveAt(index); + } + + public void Clear() + { + m_List.Clear(); + } + + public bool Contains(JSONNode item) + { + return m_List.Contains(item); + } + + public void CopyTo(JSONNode[] array, int arrayIndex) + { + m_List.CopyTo(array, arrayIndex); + } + + bool ICollection.Remove(JSONNode item) + { + return m_List.Remove(item); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return m_List.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return m_List.GetEnumerator(); + } + } + // End of JSONArray + + public partial class JSONObject : JSONNode, IDictionary + { + private Dictionary m_Dict = new Dictionary(); + + private bool inline = false; + + public override bool Inline + { + get { return inline; } + set { inline = value; } + } + + public override JSONNodeType Tag { get { return JSONNodeType.Object; } } + public override bool IsObject { get { return true; } } + + public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); } + + ICollection IDictionary.Keys + { + get + { + return m_Dict.Keys; + } + } + + ICollection IDictionary.Values + { + get + { + return m_Dict.Values; + } + } + + public bool IsReadOnly => false; + + public override JSONNode this[string aKey] + { + get + { + if (m_Dict.ContainsKey(aKey)) + return m_Dict[aKey]; + else + return new JSONLazyCreator(this, aKey); + } + set + { + if (value == null) + value = JSONNull.CreateOrGet(); + if (m_Dict.ContainsKey(aKey)) + m_Dict[aKey] = value; + else + m_Dict.Add(aKey, value); + } + } + + public override int Count + { + get { return m_Dict.Count; } + } + + public override void Add(string aKey, JSONNode aItem) + { + if (aKey == null) + { + throw new NotImplementedException(); + } + + if (aItem == null) + aItem = JSONNull.CreateOrGet(); + + m_Dict[aKey] = aItem; + } + + public override JSONNode Remove(string aKey) + { + if (!m_Dict.ContainsKey(aKey)) + return null; + JSONNode tmp = m_Dict[aKey]; + m_Dict.Remove(aKey); + return tmp; + } + + public override JSONNode Remove(int aIndex) + { + throw new NotImplementedException(); + } + + public override JSONNode Remove(JSONNode aNode) + { + foreach (var kvp in m_Dict) + { + if (kvp.Value == aNode) + { + m_Dict.Remove(kvp.Key); + return kvp.Value; + } + } + + return null; + } + + public override JSONNode Clone() + { + var node = new JSONObject(); + foreach (var n in m_Dict) + { + node.Add(n.Key, n.Value.Clone()); + } + return node; + } + + public override bool HasKey(string aKey) + { + return m_Dict.ContainsKey(aKey); + } + + public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) + { + JSONNode res; + if (m_Dict.TryGetValue(aKey, out res)) + return res; + return aDefault; + } + + public override IEnumerable Children + { + get + { + foreach (KeyValuePair N in m_Dict) + yield return N.Value; + } + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append('{'); + bool first = true; + if (inline) + aMode = JSONTextMode.Compact; + foreach (var k in m_Dict) + { + if (!first) + aSB.Append(','); + first = false; + if (aMode == JSONTextMode.Indent) + aSB.AppendLine(); + if (aMode == JSONTextMode.Indent) + aSB.Append(' ', aIndent + aIndentInc); + aSB.Append('\"').Append(Escape(k.Key)).Append('\"'); + if (aMode == JSONTextMode.Compact) + aSB.Append(':'); + else + aSB.Append(" : "); + k.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); + } + if (aMode == JSONTextMode.Indent) + aSB.AppendLine().Append(' ', aIndent); + aSB.Append('}'); + } + + public bool ContainsKey(string key) + { + return m_Dict.ContainsKey(key); + } + + bool IDictionary.Remove(string key) + { + return m_Dict.Remove(key); + } + + public bool TryGetValue(string key, out JSONNode value) + { + return m_Dict.TryGetValue(key, out value); + } + + public void Add(KeyValuePair item) + { + ((IDictionary)m_Dict).Add(item); + } + + public void Clear() + { + m_Dict.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return m_Dict.ContainsKey(item.Key); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((IDictionary)m_Dict).CopyTo(array, arrayIndex); + } + + public bool Remove(KeyValuePair item) + { + return ((IDictionary)m_Dict).Remove(item); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return m_Dict.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return m_Dict.GetEnumerator(); + } + } + // End of JSONObject + + public partial class JSONString : JSONNode + { + private string m_Data; + + public override JSONNodeType Tag { get { return JSONNodeType.String; } } + public override bool IsString { get { return true; } } + public override bool IsNull { get { return m_Data == null; } } + + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public override string Value + { + get { return m_Data; } + protected set + { + m_Data = value; + } + } + + public JSONString(string aData) + { + m_Data = aData; + } + + public override JSONNode Clone() + { + return new JSONString(m_Data); + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + if (m_Data == null) + { + aSB.Append(TOKEN_NULL); + return; + } + + aSB.Append('\"').Append(Escape(m_Data)).Append('\"'); + } + + public override bool Equals(object obj) + { + switch (obj) + { + case JSONNull nullObj: + return m_Data == null; + case string stringObj: + return m_Data.Equals(stringObj); + case JSONString jsonStringObj: + return m_Data.Equals(jsonStringObj.m_Data); + default: + return base.Equals(obj); + } + } + + public override int GetHashCode() + { + return m_Data.GetHashCode(); + } + } + // End of JSONString + + public partial class JSONNumber : JSONNode + { + private double m_Data; + + public override JSONNodeType Tag { get { return JSONNodeType.Number; } } + public override bool IsNumber { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public override string Value + { + get { return m_Data.ToString("R", CultureInfo.InvariantCulture); } + protected set + { + double v; + if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) + m_Data = v; + } + } + + public override double AsDouble + { + get { return m_Data; } + set { m_Data = value; } + } + + public override long AsLong + { + get { return (long)m_Data; } + set { m_Data = value; } + } + + public JSONNumber(double aData) + { + m_Data = aData; + } + + public JSONNumber(string aData) + { + Value = aData; + } + + public override JSONNode Clone() + { + return new JSONNumber(m_Data); + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append(Value); + } + + internal static bool IsNumeric(object value) + { + return value is int || value is uint + || value is float || value is double + || value is decimal + || value is long || value is ulong + || value is short || value is ushort + || value is sbyte || value is byte; + } + + public override bool Equals(object obj) + { + switch (obj) + { + case null: + return false; + case JSONNumber jsonNumber: + return m_Data == jsonNumber.m_Data; + default: + if (IsNumeric(obj)) + return Convert.ToDouble(obj) == m_Data; + return base.Equals(obj); + } + } + + public override int GetHashCode() + { + return m_Data.GetHashCode(); + } + } + // End of JSONNumber + + public partial class JSONBool : JSONNode + { + private bool m_Data; + + public override JSONNodeType Tag { get { return JSONNodeType.Boolean; } } + public override bool IsBoolean { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public override string Value + { + get { return m_Data.ToString(); } + protected set + { + bool v; + if (bool.TryParse(value, out v)) + m_Data = v; + } + } + + public override bool AsBool + { + get { return m_Data; } + set { m_Data = value; } + } + + public JSONBool(bool aData) + { + m_Data = aData; + } + + public JSONBool(string aData) + { + Value = aData; + } + + public override JSONNode Clone() + { + return new JSONBool(m_Data); + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append((m_Data) ? TOKEN_TRUE : TOKEN_FALSE); + } + + public override bool Equals(object obj) + { + switch (obj) + { + case null: + return false; + case JSONBool jsonBool: + return m_Data == jsonBool.m_Data; + case bool boolObj: + return m_Data == boolObj; + default: + return false; + } + } + + public override int GetHashCode() + { + return m_Data.GetHashCode(); + } + } + // End of JSONBool + + public partial class JSONNull : JSONNode + { + static JSONNull m_StaticInstance = new JSONNull(); + public static bool reuseSameInstance = true; + public static JSONNull CreateOrGet() + { + if (reuseSameInstance) + return m_StaticInstance; + return new JSONNull(); + } + + private JSONNull() { } + + public override JSONNodeType Tag { get { return JSONNodeType.NullValue; } } + public override bool IsNull { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public override string Value + { + get { return null; } + protected set { } + } + + public override bool AsBool + { + get { return false; } + set { } + } + + public override JSONNode Clone() + { + return CreateOrGet(); + } + + public override bool Equals(object obj) + { + if (object.ReferenceEquals(this, obj)) + return true; + return (obj is JSONNull); + } + + public override int GetHashCode() + { + return 0; + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append(TOKEN_NULL); + } + } + // End of JSONNull + + internal partial class JSONLazyCreator : JSONNode + { + private JSONNode m_Node = null; + private string m_Key = null; + public override JSONNodeType Tag { get { return JSONNodeType.None; } } + public override bool IsNull { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public JSONLazyCreator(JSONNode aNode) + { + m_Node = aNode; + m_Key = null; + } + + public JSONLazyCreator(JSONNode aNode, string aKey) + { + m_Node = aNode; + m_Key = aKey; + } + + private T Set(T aVal) where T : JSONNode + { + if (m_Key == null) + m_Node.Add(aVal); + else + m_Node.Add(m_Key, aVal); + m_Node = null; // Be GC friendly. + return aVal; + } + + public override JSONNode this[int aIndex] + { + get { return new JSONLazyCreator(this); } + set { Set(new JSONArray()).Add(value); } + } + + public override JSONNode this[string aKey] + { + get { return new JSONLazyCreator(this, aKey); } + set { Set(new JSONObject()).Add(aKey, value); } + } + + public override void Add(JSONNode aItem) + { + Set(new JSONArray()).Add(aItem); + } + + public override void Add(string aKey, JSONNode aItem) + { + Set(new JSONObject()).Add(aKey, aItem); + } + + public static bool operator ==(JSONLazyCreator a, object b) + { + return a.Equals(b); + } + + public static bool operator !=(JSONLazyCreator a, object b) + { + return !(a == b); + } + + public override bool Equals(object obj) + { + if (obj == null) + return true; + if (obj is JSONNull) + return true; + + return System.Object.ReferenceEquals(this, obj); + } + + public override int GetHashCode() + { + return 0; + } + + public override int AsInt + { + get { Set(new JSONNumber(0)); return 0; } + set { Set(new JSONNumber(value)); } + } + + public override float AsFloat + { + get { Set(new JSONNumber(0.0f)); return 0.0f; } + set { Set(new JSONNumber(value)); } + } + + public override double AsDouble + { + get { Set(new JSONNumber(0.0)); return 0.0; } + set { Set(new JSONNumber(value)); } + } + + public override long AsLong + { + get + { + if (longAsString) + Set(new JSONString("0")); + else + Set(new JSONNumber(0.0)); + return 0L; + } + set + { + if (longAsString) + Set(new JSONString(value.ToString())); + else + Set(new JSONNumber(value)); + } + } + + public override bool AsBool + { + get { Set(new JSONBool(false)); return false; } + set { Set(new JSONBool(value)); } + } + + public override JSONArray AsArray + { + get { return Set(new JSONArray()); } + } + + public override JSONObject AsObject + { + get { return Set(new JSONObject()); } + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append(TOKEN_NULL); + } + } + // End of JSONLazyCreator + + public static partial class JSON + { + public static JSONNode Parse(string aJSON) + { + return JSONNode.Parse(aJSON); + } + + public static bool TryParse(string aJSON, out JSONNode aResult) + { + try + { + aResult = JSON.Parse(aJSON); + return true; + } + catch (Exception e) + { #if UNITY_5_3_OR_NEWER - UnityEngine.Debug.LogException(e); + UnityEngine.Debug.LogException(e); #endif - aResult = null; - return false; - } - } - } + aResult = null; + return false; + } + } + } } diff --git a/Runtime/SimpleJSONBinary.cs b/Runtime/SimpleJSONBinary.cs index df72f13..6308e67 100644 --- a/Runtime/SimpleJSONBinary.cs +++ b/Runtime/SimpleJSONBinary.cs @@ -4,32 +4,32 @@ * serialize a JSON object tree into a compact binary format. Optionally the * binary stream can be compressed with the SharpZipLib when using the define * "USE_SharpZipLib" - * + * * Those methods where originally part of the framework but since it's rarely * used I've extracted this part into this seperate module file. - * + * * You can use the define "SimpleJSON_ExcludeBinary" to selectively disable * this extension without the need to remove the file from the project. - * + * * If you want to use compression when saving to file / stream / B64 you have to include * SharpZipLib ( http://www.icsharpcode.net/opensource/sharpziplib/ ) in your project and * define "USE_SharpZipLib" at the top of the file - * - * + * + * * The MIT License (MIT) - * + * * Copyright (c) 2012-2017 Markus Göbel (Bunny83) - * + * * 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 @@ -37,7 +37,7 @@ * 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; @@ -64,10 +64,10 @@ public void SaveToCompressedStream(System.IO.Stream aData) gzipOut.Close(); } } - + public void SaveToCompressedFile(string aFileName) { - + System.IO.Directory.CreateDirectory((new System.IO.FileInfo(aFileName)).Directory.FullName); using(var F = System.IO.File.OpenWrite(aFileName)) { @@ -83,7 +83,7 @@ public string SaveToCompressedBase64() return System.Convert.ToBase64String(stream.ToArray()); } } - + #else public void SaveToCompressedStream(System.IO.Stream aData) { diff --git a/Runtime/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs index 63b42cb..2e5feff 100644 --- a/Runtime/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -1,6 +1,6 @@ #region License and information /* * * * * - * + * * Unity extension for the SimpleJSON framework. It does only work together with * the SimpleJSON.cs * It provides several helpers and conversion operators to serialize/deserialize @@ -11,22 +11,22 @@ * control what node type should be used for serializing the given type. So a * Vector3 as array would look like [12,32,24] and {"x":12, "y":32, "z":24} as * object. - * - * + * + * * The MIT License (MIT) - * + * * Copyright (c) 2012-2017 Markus Göbel (Bunny83) - * + * * 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 @@ -34,7 +34,7 @@ * 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 License and information @@ -44,8 +44,8 @@ namespace SimpleJSON { public enum JSONContainerType { Array, Object } - public partial class JSONNode - { + public partial class JSONNode + { public static JSONContainerType VectorContainerType = JSONContainerType.Array; public static JSONContainerType QuaternionContainerType = JSONContainerType.Array; public static JSONContainerType RectContainerType = JSONContainerType.Array; @@ -58,37 +58,37 @@ private static JSONNode GetContainer(JSONContainerType aType) #region implicit conversion operators public static implicit operator JSONNode(Vector2 aVec) - { + { JSONNode n = GetContainer(VectorContainerType); n.WriteVector2(aVec); - return n; - } - public static implicit operator JSONNode(Vector3 aVec) - { + return n; + } + public static implicit operator JSONNode(Vector3 aVec) + { JSONNode n = GetContainer(VectorContainerType); n.WriteVector3(aVec); return n; } public static implicit operator JSONNode(Vector4 aVec) - { + { JSONNode n = GetContainer(VectorContainerType); n.WriteVector4(aVec); return n; } public static implicit operator JSONNode(Quaternion aRot) - { + { JSONNode n = GetContainer(QuaternionContainerType); n.WriteQuaternion(aRot); return n; } public static implicit operator JSONNode(Rect aRect) - { + { JSONNode n = GetContainer(RectContainerType); n.WriteRect(aRect); return n; } public static implicit operator JSONNode(RectOffset aRect) - { + { JSONNode n = GetContainer(RectContainerType); n.WriteRectOffset(aRect); return n; @@ -98,26 +98,32 @@ public static implicit operator Vector2(JSONNode aNode) { return aNode.ReadVector2(); } + public static implicit operator Vector3(JSONNode aNode) { return aNode.ReadVector3(); } + public static implicit operator Vector4(JSONNode aNode) { return aNode.ReadVector4(); } + public static implicit operator Quaternion(JSONNode aNode) { return aNode.ReadQuaternion(); } + public static implicit operator Rect(JSONNode aNode) { return aNode.ReadRect(); } + public static implicit operator RectOffset(JSONNode aNode) { return aNode.ReadRectOffset(); } + #endregion implicit conversion operators #region Vector2 @@ -129,6 +135,7 @@ public Vector2 ReadVector2(Vector2 aDefault) return new Vector2(this[0].AsFloat, this[1].AsFloat); return aDefault; } + public Vector2 ReadVector2(string aXName, string aYName) { if (IsObject) @@ -142,6 +149,7 @@ public Vector2 ReadVector2() { return ReadVector2(Vector2.zero); } + public JSONNode WriteVector2(Vector2 aVec, string aXName = "x", string aYName = "y") { if (IsObject) @@ -169,16 +177,19 @@ public Vector3 ReadVector3(Vector3 aDefault) return new Vector3(this[0].AsFloat, this[1].AsFloat, this[2].AsFloat); return aDefault; } + public Vector3 ReadVector3(string aXName, string aYName, string aZName) { if (IsObject) return new Vector3(this[aXName].AsFloat, this[aYName].AsFloat, this[aZName].AsFloat); return Vector3.zero; } + public Vector3 ReadVector3() { return ReadVector3(Vector3.zero); } + public JSONNode WriteVector3(Vector3 aVec, string aXName = "x", string aYName = "y", string aZName = "z") { if (IsObject) @@ -208,10 +219,12 @@ public Vector4 ReadVector4(Vector4 aDefault) return new Vector4(this[0].AsFloat, this[1].AsFloat, this[2].AsFloat, this[3].AsFloat); return aDefault; } + public Vector4 ReadVector4() { return ReadVector4(Vector4.zero); } + public JSONNode WriteVector4(Vector4 aVec) { if (IsObject) @@ -243,10 +256,12 @@ public Quaternion ReadQuaternion(Quaternion aDefault) return new Quaternion(this[0].AsFloat, this[1].AsFloat, this[2].AsFloat, this[3].AsFloat); return aDefault; } + public Quaternion ReadQuaternion() { return ReadQuaternion(Quaternion.identity); } + public JSONNode WriteQuaternion(Quaternion aRot) { if (IsObject) @@ -278,10 +293,12 @@ public Rect ReadRect(Rect aDefault) return new Rect(this[0].AsFloat, this[1].AsFloat, this[2].AsFloat, this[3].AsFloat); return aDefault; } + public Rect ReadRect() { return ReadRect(new Rect()); } + public JSONNode WriteRect(Rect aRect) { if (IsObject) @@ -313,10 +330,12 @@ public RectOffset ReadRectOffset(RectOffset aDefault) return new RectOffset(this[0].AsInt, this[1].AsInt, this[2].AsInt, this[3].AsInt); return aDefault; } + public RectOffset ReadRectOffset() { return ReadRectOffset(new RectOffset()); } + public JSONNode WriteRectOffset(RectOffset aRect) { if (IsObject) @@ -352,6 +371,7 @@ public Matrix4x4 ReadMatrix() } return result; } + public JSONNode WriteMatrix(Matrix4x4 aMatrix) { if (IsArray) From a8315f920a9284ab059b856dedecd64513b99589 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Tue, 8 Sep 2020 20:12:29 -0700 Subject: [PATCH 04/23] Utility methods to serialize lists and dictionaries of basic types. --- Runtime/SimpleJSONSerializer.cs | 182 ++++++++++++++++++++++++++++++++ Runtime/SimpleJSONUnity.cs | 50 +++++---- 2 files changed, 210 insertions(+), 22 deletions(-) create mode 100644 Runtime/SimpleJSONSerializer.cs diff --git a/Runtime/SimpleJSONSerializer.cs b/Runtime/SimpleJSONSerializer.cs new file mode 100644 index 0000000..0804ca6 --- /dev/null +++ b/Runtime/SimpleJSONSerializer.cs @@ -0,0 +1,182 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace SimpleJSON +{ + public interface ISimpleJSONSerializable + { + JSONNode ToJSONNode(); + } + + public static partial class JSON + { + public static string Serialize(object value, int aIndent = 0) + { + return ToJSONNode(value).ToString(aIndent); + } + + public static JSONNode ToJSONNode(object value) + { + switch (value) + { + case null: + return JSONNull.CreateOrGet(); + case JSONNode jsonValue: + return jsonValue; + case string strValue: + return new JSONString(strValue); + case char charValue: + return new JSONString(new string(charValue, 1)); + case bool boolValue: + return new JSONBool(boolValue); + case IList listValue: + return ToJSONNode(listValue); + case IDictionary dictValue: + return ToJSONNode(dictValue); + case ISimpleJSONSerializable serializableValue: + return serializableValue.ToJSONNode(); +#if UNITY_5_3_OR_NEWER + case Vector2 v2Value: + return v2Value.ToJSONNode(); + case Vector3 v3Value: + return v3Value.ToJSONNode(); + case Vector4 v4Value: + return v4Value.ToJSONNode(); + case Quaternion quatValue: + return quatValue.ToJSONNode(); + case Rect rectValue: + return rectValue.ToJSONNode(); + case RectOffset rectOffsetValue: + return rectOffsetValue.ToJSONNode(); + case Matrix4x4 matrixValue: + return matrixValue.ToJSONNode(); +#endif + + default: + if (JSONNumber.IsNumeric(value)) + { + return new JSONNumber(System.Convert.ToDouble(value)); + } + + return new JSONString(value.ToString()); + } + } + + private static JSONArray ToJSONNode(IList list) + { + var jsonArray = new JSONArray(); + + for (int i = 0; i < list.Count; i++) + { + jsonArray.Add(ToJSONNode(list[i])); + } + + return jsonArray; + } + + private static JSONObject ToJSONNode(IDictionary dict) + { + var jsonObject = new JSONObject(); + + foreach (object key in dict.Keys) + { + jsonObject.Add(key.ToString(), ToJSONNode(dict[key])); + } + + return jsonObject; + } + + #region Extension methods +#if UNITY_5_3_OR_NEWER + public static JSONNode ToJSONNode(this Vector2 vector2, bool asArray = false) + { + if (asArray) + { + return new JSONArray().WriteVector2(vector2); + } + else + { + return new JSONObject().WriteVector2(vector2); + } + } + + public static JSONNode ToJSONNode(this Vector3 vector3, bool asArray = false) + { + if (asArray) + { + return new JSONArray().WriteVector3(vector3); + } + else + { + return new JSONObject().WriteVector3(vector3); + } + } + + public static JSONNode ToJSONNode(this Vector4 vector4, bool asArray = false) + { + if (asArray) + { + return new JSONArray().WriteVector4(vector4); + } + else + { + return new JSONObject().WriteVector4(vector4); + } + } + + public static JSONNode ToJSONNode(this Quaternion quaternion, bool asArray = false) + { + if (asArray) + { + return new JSONArray().WriteQuaternion(quaternion); + } + else + { + return new JSONObject().WriteQuaternion(quaternion); + } + } + + public static JSONNode ToJSONNode(this Rect rect, bool asArray = false) + { + if (asArray) + { + return new JSONArray().WriteRect(rect); + } + else + { + return new JSONObject().WriteRect(rect); + } + } + + public static JSONNode ToJSONNode(this RectOffset rectOffset, bool asArray = false) + { + if (asArray) + { + return new JSONArray().WriteRectOffset(rectOffset); + } + else + { + return new JSONObject().WriteRectOffset(rectOffset); + } + } + + public static JSONNode ToJSONNode(this Matrix4x4 matrix) + { + return new JSONArray().WriteMatrix(matrix); + } +#endif + + public static JSONNode ToJSONNode(this List list) + { + return ToJSONNode((IList)list); + } + + public static JSONNode ToJSONNode(this Dictionary dict) + { + return ToJSONNode((IDictionary)dict); + } + + #endregion + } +} diff --git a/Runtime/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs index 2e5feff..2901cd2 100644 --- a/Runtime/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -48,7 +48,7 @@ public partial class JSONNode { public static JSONContainerType VectorContainerType = JSONContainerType.Array; public static JSONContainerType QuaternionContainerType = JSONContainerType.Array; - public static JSONContainerType RectContainerType = JSONContainerType.Array; + public static JSONContainerType RectContainerType = JSONContainerType.Object; private static JSONNode GetContainer(JSONContainerType aType) { if (aType == JSONContainerType.Array) @@ -161,8 +161,11 @@ public JSONNode WriteVector2(Vector2 aVec, string aXName = "x", string aYName = else if (IsArray) { Inline = true; - this[0].AsFloat = aVec.x; - this[1].AsFloat = aVec.y; + + for (int i=0; i < 2; i++) + { + Add(aVec[i]); + } } return this; } @@ -202,9 +205,10 @@ public JSONNode WriteVector3(Vector3 aVec, string aXName = "x", string aYName = else if (IsArray) { Inline = true; - this[0].AsFloat = aVec.x; - this[1].AsFloat = aVec.y; - this[2].AsFloat = aVec.z; + for (int i = 0; i < 3; i++) + { + Add(aVec[i]); + } } return this; } @@ -238,10 +242,11 @@ public JSONNode WriteVector4(Vector4 aVec) else if (IsArray) { Inline = true; - this[0].AsFloat = aVec.x; - this[1].AsFloat = aVec.y; - this[2].AsFloat = aVec.z; - this[3].AsFloat = aVec.w; + + for (int i = 0; i < 4; i++) + { + Add(aVec[i]); + } } return this; } @@ -275,10 +280,11 @@ public JSONNode WriteQuaternion(Quaternion aRot) else if (IsArray) { Inline = true; - this[0].AsFloat = aRot.x; - this[1].AsFloat = aRot.y; - this[2].AsFloat = aRot.z; - this[3].AsFloat = aRot.w; + + for (int i = 0; i < 4; i++) + { + Add(aRot[i]); + } } return this; } @@ -312,10 +318,10 @@ public JSONNode WriteRect(Rect aRect) else if (IsArray) { Inline = true; - this[0].AsFloat = aRect.x; - this[1].AsFloat = aRect.y; - this[2].AsFloat = aRect.width; - this[3].AsFloat = aRect.height; + Add(aRect.x); + Add(aRect.y); + Add(aRect.width); + Add(aRect.height); } return this; } @@ -349,10 +355,10 @@ public JSONNode WriteRectOffset(RectOffset aRect) else if (IsArray) { Inline = true; - this[0].AsInt = aRect.left; - this[1].AsInt = aRect.right; - this[2].AsInt = aRect.top; - this[3].AsInt = aRect.bottom; + Add(aRect.left); + Add(aRect.right); + Add(aRect.top); + Add(aRect.bottom); } return this; } From 16fa8fbfa427e1fccd8d447cd4c01bc2f62279cb Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Tue, 8 Sep 2020 20:32:50 -0700 Subject: [PATCH 05/23] Unit tests for SimpleJSON. --- CHANGELOG.md.meta | 7 + LICENSE.md.meta | 7 + README.md.meta | 7 + Runtime.meta | 8 + Runtime/SimpleJSON.cs.meta | 11 + Runtime/SimpleJSONBinary.cs.meta | 11 + Runtime/SimpleJSONSerializer.cs.meta | 11 + Runtime/SimpleJSONUnity.cs.meta | 11 + Runtime/Unity.SimpleJSON.asmdef.meta | 7 + Tests.meta | 8 + Tests/Editor.meta | 8 + Tests/Editor/SimpleJSONTests.cs | 358 ++++++++++++++++++ Tests/Editor/SimpleJSONTests.cs.meta | 11 + Tests/Editor/SimpleJSONUnityTests.cs | 159 ++++++++ Tests/Editor/SimpleJSONUnityTests.cs.meta | 11 + .../Unity.SimpleJSON.Editor.Tests.asmdef | 18 + .../Unity.SimpleJSON.Editor.Tests.asmdef.meta | 7 + package.json.meta | 7 + 18 files changed, 667 insertions(+) create mode 100644 CHANGELOG.md.meta create mode 100644 LICENSE.md.meta create mode 100644 README.md.meta create mode 100644 Runtime.meta create mode 100644 Runtime/SimpleJSON.cs.meta create mode 100644 Runtime/SimpleJSONBinary.cs.meta create mode 100644 Runtime/SimpleJSONSerializer.cs.meta create mode 100644 Runtime/SimpleJSONUnity.cs.meta create mode 100644 Runtime/Unity.SimpleJSON.asmdef.meta create mode 100644 Tests.meta create mode 100644 Tests/Editor.meta create mode 100644 Tests/Editor/SimpleJSONTests.cs create mode 100644 Tests/Editor/SimpleJSONTests.cs.meta create mode 100644 Tests/Editor/SimpleJSONUnityTests.cs create mode 100644 Tests/Editor/SimpleJSONUnityTests.cs.meta create mode 100644 Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef create mode 100644 Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef.meta create mode 100644 package.json.meta diff --git a/CHANGELOG.md.meta b/CHANGELOG.md.meta new file mode 100644 index 0000000..f11b095 --- /dev/null +++ b/CHANGELOG.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: be525d451c07549bfbafdf8f7429c3eb +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/LICENSE.md.meta b/LICENSE.md.meta new file mode 100644 index 0000000..5b867cf --- /dev/null +++ b/LICENSE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c910a5d7653ff4db18ccec4212b62f37 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md.meta b/README.md.meta new file mode 100644 index 0000000..5de5da3 --- /dev/null +++ b/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b7a60a647bc4c437badac26e568e2c04 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime.meta b/Runtime.meta new file mode 100644 index 0000000..734b577 --- /dev/null +++ b/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bfe6d6d6d609b47a6aa646a26c2d6533 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/SimpleJSON.cs.meta b/Runtime/SimpleJSON.cs.meta new file mode 100644 index 0000000..98b36db --- /dev/null +++ b/Runtime/SimpleJSON.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ff140cd4df54e4808b0e92f9ae8de80c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/SimpleJSONBinary.cs.meta b/Runtime/SimpleJSONBinary.cs.meta new file mode 100644 index 0000000..918d86b --- /dev/null +++ b/Runtime/SimpleJSONBinary.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8769e8fc5b16e43b58fa508510c58591 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/SimpleJSONSerializer.cs.meta b/Runtime/SimpleJSONSerializer.cs.meta new file mode 100644 index 0000000..077260f --- /dev/null +++ b/Runtime/SimpleJSONSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 29935fded8b32477586ea480f4030884 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/SimpleJSONUnity.cs.meta b/Runtime/SimpleJSONUnity.cs.meta new file mode 100644 index 0000000..5e114d3 --- /dev/null +++ b/Runtime/SimpleJSONUnity.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 33d2108f9d3e147908196dd2de0dee25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Unity.SimpleJSON.asmdef.meta b/Runtime/Unity.SimpleJSON.asmdef.meta new file mode 100644 index 0000000..c5a6c19 --- /dev/null +++ b/Runtime/Unity.SimpleJSON.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d2c4748f30f3948f6aa51a68bf0506f7 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests.meta b/Tests.meta new file mode 100644 index 0000000..e06029a --- /dev/null +++ b/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8e98dc12b0bbb48808d4dd0dcd2294ab +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor.meta b/Tests/Editor.meta new file mode 100644 index 0000000..8b37029 --- /dev/null +++ b/Tests/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: af9647390a4cd4dc7965fe0f55970a89 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/SimpleJSONTests.cs b/Tests/Editor/SimpleJSONTests.cs new file mode 100644 index 0000000..325e82e --- /dev/null +++ b/Tests/Editor/SimpleJSONTests.cs @@ -0,0 +1,358 @@ +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using SimpleJSON; + +namespace Tests +{ + public class SimpleJSONTests + { + private const string jsonString = "{ \"array\": [1.44,2,3], " + + "\"object\": {\"key1\":\"value1\", \"key2\":256}, " + + "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " + + "\"unicode\": \"\\u3041 Men\u00fa sesi\u00f3n\", " + + "\"int\": 65536, " + + "\"double\": 3.1415926, " + + "\"bool\": true, " + + "\"null\": null }"; + + private const string arrayString = "[0,1,2,3,4,5]"; + private const string objectString = "{ \"zero\":0, \"one\":1, \"two\":2, \"three\":3, \"four\":4, \"five\":5 }"; + + private JSONNode parsedJSON; + + private double[] doubleArray; + private Dictionary objectDictionary; + private string stringValue; + private string unicodeValue; + private int intValue; + private double doubleValue; + private bool boolValue; + private object nullValue; + + [SetUp] + public void SetUp() + { + parsedJSON = JSON.Parse(jsonString); + + var jsonArray = parsedJSON["array"]; + + doubleArray = new double[jsonArray.Count]; + for (int i = 0; i < jsonArray.Count; i++) + { + doubleArray[i] = jsonArray[i].AsDouble; + } + + var jsonObject = parsedJSON["object"]; + + objectDictionary = new Dictionary(); + + foreach (var key in jsonObject.Keys) + { + var value = jsonObject[key]; + + objectDictionary[key] = value.IsString ? (object)value.Value : (object)value.AsDouble; + } + + stringValue = parsedJSON["string"].Value; + unicodeValue = parsedJSON["unicode"].Value; + intValue = parsedJSON["int"].AsInt; + doubleValue = parsedJSON["double"].AsDouble; + boolValue = parsedJSON["bool"].AsBool; + nullValue = parsedJSON["null"].AsObject; + } + + [Test] + public void ArrayTest() + { + var jsonArray = JSON.ToJSONNode(doubleArray); + + for (int i = 0; i < jsonArray.Count; i++) + { + Assert.AreEqual(jsonArray[i], parsedJSON["array"][i]); + + Assert.AreEqual(jsonArray[i].AsDouble, doubleArray[i]); + } + } + + [Test] + public void DictionaryTest() + { + var jsonObject = JSON.ToJSONNode(objectDictionary); + + foreach (var key in jsonObject.Keys) + { + Assert.AreEqual(jsonObject[key], parsedJSON["object"][key]); + } + } + + [Test] + public void StringTest() + { + var jsonString = JSON.ToJSONNode(stringValue); + + Assert.AreEqual(jsonString, parsedJSON["string"]); + + Assert.AreEqual(stringValue, parsedJSON["string"].Value); + + var emptyJsonString = new JSONString(string.Empty); + + Assert.False(emptyJsonString.IsNull); + Assert.AreEqual(emptyJsonString.Value, string.Empty); + + var nullJsonString = new JSONString(null); + + Assert.True(nullJsonString.IsNull); + + Assert.AreEqual(nullJsonString.Value, null); + + Assert.True(nullJsonString == JSONNull.CreateOrGet()); + Assert.False(nullJsonString == null); + + Assert.AreEqual(nullJsonString.ToString(), "null"); + } + + [Test] + public void UnicodeTest() + { + var jsonString = JSON.ToJSONNode(unicodeValue); + + Assert.AreEqual(jsonString, parsedJSON["unicode"]); + + Assert.AreEqual(unicodeValue, parsedJSON["unicode"].Value); + } + + [Test] + public void IntegerTest() + { + var jsonNumber = JSON.ToJSONNode(intValue); + + Assert.AreEqual(jsonNumber, parsedJSON["int"]); + + Assert.AreEqual(parsedJSON["int"].AsInt, intValue); + } + + [Test] + public void DoubleTest() + { + var jsonNumber = JSON.ToJSONNode(doubleValue); + + Assert.AreEqual(jsonNumber, parsedJSON["double"]); + + Assert.AreEqual(parsedJSON["double"].AsDouble, doubleValue); + } + + [Test] + public void NumberTest() + { + var numberA = new JSONNumber(7.5); + var numberB = JSON.Parse("7.5"); + + Assert.AreEqual(numberA, numberB); + + Assert.AreEqual(numberA.AsDouble, 7.5); + + Assert.AreEqual(numberB, 7.5); + + Assert.AreEqual(numberB.AsFloat, 7.5f); + } + + [Test] + public void BooleanTest() + { + var jsonBool = JSON.ToJSONNode(boolValue); + + Assert.AreEqual(jsonBool, parsedJSON["bool"]); + + Assert.AreEqual(parsedJSON["bool"].AsBool, boolValue); + } + + [Test] + public void NullTest() + { + var jsonNull = JSON.ToJSONNode(nullValue); + + Assert.AreEqual(jsonNull, parsedJSON["null"]); + + Assert.IsNull(parsedJSON["null"].AsObject); + + Assert.True(parsedJSON["null"].IsNull); + + Assert.AreEqual(parsedJSON["null"], JSONNull.CreateOrGet()); + + Assert.AreEqual(parsedJSON["null"].Value, null); + + Assert.True(parsedJSON["null"] == null); + } + + [Test] + public void ArrayGet() + { + var jsonArray = JSON.Parse(arrayString); + + Assert.AreEqual(jsonArray[2].AsInt, 2); + } + + [Test] + public void ArraySet() + { + var jsonArray = JSON.Parse(arrayString); + + jsonArray[5] = int.MaxValue; + + Assert.AreEqual(jsonArray[5].AsInt, int.MaxValue); + } + + [Test] + public void ArrayAdd() + { + var jsonArray = JSON.Parse(arrayString); + + jsonArray.Add(int.MinValue); + + Assert.AreEqual(jsonArray.Count, 7); + + Assert.AreEqual(jsonArray[6].AsInt, int.MinValue); + + Assert.Catch(delegate { jsonArray[8] = int.MaxValue; }); + + Assert.Catch(delegate { jsonArray.Add("fail", 8); }); + } + + [Test] + public void ArrayRemove() + { + var jsonArray = JSON.Parse(arrayString); + + var removed = jsonArray.Remove(1); + + Assert.AreEqual(removed.AsInt, 1); + + Assert.AreEqual(jsonArray.Count, 5); + + Assert.AreEqual(jsonArray[1].AsInt, 2); + + Assert.Catch(delegate { var fail = jsonArray.Remove("key"); }); + } + + [Test] + public void DictionaryGet() + { + var jsonObject = JSON.Parse(objectString); + + Assert.AreEqual(jsonObject["two"].AsInt, 2); + } + + [Test] + public void DictionarySet() + { + var jsonObject = JSON.Parse(objectString); + + jsonObject["three"] = int.MaxValue; + + Assert.AreEqual(jsonObject["three"].AsInt, int.MaxValue); + } + + [Test] + public void DictionaryAdd() + { + var jsonObject = JSON.Parse(objectString); + + jsonObject.Add("six", new JSONNumber(6)); + + Assert.AreEqual(jsonObject["six"].AsInt, 6); + + jsonObject["ninetynine"] = 99; + + Assert.AreEqual(jsonObject["ninetynine"].AsInt, 99); + + Assert.Catch(delegate { jsonObject.Add(new JSONString("fail")); }); + + jsonObject["tier1"] = new JSONObject(); + + Assert.Catch(delegate { jsonObject["tier1"].Add("failure"); }); + + jsonObject["tier1"].Add("success", true); + Assert.AreEqual(jsonObject["tier1"]["success"], true); + } + + [Test] + public void DictionaryRemove() + { + var jsonObject = JSON.Parse(objectString); + + Assert.Catch(delegate { var fail = jsonObject.Remove(3); }); + + var removed = jsonObject.Remove("zero"); + + Assert.AreEqual(removed.AsInt, 0); + + Assert.AreEqual(jsonObject.Count, 5); + + Assert.True(jsonObject["zero"].IsNull); + + Assert.AreEqual(jsonObject["zero"], JSONNull.CreateOrGet()); + + Assert.AreEqual(jsonObject["zero"].Value, null); + + Assert.True(jsonObject["zero"] == null); + } + + [Test] + public void LazyCreatorTest() + { + var jsonObject = new JSONObject(); + + jsonObject["one"] = 1; + + jsonObject["tier1"]["tier2"] = "second tier"; + + Assert.True(jsonObject["tier1"].IsObject); + Assert.AreEqual(jsonObject["tier1"]["tier2"], "second tier"); + + jsonObject["array"].Add(0); + jsonObject["array"].Add(1); + jsonObject["array"].Add(99); + + Assert.True(jsonObject["array"].IsArray); + Assert.AreEqual(jsonObject["array"][2], 99); + + Assert.Catch(delegate { jsonObject["one"]["two"] = "failure"; }); + + Assert.Catch(delegate { jsonObject["tier1"][0] = true; }); + + jsonObject["tier1"]["tier2"] = true; + + Assert.AreEqual(jsonObject["tier1"]["tier2"], true); + + jsonObject["array"][0] = "replaced"; + Assert.AreEqual(jsonObject["array"][0], "replaced"); + } + + [Test] + public void SerializeTest() + { + var objectWrapper = new Dictionary(); + objectWrapper["array"] = doubleArray; + objectWrapper["object"] = objectDictionary; + objectWrapper["string"] = stringValue; + objectWrapper["unicode"] = unicodeValue; + objectWrapper["int"] = intValue; + objectWrapper["double"] = doubleValue; + objectWrapper["bool"] = boolValue; + objectWrapper["null"] = nullValue; + + var jsonString = JSON.Serialize(objectWrapper.ToJSONNode(), 4); + + Assert.AreEqual(jsonString, parsedJSON.ToString(4)); + + jsonString = JSON.Serialize(objectWrapper, 0); + + Assert.AreEqual(jsonString, parsedJSON.ToString(0)); + + Debug.LogFormat("Serialized result:\n{0}", jsonString); + } + } +} diff --git a/Tests/Editor/SimpleJSONTests.cs.meta b/Tests/Editor/SimpleJSONTests.cs.meta new file mode 100644 index 0000000..a8cca75 --- /dev/null +++ b/Tests/Editor/SimpleJSONTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c9320cce26a01451ea0bf56116f12937 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/SimpleJSONUnityTests.cs b/Tests/Editor/SimpleJSONUnityTests.cs new file mode 100644 index 0000000..f4b9181 --- /dev/null +++ b/Tests/Editor/SimpleJSONUnityTests.cs @@ -0,0 +1,159 @@ +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using SimpleJSON; + +namespace Tests +{ + public class SimpleJSONUnityTests + { + [Test] + public void Vector2Test() + { + var vec2 = Random.insideUnitCircle; + + var jsonObject = new JSONObject().WriteVector2(vec2); + var jsonArray = new JSONArray().WriteVector2(vec2); + + var jsonObjectString = jsonObject.ToString(); + var jsonArrayString = jsonArray.ToString(); + + Debug.Log($"{vec2.GetType().Name} object: {jsonObjectString} array: {jsonArrayString}"); + + var deserializedObject = JSON.Parse(jsonObjectString).ReadVector2(); + var deserializedArray = JSON.Parse(jsonArrayString).ReadVector2(); + + for (int i=0; i < 2; i++) + { + Assert.AreEqual(vec2[i], deserializedObject[i]); + Assert.AreEqual(vec2[i], deserializedArray[i]); + } + } + + [Test] + public void Vector3Test() + { + var vec3 = Random.insideUnitSphere; + + var jsonObject = new JSONObject().WriteVector3(vec3); + var jsonArray = new JSONArray().WriteVector3(vec3); + + var jsonObjectString = jsonObject.ToString(); + var jsonArrayString = jsonArray.ToString(); + + Debug.Log($"{vec3.GetType().Name} object: {jsonObjectString} array: {jsonArrayString}"); + + var deserializedObject = JSON.Parse(jsonObjectString).ReadVector3(); + var deserializedArray = JSON.Parse(jsonArrayString).ReadVector3(); + + for (int i = 0; i < 3; i++) + { + Assert.AreEqual(vec3[i], deserializedObject[i]); + Assert.AreEqual(vec3[i], deserializedArray[i]); + } + } + + [Test] + public void Vector4Test() + { + Vector4 vec4 = Random.insideUnitSphere; + vec4.w = Random.value; + + var jsonObject = new JSONObject().WriteVector4(vec4); + var jsonArray = new JSONArray().WriteVector4(vec4); + + var jsonObjectString = jsonObject.ToString(); + var jsonArrayString = jsonArray.ToString(); + + Debug.Log($"{vec4.GetType().Name} object: {jsonObjectString} array: {jsonArrayString}"); + + var deserializedObject = JSON.Parse(jsonObjectString).ReadVector4(); + var deserializedArray = JSON.Parse(jsonArrayString).ReadVector4(); + + for (int i = 0; i < 4; i++) + { + Assert.AreEqual(vec4[i], deserializedObject[i]); + Assert.AreEqual(vec4[i], deserializedArray[i]); + } + } + + [Test] + public void QuaternionTest() + { + Quaternion quat = Random.rotation; + + var jsonObject = new JSONObject().WriteQuaternion(quat); + var jsonArray = new JSONArray().WriteQuaternion(quat); + + var jsonObjectString = jsonObject.ToString(); + var jsonArrayString = jsonArray.ToString(); + + Debug.Log($"{quat.GetType().Name} object: {jsonObjectString} array: {jsonArrayString}"); + + var deserializedObject = JSON.Parse(jsonObjectString).ReadQuaternion(); + var deserializedArray = JSON.Parse(jsonArrayString).ReadQuaternion(); + + for (int i = 0; i < 4; i++) + { + Assert.AreEqual(quat[i], deserializedObject[i]); + Assert.AreEqual(quat[i], deserializedArray[i]); + } + } + + [Test] + public void RectTest() + { + Rect rect = new Rect(Random.insideUnitCircle * 10.0f, Random.insideUnitCircle * 10.0f); + + var jsonObject = new JSONObject().WriteRect(rect); + var jsonArray = new JSONArray().WriteRect(rect); + + var jsonObjectString = jsonObject.ToString(); + var jsonArrayString = jsonArray.ToString(); + + Debug.Log($"{rect.GetType().Name} object: {jsonObjectString} array: {jsonArrayString}"); + + var deserializedObject = JSON.Parse(jsonObjectString).ReadRect(); + var deserializedArray = JSON.Parse(jsonArrayString).ReadRect(); + + Assert.AreEqual(rect.x, deserializedObject.x); + Assert.AreEqual(rect.y, deserializedObject.y); + Assert.AreEqual(rect.width, deserializedObject.width); + Assert.AreEqual(rect.height, deserializedObject.height); + + Assert.AreEqual(rect.x, deserializedArray.x); + Assert.AreEqual(rect.y, deserializedArray.y); + Assert.AreEqual(rect.width, deserializedArray.width); + Assert.AreEqual(rect.height, deserializedArray.height); + } + + [Test] + public void RectOffsetTest() + { + RectOffset rectOffset = new RectOffset(Random.Range(-100, 100), Random.Range(-100, 100), Random.Range(-100, 100), Random.Range(-100, 100)); + + var jsonObject = new JSONObject().WriteRectOffset(rectOffset); + var jsonArray = new JSONArray().WriteRectOffset(rectOffset); + + var jsonObjectString = jsonObject.ToString(); + var jsonArrayString = jsonArray.ToString(); + + Debug.Log($"{rectOffset.GetType().Name} object: {jsonObjectString} array: {jsonArrayString}"); + + var deserializedObject = JSON.Parse(jsonObjectString).ReadRectOffset(); + var deserializedArray = JSON.Parse(jsonArrayString).ReadRectOffset(); + + Assert.AreEqual(rectOffset.left, deserializedObject.left); + Assert.AreEqual(rectOffset.right, deserializedObject.right); + Assert.AreEqual(rectOffset.top, deserializedObject.top); + Assert.AreEqual(rectOffset.bottom, deserializedObject.bottom); + + Assert.AreEqual(rectOffset.left, deserializedArray.left); + Assert.AreEqual(rectOffset.right, deserializedArray.right); + Assert.AreEqual(rectOffset.top, deserializedArray.top); + Assert.AreEqual(rectOffset.bottom, deserializedArray.bottom); + } + } +} diff --git a/Tests/Editor/SimpleJSONUnityTests.cs.meta b/Tests/Editor/SimpleJSONUnityTests.cs.meta new file mode 100644 index 0000000..f207032 --- /dev/null +++ b/Tests/Editor/SimpleJSONUnityTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be38ed1dbe9e14c1ab4895ca72b80d19 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef b/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef new file mode 100644 index 0000000..e1c7cd0 --- /dev/null +++ b/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef @@ -0,0 +1,18 @@ +{ + "name": "SimpleJSON Tests", + "references": [ + "SimpleJSON" + ], + "optionalUnityReferences": [ + "TestAssemblies" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +} \ No newline at end of file diff --git a/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef.meta b/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef.meta new file mode 100644 index 0000000..d6fe590 --- /dev/null +++ b/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9fca06cca9291467b9046a01b83b9cca +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/package.json.meta b/package.json.meta new file mode 100644 index 0000000..3da0f50 --- /dev/null +++ b/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2815c176ba52d43caadfab3256da784e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From b8c8d4b8aa0e5ccae74045acfebcce2eee9d41c9 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Tue, 8 Sep 2020 22:34:22 -0700 Subject: [PATCH 06/23] Read and write Unity colors. Unit tests for color methods. --- Runtime/SimpleJSON.cs | 22 +++++ Runtime/SimpleJSONSerializer.cs | 42 +++++++-- Runtime/SimpleJSONUnity.cs | 135 +++++++++++++++++++++++++++ Tests/Editor/SimpleJSONUnityTests.cs | 113 +++++++++++++--------- 4 files changed, 262 insertions(+), 50 deletions(-) diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index d91d77e..b9bcb2d 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -294,6 +294,12 @@ public virtual double AsDouble } } + public virtual byte AsByte + { + get { return (byte) AsDouble; } + set { AsDouble = value; } + } + public virtual int AsInt { get { return (int)AsDouble; } @@ -391,6 +397,11 @@ public static implicit operator JSONNode(int n) return new JSONNumber(n); } + public static implicit operator byte(JSONNode d) + { + return (d == null) ? (byte) 0 : d.AsByte; + } + public static implicit operator int(JSONNode d) { return (d == null) ? 0 : d.AsInt; @@ -1098,6 +1109,11 @@ protected set } } + public JSONString() : this(null) + { + + } + public JSONString(string aData) { m_Data = aData; @@ -1421,6 +1437,12 @@ public override int GetHashCode() return 0; } + public override byte AsByte + { + get { Set(new JSONNumber(0)); return (byte) 0; } + set { Set(new JSONNumber(value)); } + } + public override int AsInt { get { Set(new JSONNumber(0)); return 0; } diff --git a/Runtime/SimpleJSONSerializer.cs b/Runtime/SimpleJSONSerializer.cs index 0804ca6..b059931 100644 --- a/Runtime/SimpleJSONSerializer.cs +++ b/Runtime/SimpleJSONSerializer.cs @@ -38,19 +38,23 @@ public static JSONNode ToJSONNode(object value) return serializableValue.ToJSONNode(); #if UNITY_5_3_OR_NEWER case Vector2 v2Value: - return v2Value.ToJSONNode(); + return v2Value; case Vector3 v3Value: - return v3Value.ToJSONNode(); + return v3Value; case Vector4 v4Value: - return v4Value.ToJSONNode(); + return v4Value; case Quaternion quatValue: - return quatValue.ToJSONNode(); + return quatValue; case Rect rectValue: - return rectValue.ToJSONNode(); + return rectValue; case RectOffset rectOffsetValue: - return rectOffsetValue.ToJSONNode(); + return rectOffsetValue; case Matrix4x4 matrixValue: - return matrixValue.ToJSONNode(); + return matrixValue; + case Color colorValue: + return colorValue; + case Color32 color32Value: + return color32Value; #endif default: @@ -165,6 +169,30 @@ public static JSONNode ToJSONNode(this Matrix4x4 matrix) { return new JSONArray().WriteMatrix(matrix); } + + public static JSONNode ToJSONNode(this Color color, bool asArray = false) + { + if (asArray) + { + return new JSONArray().WriteColor(color); + } + else + { + return new JSONObject().WriteColor(color); + } + } + + public static JSONNode ToJSONNode(this Color32 color32, bool asArray = false) + { + if (asArray) + { + return new JSONArray().WriteColor32(color32); + } + else + { + return new JSONObject().WriteColor32(color32); + } + } #endif public static JSONNode ToJSONNode(this List list) diff --git a/Runtime/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs index 2901cd2..07daa24 100644 --- a/Runtime/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -49,6 +49,7 @@ public partial class JSONNode public static JSONContainerType VectorContainerType = JSONContainerType.Array; public static JSONContainerType QuaternionContainerType = JSONContainerType.Array; public static JSONContainerType RectContainerType = JSONContainerType.Object; + public static JSONContainerType ColorContainerType = JSONContainerType.Object; private static JSONNode GetContainer(JSONContainerType aType) { if (aType == JSONContainerType.Array) @@ -63,30 +64,35 @@ public static implicit operator JSONNode(Vector2 aVec) n.WriteVector2(aVec); return n; } + public static implicit operator JSONNode(Vector3 aVec) { JSONNode n = GetContainer(VectorContainerType); n.WriteVector3(aVec); return n; } + public static implicit operator JSONNode(Vector4 aVec) { JSONNode n = GetContainer(VectorContainerType); n.WriteVector4(aVec); return n; } + public static implicit operator JSONNode(Quaternion aRot) { JSONNode n = GetContainer(QuaternionContainerType); n.WriteQuaternion(aRot); return n; } + public static implicit operator JSONNode(Rect aRect) { JSONNode n = GetContainer(RectContainerType); n.WriteRect(aRect); return n; } + public static implicit operator JSONNode(RectOffset aRect) { JSONNode n = GetContainer(RectContainerType); @@ -94,6 +100,27 @@ public static implicit operator JSONNode(RectOffset aRect) return n; } + public static implicit operator JSONNode(Matrix4x4 aMatrix) + { + JSONNode n = new JSONArray(); + n.WriteMatrix(aMatrix); + return n; + } + + public static implicit operator JSONNode(Color aColor) + { + JSONNode n = GetContainer(ColorContainerType); + n.WriteColor(aColor); + return n; + } + + public static implicit operator JSONNode(Color32 aColor32) + { + JSONNode n = GetContainer(ColorContainerType); + n.WriteColor32(aColor32); + return n; + } + public static implicit operator Vector2(JSONNode aNode) { return aNode.ReadVector2(); @@ -124,6 +151,21 @@ public static implicit operator RectOffset(JSONNode aNode) return aNode.ReadRectOffset(); } + public static implicit operator Matrix4x4(JSONNode aNode) + { + return aNode.ReadMatrix(); + } + + public static implicit operator Color(JSONNode aNode) + { + return aNode.ReadColor(); + } + + public static implicit operator Color32(JSONNode aNode) + { + return aNode.ReadColor32(); + } + #endregion implicit conversion operators #region Vector2 @@ -391,5 +433,98 @@ public JSONNode WriteMatrix(Matrix4x4 aMatrix) return this; } #endregion Matrix4x4 + + #region Color + public Color ReadColor() + { + if (IsString && ColorUtility.TryParseHtmlString(Value, out Color htmlColor)) + { + return htmlColor; + } + + if (IsArray) + { + return ReadVector4(); + } + + if (IsObject) + { + return new Color(this["r"].AsFloat, this["g"].AsFloat, this["b"].AsFloat, this["a"].AsFloat); + } + + return Color.white; + } + + public JSONNode WriteColor(Color aColor) + { + if (IsString) + { + Value = $"#{ColorUtility.ToHtmlStringRGBA(aColor)}"; + } + else if (IsObject) + { + Inline = true; + this["r"].AsFloat = aColor.r; + this["g"].AsFloat = aColor.g; + this["b"].AsFloat = aColor.b; + this["a"].AsFloat = aColor.a; + + } + else if (IsArray) + { + WriteVector4(aColor); + } + + return this; + } + #endregion Color + + #region Color32 + public Color32 ReadColor32() + { + if (IsString && ColorUtility.TryParseHtmlString(Value, out Color htmlColor)) + { + return htmlColor; + } + + if (IsArray) + { + return new Color32(this[0].AsByte, this[1].AsByte, this[2].AsByte, this[3].AsByte); + } + + if (IsObject) + { + return new Color32(this["r"].AsByte, this["g"].AsByte, this["b"].AsByte, this["a"].AsByte); + } + + return Color.white; + } + + public JSONNode WriteColor32(Color32 aColor32) + { + if (IsString) + { + Value = $"#{ColorUtility.ToHtmlStringRGBA(aColor32)}"; + } + else if (IsObject) + { + Inline = true; + this["r"].AsByte = aColor32.r; + this["g"].AsByte = aColor32.g; + this["b"].AsByte = aColor32.b; + this["a"].AsByte = aColor32.a; + + } + else if (IsArray) + { + Add(aColor32.r); + Add(aColor32.g); + Add(aColor32.b); + Add(aColor32.a); + } + + return this; + } + #endregion Color32 } } diff --git a/Tests/Editor/SimpleJSONUnityTests.cs b/Tests/Editor/SimpleJSONUnityTests.cs index f4b9181..f3ef26f 100644 --- a/Tests/Editor/SimpleJSONUnityTests.cs +++ b/Tests/Editor/SimpleJSONUnityTests.cs @@ -25,11 +25,8 @@ public void Vector2Test() var deserializedObject = JSON.Parse(jsonObjectString).ReadVector2(); var deserializedArray = JSON.Parse(jsonArrayString).ReadVector2(); - for (int i=0; i < 2; i++) - { - Assert.AreEqual(vec2[i], deserializedObject[i]); - Assert.AreEqual(vec2[i], deserializedArray[i]); - } + Assert.AreEqual(vec2, deserializedObject); + Assert.AreEqual(vec2, deserializedArray); } [Test] @@ -48,11 +45,8 @@ public void Vector3Test() var deserializedObject = JSON.Parse(jsonObjectString).ReadVector3(); var deserializedArray = JSON.Parse(jsonArrayString).ReadVector3(); - for (int i = 0; i < 3; i++) - { - Assert.AreEqual(vec3[i], deserializedObject[i]); - Assert.AreEqual(vec3[i], deserializedArray[i]); - } + Assert.AreEqual(vec3, deserializedObject); + Assert.AreEqual(vec3, deserializedArray); } [Test] @@ -72,11 +66,8 @@ public void Vector4Test() var deserializedObject = JSON.Parse(jsonObjectString).ReadVector4(); var deserializedArray = JSON.Parse(jsonArrayString).ReadVector4(); - for (int i = 0; i < 4; i++) - { - Assert.AreEqual(vec4[i], deserializedObject[i]); - Assert.AreEqual(vec4[i], deserializedArray[i]); - } + Assert.AreEqual(vec4, deserializedObject); + Assert.AreEqual(vec4, deserializedArray); } [Test] @@ -95,11 +86,8 @@ public void QuaternionTest() var deserializedObject = JSON.Parse(jsonObjectString).ReadQuaternion(); var deserializedArray = JSON.Parse(jsonArrayString).ReadQuaternion(); - for (int i = 0; i < 4; i++) - { - Assert.AreEqual(quat[i], deserializedObject[i]); - Assert.AreEqual(quat[i], deserializedArray[i]); - } + Assert.AreEqual(quat, deserializedObject); + Assert.AreEqual(quat, deserializedArray); } [Test] @@ -118,15 +106,8 @@ public void RectTest() var deserializedObject = JSON.Parse(jsonObjectString).ReadRect(); var deserializedArray = JSON.Parse(jsonArrayString).ReadRect(); - Assert.AreEqual(rect.x, deserializedObject.x); - Assert.AreEqual(rect.y, deserializedObject.y); - Assert.AreEqual(rect.width, deserializedObject.width); - Assert.AreEqual(rect.height, deserializedObject.height); - - Assert.AreEqual(rect.x, deserializedArray.x); - Assert.AreEqual(rect.y, deserializedArray.y); - Assert.AreEqual(rect.width, deserializedArray.width); - Assert.AreEqual(rect.height, deserializedArray.height); + Assert.AreEqual(rect, deserializedObject); + Assert.AreEqual(rect, deserializedArray); } [Test] @@ -134,26 +115,72 @@ public void RectOffsetTest() { RectOffset rectOffset = new RectOffset(Random.Range(-100, 100), Random.Range(-100, 100), Random.Range(-100, 100), Random.Range(-100, 100)); - var jsonObject = new JSONObject().WriteRectOffset(rectOffset); - var jsonArray = new JSONArray().WriteRectOffset(rectOffset); + var jsonObject = new JSONObject().WriteRectOffset(rectOffset); + var jsonArray = new JSONArray().WriteRectOffset(rectOffset); - var jsonObjectString = jsonObject.ToString(); - var jsonArrayString = jsonArray.ToString(); + var jsonObjectString = jsonObject.ToString(); + var jsonArrayString = jsonArray.ToString(); - Debug.Log($"{rectOffset.GetType().Name} object: {jsonObjectString} array: {jsonArrayString}"); + Debug.Log($"{rectOffset.GetType().Name} object: {jsonObjectString} array: {jsonArrayString}"); var deserializedObject = JSON.Parse(jsonObjectString).ReadRectOffset(); var deserializedArray = JSON.Parse(jsonArrayString).ReadRectOffset(); - Assert.AreEqual(rectOffset.left, deserializedObject.left); - Assert.AreEqual(rectOffset.right, deserializedObject.right); - Assert.AreEqual(rectOffset.top, deserializedObject.top); - Assert.AreEqual(rectOffset.bottom, deserializedObject.bottom); + Assert.AreEqual(rectOffset.left, deserializedObject.left); + Assert.AreEqual(rectOffset.right, deserializedObject.right); + Assert.AreEqual(rectOffset.top, deserializedObject.top); + Assert.AreEqual(rectOffset.bottom, deserializedObject.bottom); + + Assert.AreEqual(rectOffset.left, deserializedArray.left); + Assert.AreEqual(rectOffset.right, deserializedArray.right); + Assert.AreEqual(rectOffset.top, deserializedArray.top); + Assert.AreEqual(rectOffset.bottom, deserializedArray.bottom); + } + + [Test] + public void ColorTest() + { + Color color = new Color(Random.value, Random.value, Random.value, Random.value); + + var jsonObject = new JSONObject().WriteColor(color); + var jsonArray = new JSONArray().WriteColor(color); + var jsonString = new JSONString().WriteColor(color); + + var jsonObjectString = jsonObject.ToString(); + var jsonArrayString = jsonArray.ToString(); + var jsonStringString = jsonString.ToString(); + + Debug.Log($"{color.GetType().Name} object: {jsonObjectString} array: {jsonArrayString} string: {jsonStringString}"); - Assert.AreEqual(rectOffset.left, deserializedArray.left); - Assert.AreEqual(rectOffset.right, deserializedArray.right); - Assert.AreEqual(rectOffset.top, deserializedArray.top); - Assert.AreEqual(rectOffset.bottom, deserializedArray.bottom); + var deserializedObject = JSON.Parse(jsonObjectString).ReadColor(); + var deserializedArray = JSON.Parse(jsonArrayString).ReadColor(); + + Assert.AreEqual(color, deserializedObject); + Assert.AreEqual(color, deserializedArray); } + + [Test] + public void Color32Test() + { + Color32 color32 = new Color(Random.value, Random.value, Random.value, Random.value); + + var jsonObject = new JSONObject().WriteColor32(color32); + var jsonArray = new JSONArray().WriteColor32(color32); + var jsonString = new JSONString().WriteColor32(color32); + + var jsonObjectString = jsonObject.ToString(); + var jsonArrayString = jsonArray.ToString(); + var jsonStringString = jsonString.ToString(); + + Debug.Log($"{color32.GetType().Name} object: {jsonObjectString} array: {jsonArrayString} string: {jsonStringString}"); + + var deserializedObject = JSON.Parse(jsonObjectString).ReadColor32(); + var deserializedArray = JSON.Parse(jsonArrayString).ReadColor32(); + var deserializedString = JSON.Parse(jsonStringString).ReadColor32(); + + Assert.AreEqual(color32, deserializedObject); + Assert.AreEqual(color32, deserializedArray); + Assert.AreEqual(color32, deserializedString); + } } -} +} \ No newline at end of file From af6a9aa6d9ae4fe9aaccf91977f56ee6758efc2a Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Tue, 8 Sep 2020 22:44:49 -0700 Subject: [PATCH 07/23] Unit tests for Matrix JSON serialization/deserialization. --- Runtime/SimpleJSONUnity.cs | 2 +- Tests/Editor/SimpleJSONUnityTests.cs | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Runtime/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs index 07daa24..17589cb 100644 --- a/Runtime/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -427,7 +427,7 @@ public JSONNode WriteMatrix(Matrix4x4 aMatrix) Inline = true; for (int i = 0; i < 16; i++) { - this[i].AsFloat = aMatrix[i]; + Add(aMatrix[i]); } } return this; diff --git a/Tests/Editor/SimpleJSONUnityTests.cs b/Tests/Editor/SimpleJSONUnityTests.cs index f3ef26f..54d7a04 100644 --- a/Tests/Editor/SimpleJSONUnityTests.cs +++ b/Tests/Editor/SimpleJSONUnityTests.cs @@ -137,6 +137,26 @@ public void RectOffsetTest() Assert.AreEqual(rectOffset.bottom, deserializedArray.bottom); } + [Test] + public void MatrixTest() + { + Matrix4x4 matrix = new Matrix4x4(); + for (int i=0; i < 16; i++) + { + matrix[i] = Random.Range(-1.0f, 1.0f); + } + + var jsonArray = new JSONArray().WriteMatrix(matrix); + + var jsonArrayString = jsonArray.ToString(); + + Debug.Log($"{matrix.GetType().Name} array: {jsonArrayString}"); + + var deserializedArray = JSON.Parse(jsonArrayString).ReadMatrix(); + + Assert.AreEqual(matrix, deserializedArray); + } + [Test] public void ColorTest() { From 9811c827e617500211ea3f85225f04564f348675 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Wed, 9 Sep 2020 19:21:22 -0700 Subject: [PATCH 08/23] add readme. fix white space. --- README.md | 37 + Runtime/SimpleJSON.cs | 2968 +++++++++++++------------- Runtime/SimpleJSONUnity.cs | 28 +- Tests/Editor/SimpleJSONUnityTests.cs | 42 +- 4 files changed, 1556 insertions(+), 1519 deletions(-) diff --git a/README.md b/README.md index e69de29..f9b699f 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,37 @@ +A simple JSON Parser / builder +------------------------------ + +SimpleJSON mainly has been written as a simple JSON parser. It can build a JSON string +from the node-tree, or generate a node tree from any valid JSON string. + +Written by Bunny83 +2012-06-09 + +Add the following to your package.json "dependencies" section to import SimpleJSON: + "com.github.bunny83.simplejson": "https://github.com/darktable/SimpleJSON.git#unity-package", + +SimpleJSONBinary is an extension of the SimpleJSON framework to provide methods to +serialize a JSON object tree into a compact binary format. Optionally the +binary stream can be compressed with the SharpZipLib when using the define +"USE_SharpZipLib" + +Those methods where originally part of the framework but since it's rarely +used I've extracted this part into this seperate module file. + +You can use the define "SimpleJSON_ExcludeBinary" to selectively disable +this extension without the need to remove the file from the project. + +If you want to use compression when saving to file / stream / B64 you have to include +SharpZipLib ( http://www.icsharpcode.net/opensource/sharpziplib/ ) in your project and +define "USE_SharpZipLib" at the top of the file + +SimpleJSONUnity is a Unity extension for the SimpleJSON framework. It does +only work together with the SimpleJSON.cs +It provides several helpers and conversion operators to serialize/deserialize +common Unity types such as Vector2/3/4, Rect, RectOffset, Quaternion and +Matrix4x4 as JSONObject or JSONArray. +This extension will add 3 static settings to the JSONNode class: +( VectorContainerType, QuaternionContainerType, RectContainerType ) which +control what node type should be used for serializing the given type. So a +Vector3 as array would look like [12,32,24] and {"x":12, "y":32, "z":24} as +object. diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index b9bcb2d..4a1d2d3 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -41,1490 +41,1490 @@ namespace SimpleJSON { - public enum JSONNodeType - { - Array = 1, - Object = 2, - String = 3, - Number = 4, - NullValue = 5, - Boolean = 6, - None = 7, - Custom = 0xFF, - } - public enum JSONTextMode - { - Compact, - Indent - } - - public abstract partial class JSONNode - { - protected const string TOKEN_NULL = "null"; - protected const string TOKEN_TRUE = "true"; - protected const string TOKEN_FALSE = "false"; - - #region Enumerators - public struct Enumerator - { - private enum Type { None, Array, Object } - private Type type; - private Dictionary.Enumerator m_Object; - private List.Enumerator m_Array; - public bool IsValid { get { return type != Type.None; } } - - public Enumerator(List.Enumerator aArrayEnum) - { - type = Type.Array; - m_Object = default(Dictionary.Enumerator); - m_Array = aArrayEnum; - } - - public Enumerator(Dictionary.Enumerator aDictEnum) - { - type = Type.Object; - m_Object = aDictEnum; - m_Array = default(List.Enumerator); - } - - public KeyValuePair Current - { - get - { - if (type == Type.Array) - return new KeyValuePair(string.Empty, m_Array.Current); - else if (type == Type.Object) - return m_Object.Current; - return new KeyValuePair(string.Empty, null); - } - } - - public bool MoveNext() - { - if (type == Type.Array) - return m_Array.MoveNext(); - else if (type == Type.Object) - return m_Object.MoveNext(); - return false; - } - } - - public struct ValueEnumerator - { - private Enumerator m_Enumerator; - public ValueEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } - public ValueEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } - public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } - public JSONNode Current { get { return m_Enumerator.Current.Value; } } - public bool MoveNext() { return m_Enumerator.MoveNext(); } - public ValueEnumerator GetEnumerator() { return this; } - } - - public struct KeyEnumerator - { - private Enumerator m_Enumerator; - public KeyEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } - public KeyEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } - public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } - public string Current { get { return m_Enumerator.Current.Key; } } - public bool MoveNext() { return m_Enumerator.MoveNext(); } - public KeyEnumerator GetEnumerator() { return this; } - } - - public class LinqEnumerator : IEnumerator>, IEnumerable> - { - private JSONNode m_Node; - private Enumerator m_Enumerator; - - internal LinqEnumerator(JSONNode aNode) - { - m_Node = aNode; - if (m_Node != null) - m_Enumerator = m_Node.GetEnumerator(); - } - - public KeyValuePair Current { get { return m_Enumerator.Current; } } - object IEnumerator.Current { get { return m_Enumerator.Current; } } - public bool MoveNext() { return m_Enumerator.MoveNext(); } - - public void Dispose() - { - m_Node = null; - m_Enumerator = new Enumerator(); - } - - public IEnumerator> GetEnumerator() - { - return new LinqEnumerator(m_Node); - } - - public void Reset() - { - if (m_Node != null) - m_Enumerator = m_Node.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return new LinqEnumerator(m_Node); - } - } - - #endregion Enumerators - - #region common interface - - public static bool forceASCII = false; // Use Unicode by default - public static bool longAsString = false; // lazy creator creates a JSONString instead of JSONNumber - public static bool allowLineComments = true; // allow "//"-style comments at the end of a line - - public abstract JSONNodeType Tag { get; } - - public virtual JSONNode this[int aIndex] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } - public virtual JSONNode this[string aKey] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } - - public virtual string Value { get { return null; } protected set { } } - - public virtual int Count { get { return 0; } } - - public virtual bool IsNumber { get { return false; } } - public virtual bool IsString { get { return false; } } - public virtual bool IsBoolean { get { return false; } } - public virtual bool IsNull { get { return false; } } - public virtual bool IsArray { get { return false; } } - public virtual bool IsObject { get { return false; } } - - public virtual bool Inline { get { return false; } set { } } - - public virtual void Add(string aKey, JSONNode aItem) - { - throw new NotImplementedException(); - } - - public virtual void Add(JSONNode aItem) - { - Add(null, aItem); - } - - public virtual JSONNode Remove(string aKey) - { - throw new NotImplementedException(); - } - - public virtual JSONNode Remove(int aIndex) - { - throw new NotImplementedException(); - } - - public virtual JSONNode Remove(JSONNode aNode) - { - throw new NotImplementedException(); - } - - public virtual JSONNode Clone() - { - throw new NotImplementedException(); - } - - public virtual IEnumerable Children - { - get - { - yield break; - } - } - - public IEnumerable DeepChildren - { - get - { - foreach (var C in Children) - foreach (var D in C.DeepChildren) - yield return D; - } - } - - public virtual bool HasKey(string aKey) - { - return false; - } - - public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) - { - return aDefault; - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - WriteToStringBuilder(sb, 0, 0, JSONTextMode.Compact); - return sb.ToString(); - } - - public virtual string ToString(int aIndent) - { - StringBuilder sb = new StringBuilder(); - WriteToStringBuilder(sb, 0, aIndent, aIndent > 0 ? JSONTextMode.Indent : JSONTextMode.Compact); - return sb.ToString(); - } - - internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); - - public abstract Enumerator GetEnumerator(); - public IEnumerable> Linq { get { return new LinqEnumerator(this); } } - public KeyEnumerator Keys { get { return new KeyEnumerator(GetEnumerator()); } } - public ValueEnumerator Values { get { return new ValueEnumerator(GetEnumerator()); } } - - #endregion common interface - - #region typecasting properties - - public virtual double AsDouble - { - get - { - double v = 0.0; - if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) - return v; - return 0.0; - } - set - { - Value = value.ToString(CultureInfo.InvariantCulture); - } - } - - public virtual byte AsByte - { - get { return (byte) AsDouble; } - set { AsDouble = value; } - } - - public virtual int AsInt - { - get { return (int)AsDouble; } - set { AsDouble = value; } - } - - public virtual float AsFloat - { - get { return (float)AsDouble; } - set { AsDouble = value; } - } - - public virtual bool AsBool - { - get - { - bool v = false; - if (bool.TryParse(Value, out v)) - return v; - return !string.IsNullOrEmpty(Value); - } - set - { - Value = (value) ? TOKEN_TRUE : TOKEN_FALSE; - } - } - - public virtual long AsLong - { - get - { - long val = 0; - if (long.TryParse(Value, out val)) - return val; - return 0L; - } - set - { - Value = value.ToString(); - } - } - - public virtual JSONArray AsArray - { - get - { - return this as JSONArray; - } - } - - public virtual JSONObject AsObject - { - get - { - return this as JSONObject; - } - } - - #endregion typecasting properties - - #region operators - - public static implicit operator JSONNode(string s) - { - return new JSONString(s); - } - - public static implicit operator string(JSONNode d) - { - return (d == null) ? null : d.Value; - } - - public static implicit operator JSONNode(double n) - { - return new JSONNumber(n); - } - - public static implicit operator double(JSONNode d) - { - return (d == null) ? 0 : d.AsDouble; - } - - public static implicit operator JSONNode(float n) - { - return new JSONNumber(n); - } - - public static implicit operator float(JSONNode d) - { - return (d == null) ? 0 : d.AsFloat; - } - - public static implicit operator JSONNode(int n) - { - return new JSONNumber(n); - } - - public static implicit operator byte(JSONNode d) - { - return (d == null) ? (byte) 0 : d.AsByte; - } - - public static implicit operator int(JSONNode d) - { - return (d == null) ? 0 : d.AsInt; - } - - public static implicit operator JSONNode(long n) - { - if (longAsString) - return new JSONString(n.ToString()); - return new JSONNumber(n); - } - - public static implicit operator long(JSONNode d) - { - return (d == null) ? 0L : d.AsLong; - } - - public static implicit operator JSONNode(bool b) - { - return new JSONBool(b); - } - - public static implicit operator bool(JSONNode d) - { - return (d == null) ? false : d.AsBool; - } - - public static implicit operator JSONNode(KeyValuePair aKeyValue) - { - return aKeyValue.Value; - } - - public static bool operator ==(JSONNode a, object b) - { - if (ReferenceEquals(a, b)) - return true; - bool aIsNull = a is JSONNull || ReferenceEquals(a, null) || a is JSONLazyCreator; - bool bIsNull = b is JSONNull || ReferenceEquals(b, null) || b is JSONLazyCreator; - if (aIsNull && bIsNull) - return true; - return !aIsNull && a.Equals(b); - } - - public static bool operator !=(JSONNode a, object b) - { - return !(a == b); - } - - public override bool Equals(object obj) - { - return ReferenceEquals(this, obj); - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - - #endregion operators - - [ThreadStatic] - private static StringBuilder m_EscapeBuilder; - - internal static StringBuilder EscapeBuilder - { - get - { - if (m_EscapeBuilder == null) - m_EscapeBuilder = new StringBuilder(); - return m_EscapeBuilder; - } - } - - internal static string Escape(string aText) - { - var sb = EscapeBuilder; - sb.Length = 0; - if (sb.Capacity < aText.Length + aText.Length / 10) - sb.Capacity = aText.Length + aText.Length / 10; - foreach (char c in aText) - { - switch (c) - { - case '\\': - sb.Append("\\\\"); - break; - case '\"': - sb.Append("\\\""); - break; - case '\n': - sb.Append("\\n"); - break; - case '\r': - sb.Append("\\r"); - break; - case '\t': - sb.Append("\\t"); - break; - case '\b': - sb.Append("\\b"); - break; - case '\f': - sb.Append("\\f"); - break; - default: - if (c < ' ' || (forceASCII && c > 127)) - { - ushort val = c; - sb.Append("\\u").Append(val.ToString("X4")); - } - else - sb.Append(c); - break; - } - } - string result = sb.ToString(); - sb.Length = 0; - return result; - } - - private static JSONNode ParseElement(string token, bool quoted) - { - if (quoted) - return token; - if (token.Equals(TOKEN_FALSE, StringComparison.InvariantCultureIgnoreCase) - || token.Equals(TOKEN_TRUE, StringComparison.InvariantCultureIgnoreCase)) - return token.Equals(TOKEN_TRUE, StringComparison.InvariantCultureIgnoreCase); - if (token.Equals(TOKEN_NULL, StringComparison.InvariantCultureIgnoreCase)) - return JSONNull.CreateOrGet(); - - if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out double val)) - return val; - else - return token; - } - - public static JSONNode Parse(string aJSON) - { - Stack stack = new Stack(); - JSONNode ctx = null; - int i = 0; - StringBuilder Token = new StringBuilder(); - string TokenName = null; - bool QuoteMode = false; - bool TokenIsQuoted = false; - while (i < aJSON.Length) - { - switch (aJSON[i]) - { - case '{': - if (QuoteMode) - { - Token.Append(aJSON[i]); - break; - } - stack.Push(new JSONObject()); - if (ctx != null) - { - ctx.Add(TokenName, stack.Peek()); - } - TokenName = null; - Token.Length = 0; - ctx = stack.Peek(); - break; - - case '[': - if (QuoteMode) - { - Token.Append(aJSON[i]); - break; - } - - stack.Push(new JSONArray()); - if (ctx != null) - { - ctx.Add(TokenName, stack.Peek()); - } - TokenName = null; - Token.Length = 0; - ctx = stack.Peek(); - break; - - case '}': - case ']': - if (QuoteMode) - { - - Token.Append(aJSON[i]); - break; - } - if (stack.Count == 0) - throw new Exception("JSON Parse: Too many closing brackets"); - - stack.Pop(); - if (Token.Length > 0 || TokenIsQuoted) - ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); - TokenIsQuoted = false; - TokenName = null; - Token.Length = 0; - if (stack.Count > 0) - ctx = stack.Peek(); - break; - - case ':': - if (QuoteMode) - { - Token.Append(aJSON[i]); - break; - } - TokenName = Token.ToString(); - Token.Length = 0; - TokenIsQuoted = false; - break; - - case '"': - QuoteMode ^= true; - TokenIsQuoted |= QuoteMode; - break; - - case ',': - if (QuoteMode) - { - Token.Append(aJSON[i]); - break; - } - if (Token.Length > 0 || TokenIsQuoted) - ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); - TokenIsQuoted = false; - TokenName = null; - Token.Length = 0; - TokenIsQuoted = false; - break; - - case '\r': - case '\n': - break; - - case ' ': - case '\t': - if (QuoteMode) - Token.Append(aJSON[i]); - break; - - case '\\': - ++i; - if (QuoteMode) - { - char C = aJSON[i]; - switch (C) - { - case 't': - Token.Append('\t'); - break; - case 'r': - Token.Append('\r'); - break; - case 'n': - Token.Append('\n'); - break; - case 'b': - Token.Append('\b'); - break; - case 'f': - Token.Append('\f'); - break; - case 'u': - { - string s = aJSON.Substring(i + 1, 4); - Token.Append((char)int.Parse( - s, - System.Globalization.NumberStyles.AllowHexSpecifier)); - i += 4; - break; - } - default: - Token.Append(C); - break; - } - } - break; - case '/': - if (allowLineComments && !QuoteMode && i + 1 < aJSON.Length && aJSON[i + 1] == '/') - { - while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') ; - break; - } - Token.Append(aJSON[i]); - break; - case '\uFEFF': // remove / ignore BOM (Byte Order Mark) - break; - - default: - Token.Append(aJSON[i]); - break; - } - ++i; - } - if (QuoteMode) - { - throw new Exception("JSON Parse: Quotation marks seems to be messed up."); - } - if (ctx == null) - return ParseElement(Token.ToString(), TokenIsQuoted); - return ctx; - } - } - // End of JSONNode - - public partial class JSONArray : JSONNode, IList - { - private List m_List = new List(); - private bool inline = false; - public override bool Inline - { - get { return inline; } - set { inline = value; } - } - - public override JSONNodeType Tag { get { return JSONNodeType.Array; } } - public override bool IsArray { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } - public bool IsReadOnly => false; - - public override JSONNode this[int aIndex] - { - get - { - if (aIndex < 0 || aIndex >= m_List.Count) - throw new IndexOutOfRangeException(); - - return m_List[aIndex]; - } - set - { - if (aIndex < 0 || aIndex >= m_List.Count) - throw new IndexOutOfRangeException(); - - if (value == null) - value = JSONNull.CreateOrGet(); - - m_List[aIndex] = value; - } - } - - public override int Count - { - get { return m_List.Count; } - } - - public override void Add(string aKey, JSONNode aItem) - { - if (aKey != null) - { - throw new NotImplementedException(); - } - - if (aItem == null) - aItem = JSONNull.CreateOrGet(); - m_List.Add(aItem); - } - - public override JSONNode Remove(string aKey) - { - throw new NotImplementedException(); - } - - public override JSONNode Remove(int aIndex) - { - if (aIndex < 0 || aIndex >= m_List.Count) - { - throw new IndexOutOfRangeException(); - } - - JSONNode tmp = m_List[aIndex]; - m_List.RemoveAt(aIndex); - return tmp; - } - - public override JSONNode Remove(JSONNode aNode) - { - m_List.Remove(aNode); - return aNode; - } - - public override JSONNode Clone() - { - var node = new JSONArray(); - node.m_List.Capacity = m_List.Capacity; - foreach (var n in m_List) - { - if (n != null) - node.Add(n.Clone()); - else - node.Add(null); - } - return node; - } - - public override IEnumerable Children - { - get - { - foreach (JSONNode N in m_List) - yield return N; - } - } - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append('['); - int count = m_List.Count; - if (inline) - aMode = JSONTextMode.Compact; - for (int i = 0; i < count; i++) - { - if (i > 0) - aSB.Append(','); - if (aMode == JSONTextMode.Indent) - aSB.AppendLine(); - - if (aMode == JSONTextMode.Indent) - aSB.Append(' ', aIndent + aIndentInc); - m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); - } - if (aMode == JSONTextMode.Indent) - aSB.AppendLine().Append(' ', aIndent); - aSB.Append(']'); - } - - public int IndexOf(JSONNode item) - { - return m_List.IndexOf(item); - } - - public void Insert(int index, JSONNode item) - { - m_List.Insert(index, item); - } - - public void RemoveAt(int index) - { - m_List.RemoveAt(index); - } - - public void Clear() - { - m_List.Clear(); - } - - public bool Contains(JSONNode item) - { - return m_List.Contains(item); - } - - public void CopyTo(JSONNode[] array, int arrayIndex) - { - m_List.CopyTo(array, arrayIndex); - } - - bool ICollection.Remove(JSONNode item) - { - return m_List.Remove(item); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return m_List.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return m_List.GetEnumerator(); - } - } - // End of JSONArray - - public partial class JSONObject : JSONNode, IDictionary - { - private Dictionary m_Dict = new Dictionary(); - - private bool inline = false; - - public override bool Inline - { - get { return inline; } - set { inline = value; } - } - - public override JSONNodeType Tag { get { return JSONNodeType.Object; } } - public override bool IsObject { get { return true; } } - - public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); } - - ICollection IDictionary.Keys - { - get - { - return m_Dict.Keys; - } - } - - ICollection IDictionary.Values - { - get - { - return m_Dict.Values; - } - } - - public bool IsReadOnly => false; - - public override JSONNode this[string aKey] - { - get - { - if (m_Dict.ContainsKey(aKey)) - return m_Dict[aKey]; - else - return new JSONLazyCreator(this, aKey); - } - set - { - if (value == null) - value = JSONNull.CreateOrGet(); - if (m_Dict.ContainsKey(aKey)) - m_Dict[aKey] = value; - else - m_Dict.Add(aKey, value); - } - } - - public override int Count - { - get { return m_Dict.Count; } - } - - public override void Add(string aKey, JSONNode aItem) - { - if (aKey == null) - { - throw new NotImplementedException(); - } - - if (aItem == null) - aItem = JSONNull.CreateOrGet(); - - m_Dict[aKey] = aItem; - } - - public override JSONNode Remove(string aKey) - { - if (!m_Dict.ContainsKey(aKey)) - return null; - JSONNode tmp = m_Dict[aKey]; - m_Dict.Remove(aKey); - return tmp; - } - - public override JSONNode Remove(int aIndex) - { - throw new NotImplementedException(); - } - - public override JSONNode Remove(JSONNode aNode) - { - foreach (var kvp in m_Dict) - { - if (kvp.Value == aNode) - { - m_Dict.Remove(kvp.Key); - return kvp.Value; - } - } - - return null; - } - - public override JSONNode Clone() - { - var node = new JSONObject(); - foreach (var n in m_Dict) - { - node.Add(n.Key, n.Value.Clone()); - } - return node; - } - - public override bool HasKey(string aKey) - { - return m_Dict.ContainsKey(aKey); - } - - public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) - { - JSONNode res; - if (m_Dict.TryGetValue(aKey, out res)) - return res; - return aDefault; - } - - public override IEnumerable Children - { - get - { - foreach (KeyValuePair N in m_Dict) - yield return N.Value; - } - } - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append('{'); - bool first = true; - if (inline) - aMode = JSONTextMode.Compact; - foreach (var k in m_Dict) - { - if (!first) - aSB.Append(','); - first = false; - if (aMode == JSONTextMode.Indent) - aSB.AppendLine(); - if (aMode == JSONTextMode.Indent) - aSB.Append(' ', aIndent + aIndentInc); - aSB.Append('\"').Append(Escape(k.Key)).Append('\"'); - if (aMode == JSONTextMode.Compact) - aSB.Append(':'); - else - aSB.Append(" : "); - k.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); - } - if (aMode == JSONTextMode.Indent) - aSB.AppendLine().Append(' ', aIndent); - aSB.Append('}'); - } - - public bool ContainsKey(string key) - { - return m_Dict.ContainsKey(key); - } - - bool IDictionary.Remove(string key) - { - return m_Dict.Remove(key); - } - - public bool TryGetValue(string key, out JSONNode value) - { - return m_Dict.TryGetValue(key, out value); - } - - public void Add(KeyValuePair item) - { - ((IDictionary)m_Dict).Add(item); - } - - public void Clear() - { - m_Dict.Clear(); - } - - public bool Contains(KeyValuePair item) - { - return m_Dict.ContainsKey(item.Key); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - ((IDictionary)m_Dict).CopyTo(array, arrayIndex); - } - - public bool Remove(KeyValuePair item) - { - return ((IDictionary)m_Dict).Remove(item); - } - - IEnumerator> IEnumerable>.GetEnumerator() - { - return m_Dict.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return m_Dict.GetEnumerator(); - } - } - // End of JSONObject - - public partial class JSONString : JSONNode - { - private string m_Data; - - public override JSONNodeType Tag { get { return JSONNodeType.String; } } - public override bool IsString { get { return true; } } - public override bool IsNull { get { return m_Data == null; } } - - public override Enumerator GetEnumerator() { return new Enumerator(); } - - public override string Value - { - get { return m_Data; } - protected set - { - m_Data = value; - } - } - - public JSONString() : this(null) - { - - } - - public JSONString(string aData) - { - m_Data = aData; - } - - public override JSONNode Clone() - { - return new JSONString(m_Data); - } - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - if (m_Data == null) - { - aSB.Append(TOKEN_NULL); - return; - } - - aSB.Append('\"').Append(Escape(m_Data)).Append('\"'); - } - - public override bool Equals(object obj) - { - switch (obj) - { - case JSONNull nullObj: - return m_Data == null; - case string stringObj: - return m_Data.Equals(stringObj); - case JSONString jsonStringObj: - return m_Data.Equals(jsonStringObj.m_Data); - default: - return base.Equals(obj); - } - } - - public override int GetHashCode() - { - return m_Data.GetHashCode(); - } - } - // End of JSONString - - public partial class JSONNumber : JSONNode - { - private double m_Data; - - public override JSONNodeType Tag { get { return JSONNodeType.Number; } } - public override bool IsNumber { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } - - public override string Value - { - get { return m_Data.ToString("R", CultureInfo.InvariantCulture); } - protected set - { - double v; - if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) - m_Data = v; - } - } - - public override double AsDouble - { - get { return m_Data; } - set { m_Data = value; } - } - - public override long AsLong - { - get { return (long)m_Data; } - set { m_Data = value; } - } - - public JSONNumber(double aData) - { - m_Data = aData; - } - - public JSONNumber(string aData) - { - Value = aData; - } - - public override JSONNode Clone() - { - return new JSONNumber(m_Data); - } - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append(Value); - } - - internal static bool IsNumeric(object value) - { - return value is int || value is uint - || value is float || value is double - || value is decimal - || value is long || value is ulong - || value is short || value is ushort - || value is sbyte || value is byte; - } - - public override bool Equals(object obj) - { - switch (obj) - { - case null: - return false; - case JSONNumber jsonNumber: - return m_Data == jsonNumber.m_Data; - default: - if (IsNumeric(obj)) - return Convert.ToDouble(obj) == m_Data; - return base.Equals(obj); - } - } - - public override int GetHashCode() - { - return m_Data.GetHashCode(); - } - } - // End of JSONNumber - - public partial class JSONBool : JSONNode - { - private bool m_Data; - - public override JSONNodeType Tag { get { return JSONNodeType.Boolean; } } - public override bool IsBoolean { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } - - public override string Value - { - get { return m_Data.ToString(); } - protected set - { - bool v; - if (bool.TryParse(value, out v)) - m_Data = v; - } - } - - public override bool AsBool - { - get { return m_Data; } - set { m_Data = value; } - } - - public JSONBool(bool aData) - { - m_Data = aData; - } - - public JSONBool(string aData) - { - Value = aData; - } - - public override JSONNode Clone() - { - return new JSONBool(m_Data); - } - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append((m_Data) ? TOKEN_TRUE : TOKEN_FALSE); - } - - public override bool Equals(object obj) - { - switch (obj) - { - case null: - return false; - case JSONBool jsonBool: - return m_Data == jsonBool.m_Data; - case bool boolObj: - return m_Data == boolObj; - default: - return false; - } - } - - public override int GetHashCode() - { - return m_Data.GetHashCode(); - } - } - // End of JSONBool - - public partial class JSONNull : JSONNode - { - static JSONNull m_StaticInstance = new JSONNull(); - public static bool reuseSameInstance = true; - public static JSONNull CreateOrGet() - { - if (reuseSameInstance) - return m_StaticInstance; - return new JSONNull(); - } - - private JSONNull() { } - - public override JSONNodeType Tag { get { return JSONNodeType.NullValue; } } - public override bool IsNull { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } - - public override string Value - { - get { return null; } - protected set { } - } - - public override bool AsBool - { - get { return false; } - set { } - } - - public override JSONNode Clone() - { - return CreateOrGet(); - } - - public override bool Equals(object obj) - { - if (object.ReferenceEquals(this, obj)) - return true; - return (obj is JSONNull); - } - - public override int GetHashCode() - { - return 0; - } - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append(TOKEN_NULL); - } - } - // End of JSONNull - - internal partial class JSONLazyCreator : JSONNode - { - private JSONNode m_Node = null; - private string m_Key = null; - public override JSONNodeType Tag { get { return JSONNodeType.None; } } - public override bool IsNull { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } - - public JSONLazyCreator(JSONNode aNode) - { - m_Node = aNode; - m_Key = null; - } - - public JSONLazyCreator(JSONNode aNode, string aKey) - { - m_Node = aNode; - m_Key = aKey; - } - - private T Set(T aVal) where T : JSONNode - { - if (m_Key == null) - m_Node.Add(aVal); - else - m_Node.Add(m_Key, aVal); - m_Node = null; // Be GC friendly. - return aVal; - } - - public override JSONNode this[int aIndex] - { - get { return new JSONLazyCreator(this); } - set { Set(new JSONArray()).Add(value); } - } - - public override JSONNode this[string aKey] - { - get { return new JSONLazyCreator(this, aKey); } - set { Set(new JSONObject()).Add(aKey, value); } - } - - public override void Add(JSONNode aItem) - { - Set(new JSONArray()).Add(aItem); - } - - public override void Add(string aKey, JSONNode aItem) - { - Set(new JSONObject()).Add(aKey, aItem); - } - - public static bool operator ==(JSONLazyCreator a, object b) - { - return a.Equals(b); - } - - public static bool operator !=(JSONLazyCreator a, object b) - { - return !(a == b); - } - - public override bool Equals(object obj) - { - if (obj == null) - return true; - if (obj is JSONNull) - return true; - - return System.Object.ReferenceEquals(this, obj); - } - - public override int GetHashCode() - { - return 0; - } - - public override byte AsByte - { - get { Set(new JSONNumber(0)); return (byte) 0; } - set { Set(new JSONNumber(value)); } - } - - public override int AsInt - { - get { Set(new JSONNumber(0)); return 0; } - set { Set(new JSONNumber(value)); } - } - - public override float AsFloat - { - get { Set(new JSONNumber(0.0f)); return 0.0f; } - set { Set(new JSONNumber(value)); } - } - - public override double AsDouble - { - get { Set(new JSONNumber(0.0)); return 0.0; } - set { Set(new JSONNumber(value)); } - } - - public override long AsLong - { - get - { - if (longAsString) - Set(new JSONString("0")); - else - Set(new JSONNumber(0.0)); - return 0L; - } - set - { - if (longAsString) - Set(new JSONString(value.ToString())); - else - Set(new JSONNumber(value)); - } - } - - public override bool AsBool - { - get { Set(new JSONBool(false)); return false; } - set { Set(new JSONBool(value)); } - } - - public override JSONArray AsArray - { - get { return Set(new JSONArray()); } - } - - public override JSONObject AsObject - { - get { return Set(new JSONObject()); } - } - - internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) - { - aSB.Append(TOKEN_NULL); - } - } - // End of JSONLazyCreator - - public static partial class JSON - { - public static JSONNode Parse(string aJSON) - { - return JSONNode.Parse(aJSON); - } - - public static bool TryParse(string aJSON, out JSONNode aResult) - { - try - { - aResult = JSON.Parse(aJSON); - return true; - } - catch (Exception e) - { + public enum JSONNodeType + { + Array = 1, + Object = 2, + String = 3, + Number = 4, + NullValue = 5, + Boolean = 6, + None = 7, + Custom = 0xFF, + } + public enum JSONTextMode + { + Compact, + Indent + } + + public abstract partial class JSONNode + { + protected const string TOKEN_NULL = "null"; + protected const string TOKEN_TRUE = "true"; + protected const string TOKEN_FALSE = "false"; + + #region Enumerators + public struct Enumerator + { + private enum Type { None, Array, Object } + private Type type; + private Dictionary.Enumerator m_Object; + private List.Enumerator m_Array; + public bool IsValid { get { return type != Type.None; } } + + public Enumerator(List.Enumerator aArrayEnum) + { + type = Type.Array; + m_Object = default(Dictionary.Enumerator); + m_Array = aArrayEnum; + } + + public Enumerator(Dictionary.Enumerator aDictEnum) + { + type = Type.Object; + m_Object = aDictEnum; + m_Array = default(List.Enumerator); + } + + public KeyValuePair Current + { + get + { + if (type == Type.Array) + return new KeyValuePair(string.Empty, m_Array.Current); + else if (type == Type.Object) + return m_Object.Current; + return new KeyValuePair(string.Empty, null); + } + } + + public bool MoveNext() + { + if (type == Type.Array) + return m_Array.MoveNext(); + else if (type == Type.Object) + return m_Object.MoveNext(); + return false; + } + } + + public struct ValueEnumerator + { + private Enumerator m_Enumerator; + public ValueEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } + public ValueEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } + public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } + public JSONNode Current { get { return m_Enumerator.Current.Value; } } + public bool MoveNext() { return m_Enumerator.MoveNext(); } + public ValueEnumerator GetEnumerator() { return this; } + } + + public struct KeyEnumerator + { + private Enumerator m_Enumerator; + public KeyEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } + public KeyEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } + public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } + public string Current { get { return m_Enumerator.Current.Key; } } + public bool MoveNext() { return m_Enumerator.MoveNext(); } + public KeyEnumerator GetEnumerator() { return this; } + } + + public class LinqEnumerator : IEnumerator>, IEnumerable> + { + private JSONNode m_Node; + private Enumerator m_Enumerator; + + internal LinqEnumerator(JSONNode aNode) + { + m_Node = aNode; + if (m_Node != null) + m_Enumerator = m_Node.GetEnumerator(); + } + + public KeyValuePair Current { get { return m_Enumerator.Current; } } + object IEnumerator.Current { get { return m_Enumerator.Current; } } + public bool MoveNext() { return m_Enumerator.MoveNext(); } + + public void Dispose() + { + m_Node = null; + m_Enumerator = new Enumerator(); + } + + public IEnumerator> GetEnumerator() + { + return new LinqEnumerator(m_Node); + } + + public void Reset() + { + if (m_Node != null) + m_Enumerator = m_Node.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new LinqEnumerator(m_Node); + } + } + + #endregion Enumerators + + #region common interface + + public static bool forceASCII = false; // Use Unicode by default + public static bool longAsString = false; // lazy creator creates a JSONString instead of JSONNumber + public static bool allowLineComments = true; // allow "//"-style comments at the end of a line + + public abstract JSONNodeType Tag { get; } + + public virtual JSONNode this[int aIndex] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } + public virtual JSONNode this[string aKey] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } + + public virtual string Value { get { return null; } protected set { } } + + public virtual int Count { get { return 0; } } + + public virtual bool IsNumber { get { return false; } } + public virtual bool IsString { get { return false; } } + public virtual bool IsBoolean { get { return false; } } + public virtual bool IsNull { get { return false; } } + public virtual bool IsArray { get { return false; } } + public virtual bool IsObject { get { return false; } } + + public virtual bool Inline { get { return false; } set { } } + + public virtual void Add(string aKey, JSONNode aItem) + { + throw new NotImplementedException(); + } + + public virtual void Add(JSONNode aItem) + { + Add(null, aItem); + } + + public virtual JSONNode Remove(string aKey) + { + throw new NotImplementedException(); + } + + public virtual JSONNode Remove(int aIndex) + { + throw new NotImplementedException(); + } + + public virtual JSONNode Remove(JSONNode aNode) + { + throw new NotImplementedException(); + } + + public virtual JSONNode Clone() + { + throw new NotImplementedException(); + } + + public virtual IEnumerable Children + { + get + { + yield break; + } + } + + public IEnumerable DeepChildren + { + get + { + foreach (var C in Children) + foreach (var D in C.DeepChildren) + yield return D; + } + } + + public virtual bool HasKey(string aKey) + { + return false; + } + + public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) + { + return aDefault; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + WriteToStringBuilder(sb, 0, 0, JSONTextMode.Compact); + return sb.ToString(); + } + + public virtual string ToString(int aIndent) + { + StringBuilder sb = new StringBuilder(); + WriteToStringBuilder(sb, 0, aIndent, aIndent > 0 ? JSONTextMode.Indent : JSONTextMode.Compact); + return sb.ToString(); + } + + internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); + + public abstract Enumerator GetEnumerator(); + public IEnumerable> Linq { get { return new LinqEnumerator(this); } } + public KeyEnumerator Keys { get { return new KeyEnumerator(GetEnumerator()); } } + public ValueEnumerator Values { get { return new ValueEnumerator(GetEnumerator()); } } + + #endregion common interface + + #region typecasting properties + + public virtual double AsDouble + { + get + { + double v = 0.0; + if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) + return v; + return 0.0; + } + set + { + Value = value.ToString(CultureInfo.InvariantCulture); + } + } + + public virtual byte AsByte + { + get { return (byte)AsDouble; } + set { AsDouble = value; } + } + + public virtual int AsInt + { + get { return (int)AsDouble; } + set { AsDouble = value; } + } + + public virtual float AsFloat + { + get { return (float)AsDouble; } + set { AsDouble = value; } + } + + public virtual bool AsBool + { + get + { + bool v = false; + if (bool.TryParse(Value, out v)) + return v; + return !string.IsNullOrEmpty(Value); + } + set + { + Value = (value) ? TOKEN_TRUE : TOKEN_FALSE; + } + } + + public virtual long AsLong + { + get + { + long val = 0; + if (long.TryParse(Value, out val)) + return val; + return 0L; + } + set + { + Value = value.ToString(); + } + } + + public virtual JSONArray AsArray + { + get + { + return this as JSONArray; + } + } + + public virtual JSONObject AsObject + { + get + { + return this as JSONObject; + } + } + + #endregion typecasting properties + + #region operators + + public static implicit operator JSONNode(string s) + { + return new JSONString(s); + } + + public static implicit operator string(JSONNode d) + { + return (d == null) ? null : d.Value; + } + + public static implicit operator JSONNode(double n) + { + return new JSONNumber(n); + } + + public static implicit operator double(JSONNode d) + { + return (d == null) ? 0 : d.AsDouble; + } + + public static implicit operator JSONNode(float n) + { + return new JSONNumber(n); + } + + public static implicit operator float(JSONNode d) + { + return (d == null) ? 0 : d.AsFloat; + } + + public static implicit operator JSONNode(int n) + { + return new JSONNumber(n); + } + + public static implicit operator byte(JSONNode d) + { + return (d == null) ? (byte)0 : d.AsByte; + } + + public static implicit operator int(JSONNode d) + { + return (d == null) ? 0 : d.AsInt; + } + + public static implicit operator JSONNode(long n) + { + if (longAsString) + return new JSONString(n.ToString()); + return new JSONNumber(n); + } + + public static implicit operator long(JSONNode d) + { + return (d == null) ? 0L : d.AsLong; + } + + public static implicit operator JSONNode(bool b) + { + return new JSONBool(b); + } + + public static implicit operator bool(JSONNode d) + { + return (d == null) ? false : d.AsBool; + } + + public static implicit operator JSONNode(KeyValuePair aKeyValue) + { + return aKeyValue.Value; + } + + public static bool operator ==(JSONNode a, object b) + { + if (ReferenceEquals(a, b)) + return true; + bool aIsNull = a is JSONNull || ReferenceEquals(a, null) || a is JSONLazyCreator; + bool bIsNull = b is JSONNull || ReferenceEquals(b, null) || b is JSONLazyCreator; + if (aIsNull && bIsNull) + return true; + return !aIsNull && a.Equals(b); + } + + public static bool operator !=(JSONNode a, object b) + { + return !(a == b); + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + #endregion operators + + [ThreadStatic] + private static StringBuilder m_EscapeBuilder; + + internal static StringBuilder EscapeBuilder + { + get + { + if (m_EscapeBuilder == null) + m_EscapeBuilder = new StringBuilder(); + return m_EscapeBuilder; + } + } + + internal static string Escape(string aText) + { + var sb = EscapeBuilder; + sb.Length = 0; + if (sb.Capacity < aText.Length + aText.Length / 10) + sb.Capacity = aText.Length + aText.Length / 10; + foreach (char c in aText) + { + switch (c) + { + case '\\': + sb.Append("\\\\"); + break; + case '\"': + sb.Append("\\\""); + break; + case '\n': + sb.Append("\\n"); + break; + case '\r': + sb.Append("\\r"); + break; + case '\t': + sb.Append("\\t"); + break; + case '\b': + sb.Append("\\b"); + break; + case '\f': + sb.Append("\\f"); + break; + default: + if (c < ' ' || (forceASCII && c > 127)) + { + ushort val = c; + sb.Append("\\u").Append(val.ToString("X4")); + } + else + sb.Append(c); + break; + } + } + string result = sb.ToString(); + sb.Length = 0; + return result; + } + + private static JSONNode ParseElement(string token, bool quoted) + { + if (quoted) + return token; + if (token.Equals(TOKEN_FALSE, StringComparison.InvariantCultureIgnoreCase) + || token.Equals(TOKEN_TRUE, StringComparison.InvariantCultureIgnoreCase)) + return token.Equals(TOKEN_TRUE, StringComparison.InvariantCultureIgnoreCase); + if (token.Equals(TOKEN_NULL, StringComparison.InvariantCultureIgnoreCase)) + return JSONNull.CreateOrGet(); + + if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out double val)) + return val; + else + return token; + } + + public static JSONNode Parse(string aJSON) + { + Stack stack = new Stack(); + JSONNode ctx = null; + int i = 0; + StringBuilder Token = new StringBuilder(); + string TokenName = null; + bool QuoteMode = false; + bool TokenIsQuoted = false; + while (i < aJSON.Length) + { + switch (aJSON[i]) + { + case '{': + if (QuoteMode) + { + Token.Append(aJSON[i]); + break; + } + stack.Push(new JSONObject()); + if (ctx != null) + { + ctx.Add(TokenName, stack.Peek()); + } + TokenName = null; + Token.Length = 0; + ctx = stack.Peek(); + break; + + case '[': + if (QuoteMode) + { + Token.Append(aJSON[i]); + break; + } + + stack.Push(new JSONArray()); + if (ctx != null) + { + ctx.Add(TokenName, stack.Peek()); + } + TokenName = null; + Token.Length = 0; + ctx = stack.Peek(); + break; + + case '}': + case ']': + if (QuoteMode) + { + + Token.Append(aJSON[i]); + break; + } + if (stack.Count == 0) + throw new Exception("JSON Parse: Too many closing brackets"); + + stack.Pop(); + if (Token.Length > 0 || TokenIsQuoted) + ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); + TokenIsQuoted = false; + TokenName = null; + Token.Length = 0; + if (stack.Count > 0) + ctx = stack.Peek(); + break; + + case ':': + if (QuoteMode) + { + Token.Append(aJSON[i]); + break; + } + TokenName = Token.ToString(); + Token.Length = 0; + TokenIsQuoted = false; + break; + + case '"': + QuoteMode ^= true; + TokenIsQuoted |= QuoteMode; + break; + + case ',': + if (QuoteMode) + { + Token.Append(aJSON[i]); + break; + } + if (Token.Length > 0 || TokenIsQuoted) + ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); + TokenIsQuoted = false; + TokenName = null; + Token.Length = 0; + TokenIsQuoted = false; + break; + + case '\r': + case '\n': + break; + + case ' ': + case '\t': + if (QuoteMode) + Token.Append(aJSON[i]); + break; + + case '\\': + ++i; + if (QuoteMode) + { + char C = aJSON[i]; + switch (C) + { + case 't': + Token.Append('\t'); + break; + case 'r': + Token.Append('\r'); + break; + case 'n': + Token.Append('\n'); + break; + case 'b': + Token.Append('\b'); + break; + case 'f': + Token.Append('\f'); + break; + case 'u': + { + string s = aJSON.Substring(i + 1, 4); + Token.Append((char)int.Parse( + s, + System.Globalization.NumberStyles.AllowHexSpecifier)); + i += 4; + break; + } + default: + Token.Append(C); + break; + } + } + break; + case '/': + if (allowLineComments && !QuoteMode && i + 1 < aJSON.Length && aJSON[i + 1] == '/') + { + while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') ; + break; + } + Token.Append(aJSON[i]); + break; + case '\uFEFF': // remove / ignore BOM (Byte Order Mark) + break; + + default: + Token.Append(aJSON[i]); + break; + } + ++i; + } + if (QuoteMode) + { + throw new Exception("JSON Parse: Quotation marks seems to be messed up."); + } + if (ctx == null) + return ParseElement(Token.ToString(), TokenIsQuoted); + return ctx; + } + } + // End of JSONNode + + public partial class JSONArray : JSONNode, IList + { + private List m_List = new List(); + private bool inline = false; + public override bool Inline + { + get { return inline; } + set { inline = value; } + } + + public override JSONNodeType Tag { get { return JSONNodeType.Array; } } + public override bool IsArray { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } + public bool IsReadOnly => false; + + public override JSONNode this[int aIndex] + { + get + { + if (aIndex < 0 || aIndex >= m_List.Count) + throw new IndexOutOfRangeException(); + + return m_List[aIndex]; + } + set + { + if (aIndex < 0 || aIndex >= m_List.Count) + throw new IndexOutOfRangeException(); + + if (value == null) + value = JSONNull.CreateOrGet(); + + m_List[aIndex] = value; + } + } + + public override int Count + { + get { return m_List.Count; } + } + + public override void Add(string aKey, JSONNode aItem) + { + if (aKey != null) + { + throw new NotImplementedException(); + } + + if (aItem == null) + aItem = JSONNull.CreateOrGet(); + m_List.Add(aItem); + } + + public override JSONNode Remove(string aKey) + { + throw new NotImplementedException(); + } + + public override JSONNode Remove(int aIndex) + { + if (aIndex < 0 || aIndex >= m_List.Count) + { + throw new IndexOutOfRangeException(); + } + + JSONNode tmp = m_List[aIndex]; + m_List.RemoveAt(aIndex); + return tmp; + } + + public override JSONNode Remove(JSONNode aNode) + { + m_List.Remove(aNode); + return aNode; + } + + public override JSONNode Clone() + { + var node = new JSONArray(); + node.m_List.Capacity = m_List.Capacity; + foreach (var n in m_List) + { + if (n != null) + node.Add(n.Clone()); + else + node.Add(null); + } + return node; + } + + public override IEnumerable Children + { + get + { + foreach (JSONNode N in m_List) + yield return N; + } + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append('['); + int count = m_List.Count; + if (inline) + aMode = JSONTextMode.Compact; + for (int i = 0; i < count; i++) + { + if (i > 0) + aSB.Append(','); + if (aMode == JSONTextMode.Indent) + aSB.AppendLine(); + + if (aMode == JSONTextMode.Indent) + aSB.Append(' ', aIndent + aIndentInc); + m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); + } + if (aMode == JSONTextMode.Indent) + aSB.AppendLine().Append(' ', aIndent); + aSB.Append(']'); + } + + public int IndexOf(JSONNode item) + { + return m_List.IndexOf(item); + } + + public void Insert(int index, JSONNode item) + { + m_List.Insert(index, item); + } + + public void RemoveAt(int index) + { + m_List.RemoveAt(index); + } + + public void Clear() + { + m_List.Clear(); + } + + public bool Contains(JSONNode item) + { + return m_List.Contains(item); + } + + public void CopyTo(JSONNode[] array, int arrayIndex) + { + m_List.CopyTo(array, arrayIndex); + } + + bool ICollection.Remove(JSONNode item) + { + return m_List.Remove(item); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return m_List.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return m_List.GetEnumerator(); + } + } + // End of JSONArray + + public partial class JSONObject : JSONNode, IDictionary + { + private Dictionary m_Dict = new Dictionary(); + + private bool inline = false; + + public override bool Inline + { + get { return inline; } + set { inline = value; } + } + + public override JSONNodeType Tag { get { return JSONNodeType.Object; } } + public override bool IsObject { get { return true; } } + + public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); } + + ICollection IDictionary.Keys + { + get + { + return m_Dict.Keys; + } + } + + ICollection IDictionary.Values + { + get + { + return m_Dict.Values; + } + } + + public bool IsReadOnly => false; + + public override JSONNode this[string aKey] + { + get + { + if (m_Dict.ContainsKey(aKey)) + return m_Dict[aKey]; + else + return new JSONLazyCreator(this, aKey); + } + set + { + if (value == null) + value = JSONNull.CreateOrGet(); + if (m_Dict.ContainsKey(aKey)) + m_Dict[aKey] = value; + else + m_Dict.Add(aKey, value); + } + } + + public override int Count + { + get { return m_Dict.Count; } + } + + public override void Add(string aKey, JSONNode aItem) + { + if (aKey == null) + { + throw new NotImplementedException(); + } + + if (aItem == null) + aItem = JSONNull.CreateOrGet(); + + m_Dict[aKey] = aItem; + } + + public override JSONNode Remove(string aKey) + { + if (!m_Dict.ContainsKey(aKey)) + return null; + JSONNode tmp = m_Dict[aKey]; + m_Dict.Remove(aKey); + return tmp; + } + + public override JSONNode Remove(int aIndex) + { + throw new NotImplementedException(); + } + + public override JSONNode Remove(JSONNode aNode) + { + foreach (var kvp in m_Dict) + { + if (kvp.Value == aNode) + { + m_Dict.Remove(kvp.Key); + return kvp.Value; + } + } + + return null; + } + + public override JSONNode Clone() + { + var node = new JSONObject(); + foreach (var n in m_Dict) + { + node.Add(n.Key, n.Value.Clone()); + } + return node; + } + + public override bool HasKey(string aKey) + { + return m_Dict.ContainsKey(aKey); + } + + public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) + { + JSONNode res; + if (m_Dict.TryGetValue(aKey, out res)) + return res; + return aDefault; + } + + public override IEnumerable Children + { + get + { + foreach (KeyValuePair N in m_Dict) + yield return N.Value; + } + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append('{'); + bool first = true; + if (inline) + aMode = JSONTextMode.Compact; + foreach (var k in m_Dict) + { + if (!first) + aSB.Append(','); + first = false; + if (aMode == JSONTextMode.Indent) + aSB.AppendLine(); + if (aMode == JSONTextMode.Indent) + aSB.Append(' ', aIndent + aIndentInc); + aSB.Append('\"').Append(Escape(k.Key)).Append('\"'); + if (aMode == JSONTextMode.Compact) + aSB.Append(':'); + else + aSB.Append(" : "); + k.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); + } + if (aMode == JSONTextMode.Indent) + aSB.AppendLine().Append(' ', aIndent); + aSB.Append('}'); + } + + public bool ContainsKey(string key) + { + return m_Dict.ContainsKey(key); + } + + bool IDictionary.Remove(string key) + { + return m_Dict.Remove(key); + } + + public bool TryGetValue(string key, out JSONNode value) + { + return m_Dict.TryGetValue(key, out value); + } + + public void Add(KeyValuePair item) + { + ((IDictionary)m_Dict).Add(item); + } + + public void Clear() + { + m_Dict.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return m_Dict.ContainsKey(item.Key); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((IDictionary)m_Dict).CopyTo(array, arrayIndex); + } + + public bool Remove(KeyValuePair item) + { + return ((IDictionary)m_Dict).Remove(item); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return m_Dict.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return m_Dict.GetEnumerator(); + } + } + // End of JSONObject + + public partial class JSONString : JSONNode + { + private string m_Data; + + public override JSONNodeType Tag { get { return JSONNodeType.String; } } + public override bool IsString { get { return true; } } + public override bool IsNull { get { return m_Data == null; } } + + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public override string Value + { + get { return m_Data; } + protected set + { + m_Data = value; + } + } + + public JSONString() : this(null) + { + + } + + public JSONString(string aData) + { + m_Data = aData; + } + + public override JSONNode Clone() + { + return new JSONString(m_Data); + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + if (m_Data == null) + { + aSB.Append(TOKEN_NULL); + return; + } + + aSB.Append('\"').Append(Escape(m_Data)).Append('\"'); + } + + public override bool Equals(object obj) + { + switch (obj) + { + case JSONNull nullObj: + return m_Data == null; + case string stringObj: + return m_Data.Equals(stringObj); + case JSONString jsonStringObj: + return m_Data.Equals(jsonStringObj.m_Data); + default: + return base.Equals(obj); + } + } + + public override int GetHashCode() + { + return m_Data.GetHashCode(); + } + } + // End of JSONString + + public partial class JSONNumber : JSONNode + { + private double m_Data; + + public override JSONNodeType Tag { get { return JSONNodeType.Number; } } + public override bool IsNumber { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public override string Value + { + get { return m_Data.ToString("R", CultureInfo.InvariantCulture); } + protected set + { + double v; + if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) + m_Data = v; + } + } + + public override double AsDouble + { + get { return m_Data; } + set { m_Data = value; } + } + + public override long AsLong + { + get { return (long)m_Data; } + set { m_Data = value; } + } + + public JSONNumber(double aData) + { + m_Data = aData; + } + + public JSONNumber(string aData) + { + Value = aData; + } + + public override JSONNode Clone() + { + return new JSONNumber(m_Data); + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append(Value); + } + + internal static bool IsNumeric(object value) + { + return value is int || value is uint + || value is float || value is double + || value is decimal + || value is long || value is ulong + || value is short || value is ushort + || value is sbyte || value is byte; + } + + public override bool Equals(object obj) + { + switch (obj) + { + case null: + return false; + case JSONNumber jsonNumber: + return m_Data == jsonNumber.m_Data; + default: + if (IsNumeric(obj)) + return Convert.ToDouble(obj) == m_Data; + return base.Equals(obj); + } + } + + public override int GetHashCode() + { + return m_Data.GetHashCode(); + } + } + // End of JSONNumber + + public partial class JSONBool : JSONNode + { + private bool m_Data; + + public override JSONNodeType Tag { get { return JSONNodeType.Boolean; } } + public override bool IsBoolean { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public override string Value + { + get { return m_Data.ToString(); } + protected set + { + bool v; + if (bool.TryParse(value, out v)) + m_Data = v; + } + } + + public override bool AsBool + { + get { return m_Data; } + set { m_Data = value; } + } + + public JSONBool(bool aData) + { + m_Data = aData; + } + + public JSONBool(string aData) + { + Value = aData; + } + + public override JSONNode Clone() + { + return new JSONBool(m_Data); + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append((m_Data) ? TOKEN_TRUE : TOKEN_FALSE); + } + + public override bool Equals(object obj) + { + switch (obj) + { + case null: + return false; + case JSONBool jsonBool: + return m_Data == jsonBool.m_Data; + case bool boolObj: + return m_Data == boolObj; + default: + return false; + } + } + + public override int GetHashCode() + { + return m_Data.GetHashCode(); + } + } + // End of JSONBool + + public partial class JSONNull : JSONNode + { + static JSONNull m_StaticInstance = new JSONNull(); + public static bool reuseSameInstance = true; + public static JSONNull CreateOrGet() + { + if (reuseSameInstance) + return m_StaticInstance; + return new JSONNull(); + } + + private JSONNull() { } + + public override JSONNodeType Tag { get { return JSONNodeType.NullValue; } } + public override bool IsNull { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public override string Value + { + get { return null; } + protected set { } + } + + public override bool AsBool + { + get { return false; } + set { } + } + + public override JSONNode Clone() + { + return CreateOrGet(); + } + + public override bool Equals(object obj) + { + if (object.ReferenceEquals(this, obj)) + return true; + return (obj is JSONNull); + } + + public override int GetHashCode() + { + return 0; + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append(TOKEN_NULL); + } + } + // End of JSONNull + + internal partial class JSONLazyCreator : JSONNode + { + private JSONNode m_Node = null; + private string m_Key = null; + public override JSONNodeType Tag { get { return JSONNodeType.None; } } + public override bool IsNull { get { return true; } } + public override Enumerator GetEnumerator() { return new Enumerator(); } + + public JSONLazyCreator(JSONNode aNode) + { + m_Node = aNode; + m_Key = null; + } + + public JSONLazyCreator(JSONNode aNode, string aKey) + { + m_Node = aNode; + m_Key = aKey; + } + + private T Set(T aVal) where T : JSONNode + { + if (m_Key == null) + m_Node.Add(aVal); + else + m_Node.Add(m_Key, aVal); + m_Node = null; // Be GC friendly. + return aVal; + } + + public override JSONNode this[int aIndex] + { + get { return new JSONLazyCreator(this); } + set { Set(new JSONArray()).Add(value); } + } + + public override JSONNode this[string aKey] + { + get { return new JSONLazyCreator(this, aKey); } + set { Set(new JSONObject()).Add(aKey, value); } + } + + public override void Add(JSONNode aItem) + { + Set(new JSONArray()).Add(aItem); + } + + public override void Add(string aKey, JSONNode aItem) + { + Set(new JSONObject()).Add(aKey, aItem); + } + + public static bool operator ==(JSONLazyCreator a, object b) + { + return a.Equals(b); + } + + public static bool operator !=(JSONLazyCreator a, object b) + { + return !(a == b); + } + + public override bool Equals(object obj) + { + if (obj == null) + return true; + if (obj is JSONNull) + return true; + + return System.Object.ReferenceEquals(this, obj); + } + + public override int GetHashCode() + { + return 0; + } + + public override byte AsByte + { + get { Set(new JSONNumber(0)); return (byte)0; } + set { Set(new JSONNumber(value)); } + } + + public override int AsInt + { + get { Set(new JSONNumber(0)); return 0; } + set { Set(new JSONNumber(value)); } + } + + public override float AsFloat + { + get { Set(new JSONNumber(0.0f)); return 0.0f; } + set { Set(new JSONNumber(value)); } + } + + public override double AsDouble + { + get { Set(new JSONNumber(0.0)); return 0.0; } + set { Set(new JSONNumber(value)); } + } + + public override long AsLong + { + get + { + if (longAsString) + Set(new JSONString("0")); + else + Set(new JSONNumber(0.0)); + return 0L; + } + set + { + if (longAsString) + Set(new JSONString(value.ToString())); + else + Set(new JSONNumber(value)); + } + } + + public override bool AsBool + { + get { Set(new JSONBool(false)); return false; } + set { Set(new JSONBool(value)); } + } + + public override JSONArray AsArray + { + get { return Set(new JSONArray()); } + } + + public override JSONObject AsObject + { + get { return Set(new JSONObject()); } + } + + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) + { + aSB.Append(TOKEN_NULL); + } + } + // End of JSONLazyCreator + + public static partial class JSON + { + public static JSONNode Parse(string aJSON) + { + return JSONNode.Parse(aJSON); + } + + public static bool TryParse(string aJSON, out JSONNode aResult) + { + try + { + aResult = JSON.Parse(aJSON); + return true; + } + catch (Exception e) + { #if UNITY_5_3_OR_NEWER - UnityEngine.Debug.LogException(e); + UnityEngine.Debug.LogException(e); #endif - aResult = null; - return false; - } - } - } + aResult = null; + return false; + } + } + } } diff --git a/Runtime/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs index 17589cb..0e9fdfb 100644 --- a/Runtime/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -204,10 +204,10 @@ public JSONNode WriteVector2(Vector2 aVec, string aXName = "x", string aYName = { Inline = true; - for (int i=0; i < 2; i++) - { + for (int i = 0; i < 2; i++) + { Add(aVec[i]); - } + } } return this; } @@ -438,29 +438,29 @@ public JSONNode WriteMatrix(Matrix4x4 aMatrix) public Color ReadColor() { if (IsString && ColorUtility.TryParseHtmlString(Value, out Color htmlColor)) - { + { return htmlColor; - } - + } + if (IsArray) { return ReadVector4(); } if (IsObject) - { + { return new Color(this["r"].AsFloat, this["g"].AsFloat, this["b"].AsFloat, this["a"].AsFloat); - } + } return Color.white; } public JSONNode WriteColor(Color aColor) - { + { if (IsString) - { - Value = $"#{ColorUtility.ToHtmlStringRGBA(aColor)}"; - } + { + Value = $"#{ColorUtility.ToHtmlStringRGBA(aColor)}"; + } else if (IsObject) { Inline = true; @@ -471,12 +471,12 @@ public JSONNode WriteColor(Color aColor) } else if (IsArray) - { + { WriteVector4(aColor); } return this; - } + } #endregion Color #region Color32 diff --git a/Tests/Editor/SimpleJSONUnityTests.cs b/Tests/Editor/SimpleJSONUnityTests.cs index 54d7a04..829584d 100644 --- a/Tests/Editor/SimpleJSONUnityTests.cs +++ b/Tests/Editor/SimpleJSONUnityTests.cs @@ -126,25 +126,25 @@ public void RectOffsetTest() var deserializedObject = JSON.Parse(jsonObjectString).ReadRectOffset(); var deserializedArray = JSON.Parse(jsonArrayString).ReadRectOffset(); - Assert.AreEqual(rectOffset.left, deserializedObject.left); - Assert.AreEqual(rectOffset.right, deserializedObject.right); - Assert.AreEqual(rectOffset.top, deserializedObject.top); - Assert.AreEqual(rectOffset.bottom, deserializedObject.bottom); - - Assert.AreEqual(rectOffset.left, deserializedArray.left); - Assert.AreEqual(rectOffset.right, deserializedArray.right); - Assert.AreEqual(rectOffset.top, deserializedArray.top); - Assert.AreEqual(rectOffset.bottom, deserializedArray.bottom); - } + Assert.AreEqual(rectOffset.left, deserializedObject.left); + Assert.AreEqual(rectOffset.right, deserializedObject.right); + Assert.AreEqual(rectOffset.top, deserializedObject.top); + Assert.AreEqual(rectOffset.bottom, deserializedObject.bottom); + + Assert.AreEqual(rectOffset.left, deserializedArray.left); + Assert.AreEqual(rectOffset.right, deserializedArray.right); + Assert.AreEqual(rectOffset.top, deserializedArray.top); + Assert.AreEqual(rectOffset.bottom, deserializedArray.bottom); + } [Test] public void MatrixTest() { Matrix4x4 matrix = new Matrix4x4(); - for (int i=0; i < 16; i++) - { + for (int i = 0; i < 16; i++) + { matrix[i] = Random.Range(-1.0f, 1.0f); - } + } var jsonArray = new JSONArray().WriteMatrix(matrix); @@ -159,21 +159,21 @@ public void MatrixTest() [Test] public void ColorTest() - { + { Color color = new Color(Random.value, Random.value, Random.value, Random.value); - var jsonObject = new JSONObject().WriteColor(color); - var jsonArray = new JSONArray().WriteColor(color); + var jsonObject = new JSONObject().WriteColor(color); + var jsonArray = new JSONArray().WriteColor(color); var jsonString = new JSONString().WriteColor(color); var jsonObjectString = jsonObject.ToString(); - var jsonArrayString = jsonArray.ToString(); + var jsonArrayString = jsonArray.ToString(); var jsonStringString = jsonString.ToString(); - Debug.Log($"{color.GetType().Name} object: {jsonObjectString} array: {jsonArrayString} string: {jsonStringString}"); + Debug.Log($"{color.GetType().Name} object: {jsonObjectString} array: {jsonArrayString} string: {jsonStringString}"); - var deserializedObject = JSON.Parse(jsonObjectString).ReadColor(); - var deserializedArray = JSON.Parse(jsonArrayString).ReadColor(); + var deserializedObject = JSON.Parse(jsonObjectString).ReadColor(); + var deserializedArray = JSON.Parse(jsonArrayString).ReadColor(); Assert.AreEqual(color, deserializedObject); Assert.AreEqual(color, deserializedArray); @@ -201,6 +201,6 @@ public void Color32Test() Assert.AreEqual(color32, deserializedObject); Assert.AreEqual(color32, deserializedArray); Assert.AreEqual(color32, deserializedString); - } + } } } \ No newline at end of file From 95f8812ca8838681e35f8ef14840395a8604cf5d Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Wed, 9 Sep 2020 21:00:10 -0700 Subject: [PATCH 09/23] add basic editorconfig. remove unused usings. --- .editorconfig | 22 ++++++++++++++++++++++ Runtime/SimpleJSON.cs | 20 +++++++------------- Tests/Editor/SimpleJSONTests.cs | 4 +--- Tests/Editor/SimpleJSONUnityTests.cs | 5 +---- 4 files changed, 31 insertions(+), 20 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b98f5d9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# see http://editorconfig.org/ for docs on this file + +[{*.cs,*.json}] +charset=utf-8 +end_of_line=lf +indent_size=4 +indent_style=space +insert_final_newline=true +trim_trailing_whitespace=true + +# Microsoft .NET properties +dotnet_sort_system_directives_first = true +csharp_new_line_before_catch=true +csharp_new_line_before_else=true +csharp_new_line_before_finally=true +csharp_new_line_before_members_in_object_initializers=false +csharp_new_line_before_open_brace = accessors, anonymous_methods, anonymous_types, control_blocks, events, indexers, lambdas, local_functions, methods, object_collection_array_initializers, properties, types +csharp_space_after_cast=false + +[{*.asmdef,*.meta}] +indent_size=2 +indent_style=space diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index 4a1d2d3..600e17d 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -283,8 +283,7 @@ public virtual double AsDouble { get { - double v = 0.0; - if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) + if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double v)) return v; return 0.0; } @@ -316,8 +315,7 @@ public virtual bool AsBool { get { - bool v = false; - if (bool.TryParse(Value, out v)) + if (bool.TryParse(Value, out bool v)) return v; return !string.IsNullOrEmpty(Value); } @@ -331,8 +329,7 @@ public virtual long AsLong { get { - long val = 0; - if (long.TryParse(Value, out val)) + if (long.TryParse(Value, out long val)) return val; return 0L; } @@ -996,8 +993,7 @@ public override bool HasKey(string aKey) public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) { - JSONNode res; - if (m_Dict.TryGetValue(aKey, out res)) + if (m_Dict.TryGetValue(aKey, out JSONNode res)) return res; return aDefault; } @@ -1170,8 +1166,7 @@ public override string Value get { return m_Data.ToString("R", CultureInfo.InvariantCulture); } protected set { - double v; - if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) + if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out double v)) m_Data = v; } } @@ -1253,8 +1248,7 @@ public override string Value get { return m_Data.ToString(); } protected set { - bool v; - if (bool.TryParse(value, out v)) + if (bool.TryParse(value, out bool v)) m_Data = v; } } @@ -1309,7 +1303,7 @@ public override int GetHashCode() public partial class JSONNull : JSONNode { - static JSONNull m_StaticInstance = new JSONNull(); + private static readonly JSONNull m_StaticInstance = new JSONNull(); public static bool reuseSameInstance = true; public static JSONNull CreateOrGet() { diff --git a/Tests/Editor/SimpleJSONTests.cs b/Tests/Editor/SimpleJSONTests.cs index 325e82e..1eae229 100644 --- a/Tests/Editor/SimpleJSONTests.cs +++ b/Tests/Editor/SimpleJSONTests.cs @@ -1,9 +1,7 @@ -using System.Collections; using System.Collections.Generic; using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; using SimpleJSON; +using UnityEngine; namespace Tests { diff --git a/Tests/Editor/SimpleJSONUnityTests.cs b/Tests/Editor/SimpleJSONUnityTests.cs index 829584d..cf0282e 100644 --- a/Tests/Editor/SimpleJSONUnityTests.cs +++ b/Tests/Editor/SimpleJSONUnityTests.cs @@ -1,9 +1,6 @@ -using System.Collections; -using System.Collections.Generic; using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; using SimpleJSON; +using UnityEngine; namespace Tests { From bff12395c1e715a742eb05f3237e7658d37eb528 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Wed, 9 Sep 2020 21:09:30 -0700 Subject: [PATCH 10/23] update assembly definition names. --- Runtime/Unity.SimpleJSON.asmdef | 15 ++++++++++++--- Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Runtime/Unity.SimpleJSON.asmdef b/Runtime/Unity.SimpleJSON.asmdef index 70914bb..3d889a1 100644 --- a/Runtime/Unity.SimpleJSON.asmdef +++ b/Runtime/Unity.SimpleJSON.asmdef @@ -1,3 +1,12 @@ -{ - "name": "SimpleJSON" -} +{ + "name": "Unity.SimpleJSON", + "references": [], + "optionalUnityReferences": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +} \ No newline at end of file diff --git a/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef b/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef index e1c7cd0..f85242d 100644 --- a/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef +++ b/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef @@ -1,7 +1,7 @@ { - "name": "SimpleJSON Tests", + "name": "Unity.SimpleJSON.Editor.Tests", "references": [ - "SimpleJSON" + "Unity.SimpleJSON" ], "optionalUnityReferences": [ "TestAssemblies" From 2b302ba3590af502868eb8e3521e191855df0f15 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Wed, 30 Sep 2020 19:52:19 -0700 Subject: [PATCH 11/23] Add virtual Clear method to base JSONNode. Clear nodes when writing Unity types. --- Runtime/SimpleJSON.cs | 14 ++++++++++++-- Runtime/SimpleJSONUnity.cs | 20 ++++++++++++++++++-- Tests/Editor/SimpleJSONUnityTests.cs | 3 +++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index 600e17d..c34d571 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -221,6 +221,11 @@ public virtual JSONNode Remove(JSONNode aNode) throw new NotImplementedException(); } + public virtual void Clear() + { + throw new NotImplementedException(); + } + public virtual JSONNode Clone() { throw new NotImplementedException(); @@ -843,7 +848,7 @@ public void RemoveAt(int index) m_List.RemoveAt(index); } - public void Clear() + public override void Clear() { m_List.Clear(); } @@ -1054,7 +1059,7 @@ public void Add(KeyValuePair item) ((IDictionary)m_Dict).Add(item); } - public void Clear() + public override void Clear() { m_Dict.Clear(); } @@ -1115,6 +1120,11 @@ public JSONString(string aData) m_Data = aData; } + public override void Clear() + { + Value = null; + } + public override JSONNode Clone() { return new JSONString(m_Data); diff --git a/Runtime/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs index 0e9fdfb..606b008 100644 --- a/Runtime/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -194,6 +194,8 @@ public Vector2 ReadVector2() public JSONNode WriteVector2(Vector2 aVec, string aXName = "x", string aYName = "y") { + Clear(); + if (IsObject) { Inline = true; @@ -237,6 +239,8 @@ public Vector3 ReadVector3() public JSONNode WriteVector3(Vector3 aVec, string aXName = "x", string aYName = "y", string aZName = "z") { + Clear(); + if (IsObject) { Inline = true; @@ -273,6 +277,8 @@ public Vector4 ReadVector4() public JSONNode WriteVector4(Vector4 aVec) { + Clear(); + if (IsObject) { Inline = true; @@ -311,6 +317,8 @@ public Quaternion ReadQuaternion() public JSONNode WriteQuaternion(Quaternion aRot) { + Clear(); + if (IsObject) { Inline = true; @@ -349,6 +357,8 @@ public Rect ReadRect() public JSONNode WriteRect(Rect aRect) { + Clear(); + if (IsObject) { Inline = true; @@ -386,6 +396,8 @@ public RectOffset ReadRectOffset() public JSONNode WriteRectOffset(RectOffset aRect) { + Clear(); + if (IsObject) { Inline = true; @@ -422,6 +434,8 @@ public Matrix4x4 ReadMatrix() public JSONNode WriteMatrix(Matrix4x4 aMatrix) { + Clear(); + if (IsArray) { Inline = true; @@ -457,6 +471,8 @@ public Color ReadColor() public JSONNode WriteColor(Color aColor) { + Clear(); + if (IsString) { Value = $"#{ColorUtility.ToHtmlStringRGBA(aColor)}"; @@ -468,7 +484,6 @@ public JSONNode WriteColor(Color aColor) this["g"].AsFloat = aColor.g; this["b"].AsFloat = aColor.b; this["a"].AsFloat = aColor.a; - } else if (IsArray) { @@ -502,6 +517,8 @@ public Color32 ReadColor32() public JSONNode WriteColor32(Color32 aColor32) { + Clear(); + if (IsString) { Value = $"#{ColorUtility.ToHtmlStringRGBA(aColor32)}"; @@ -513,7 +530,6 @@ public JSONNode WriteColor32(Color32 aColor32) this["g"].AsByte = aColor32.g; this["b"].AsByte = aColor32.b; this["a"].AsByte = aColor32.a; - } else if (IsArray) { diff --git a/Tests/Editor/SimpleJSONUnityTests.cs b/Tests/Editor/SimpleJSONUnityTests.cs index cf0282e..6701747 100644 --- a/Tests/Editor/SimpleJSONUnityTests.cs +++ b/Tests/Editor/SimpleJSONUnityTests.cs @@ -174,6 +174,7 @@ public void ColorTest() Assert.AreEqual(color, deserializedObject); Assert.AreEqual(color, deserializedArray); + Assert.AreEqual(deserializedObject, deserializedArray); } [Test] @@ -198,6 +199,8 @@ public void Color32Test() Assert.AreEqual(color32, deserializedObject); Assert.AreEqual(color32, deserializedArray); Assert.AreEqual(color32, deserializedString); + Assert.AreEqual(deserializedObject, deserializedArray); + Assert.AreEqual(deserializedString, deserializedArray); } } } \ No newline at end of file From 77b9366a5432789e022782bd2cf5ef824b10ee41 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Wed, 30 Sep 2020 21:33:14 -0700 Subject: [PATCH 12/23] Add DotNetTypes and ulong operators from upstream Bunny83/master --- Runtime/SimpleJSON.cs | 94 +++-- Runtime/SimpleJSONDotNetTypes.cs | 527 ++++++++++++++++++++++++++ Runtime/SimpleJSONDotNetTypes.cs.meta | 11 + Tests/Editor/SimpleJSONTests.cs | 12 + 4 files changed, 620 insertions(+), 24 deletions(-) create mode 100644 Runtime/SimpleJSONDotNetTypes.cs create mode 100644 Runtime/SimpleJSONDotNetTypes.cs.meta diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index c34d571..c816120 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -298,12 +298,6 @@ public virtual double AsDouble } } - public virtual byte AsByte - { - get { return (byte)AsDouble; } - set { AsDouble = value; } - } - public virtual int AsInt { get { return (int)AsDouble; } @@ -344,6 +338,21 @@ public virtual long AsLong } } + public virtual ulong AsULong + { + get + { + ulong val = 0; + if (ulong.TryParse(Value, out val)) + return val; + return 0; + } + set + { + Value = value.ToString(); + } + } + public virtual JSONArray AsArray { get @@ -399,11 +408,6 @@ public static implicit operator JSONNode(int n) return new JSONNumber(n); } - public static implicit operator byte(JSONNode d) - { - return (d == null) ? (byte)0 : d.AsByte; - } - public static implicit operator int(JSONNode d) { return (d == null) ? 0 : d.AsInt; @@ -421,6 +425,18 @@ public static implicit operator long(JSONNode d) return (d == null) ? 0L : d.AsLong; } + public static implicit operator JSONNode(ulong n) + { + if (longAsString) + return new JSONString(n.ToString()); + return new JSONNumber(n); + } + + public static implicit operator ulong(JSONNode d) + { + return (d == null) ? 0 : d.AsULong; + } + public static implicit operator JSONNode(bool b) { return new JSONBool(b); @@ -1193,6 +1209,12 @@ public override long AsLong set { m_Data = value; } } + public override ulong AsULong + { + get { return (ulong)m_Data; } + set { m_Data = value; } + } + public JSONNumber(double aData) { m_Data = aData; @@ -1213,14 +1235,25 @@ internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aSB.Append(Value); } - internal static bool IsNumeric(object value) + public static bool IsNumeric(object value) { - return value is int || value is uint - || value is float || value is double - || value is decimal - || value is long || value is ulong - || value is short || value is ushort - || value is sbyte || value is byte; + switch (value) + { + case byte _: + case decimal _: + case double _: + case float _: + case int _: + case long _: + case sbyte _: + case short _: + case uint _: + case ulong _: + case ushort _: + return true; + default: + return false; + } } public override bool Equals(object obj) @@ -1441,12 +1474,6 @@ public override int GetHashCode() return 0; } - public override byte AsByte - { - get { Set(new JSONNumber(0)); return (byte)0; } - set { Set(new JSONNumber(value)); } - } - public override int AsInt { get { Set(new JSONNumber(0)); return 0; } @@ -1484,6 +1511,25 @@ public override long AsLong } } + public override ulong AsULong + { + get + { + if (longAsString) + Set(new JSONString("0")); + else + Set(new JSONNumber(0.0)); + return 0L; + } + set + { + if (longAsString) + Set(new JSONString(value.ToString())); + else + Set(new JSONNumber(value)); + } + } + public override bool AsBool { get { Set(new JSONBool(false)); return false; } diff --git a/Runtime/SimpleJSONDotNetTypes.cs b/Runtime/SimpleJSONDotNetTypes.cs new file mode 100644 index 0000000..e4b8fde --- /dev/null +++ b/Runtime/SimpleJSONDotNetTypes.cs @@ -0,0 +1,527 @@ +#region License and information +/* * * * * + * + * Extension file for the SimpleJSON framework for better support of some common + * .NET types. It does only work together with the SimpleJSON.cs + * It provides direct conversion support for types like decimal, char, byte, + * sbyte, short, ushort, uint, DateTime, TimeSpan and Guid. In addition there + * are conversion helpers for converting an array of number values into a byte[] + * or a List as well as converting an array of string values into a string[] + * or List. + * Finally there are some additional type conversion operators for some nullable + * types like short?, int?, float?, double?, long? and bool?. They will actually + * assign a JSONNull value when it's null or a JSONNumber when it's not. + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Markus Göbel (Bunny83) + * + * 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 License and information + +namespace SimpleJSON +{ + using System.Globalization; + using System.Collections.Generic; + public partial class JSONNode + { + #region Decimal + public virtual decimal AsDecimal + { + get + { + decimal result; + if (!decimal.TryParse(Value, out result)) + result = 0; + return result; + } + set + { + Value = value.ToString(); + } + } + + public static implicit operator JSONNode(decimal aDecimal) + { + return new JSONString(aDecimal.ToString()); + } + + public static implicit operator decimal(JSONNode aNode) + { + return aNode.AsDecimal; + } + #endregion Decimal + + #region Char + public virtual char AsChar + { + get + { + if (IsString && Value.Length > 0) + return Value[0]; + if (IsNumber) + return (char)AsInt; + return '\0'; + } + set + { + if (IsString) + Value = value.ToString(); + else if (IsNumber) + AsInt = (int)value; + } + } + + public static implicit operator JSONNode(char aChar) + { + return new JSONString(aChar.ToString()); + } + + public static implicit operator char(JSONNode aNode) + { + return aNode.AsChar; + } + #endregion Char + + #region UInt + public virtual uint AsUInt + { + get + { + return (uint)AsDouble; + } + set + { + AsDouble = value; + } + } + + public static implicit operator JSONNode(uint aUInt) + { + return new JSONNumber(aUInt); + } + + public static implicit operator uint(JSONNode aNode) + { + return aNode.AsUInt; + } + #endregion UInt + + #region Byte + public virtual byte AsByte + { + get + { + return (byte)AsInt; + } + set + { + AsInt = value; + } + } + + public static implicit operator JSONNode(byte aByte) + { + return new JSONNumber(aByte); + } + + public static implicit operator byte(JSONNode aNode) + { + return aNode.AsByte; + } + #endregion Byte + + #region SByte + public virtual sbyte AsSByte + { + get + { + return (sbyte)AsInt; + } + set + { + AsInt = value; + } + } + + public static implicit operator JSONNode(sbyte aSByte) + { + return new JSONNumber(aSByte); + } + + public static implicit operator sbyte(JSONNode aNode) + { + return aNode.AsSByte; + } + #endregion SByte + + #region Short + public virtual short AsShort + { + get + { + return (short)AsInt; + } + set + { + AsInt = value; + } + } + + public static implicit operator JSONNode(short aShort) + { + return new JSONNumber(aShort); + } + + public static implicit operator short(JSONNode aNode) + { + return aNode.AsShort; + } + #endregion Short + + #region UShort + public virtual ushort AsUShort + { + get + { + return (ushort)AsInt; + } + set + { + AsInt = value; + } + } + + public static implicit operator JSONNode(ushort aUShort) + { + return new JSONNumber(aUShort); + } + + public static implicit operator ushort(JSONNode aNode) + { + return aNode.AsUShort; + } + #endregion UShort + + #region DateTime + public virtual System.DateTime AsDateTime + { + get + { + System.DateTime result; + if (!System.DateTime.TryParse(Value, CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) + result = new System.DateTime(0); + return result; + } + set + { + Value = value.ToString(CultureInfo.InvariantCulture); + } + } + + public static implicit operator JSONNode(System.DateTime aDateTime) + { + return new JSONString(aDateTime.ToString(CultureInfo.InvariantCulture)); + } + + public static implicit operator System.DateTime(JSONNode aNode) + { + return aNode.AsDateTime; + } + #endregion DateTime + + #region TimeSpan + public virtual System.TimeSpan AsTimeSpan + { + get + { + System.TimeSpan result; + if (!System.TimeSpan.TryParse(Value, CultureInfo.InvariantCulture, out result)) + result = new System.TimeSpan(0); + return result; + } + set + { + Value = value.ToString(); + } + } + + public static implicit operator JSONNode(System.TimeSpan aTimeSpan) + { + return new JSONString(aTimeSpan.ToString()); + } + + public static implicit operator System.TimeSpan(JSONNode aNode) + { + return aNode.AsTimeSpan; + } + #endregion TimeSpan + + #region Guid + public virtual System.Guid AsGuid + { + get + { + System.Guid result; + System.Guid.TryParse(Value, out result); + return result; + } + set + { + Value = value.ToString(); + } + } + + public static implicit operator JSONNode(System.Guid aGuid) + { + return new JSONString(aGuid.ToString()); + } + + public static implicit operator System.Guid(JSONNode aNode) + { + return aNode.AsGuid; + } + #endregion Guid + + #region ByteArray + public virtual byte[] AsByteArray + { + get + { + if (this.IsNull || !this.IsArray) + return null; + int count = Count; + byte[] result = new byte[count]; + for (int i = 0; i < count; i++) + result[i] = this[i].AsByte; + return result; + } + set + { + if (!IsArray || value == null) + return; + Clear(); + for (int i = 0; i < value.Length; i++) + Add(value[i]); + } + } + + public static implicit operator JSONNode(byte[] aByteArray) + { + return new JSONArray { AsByteArray = aByteArray }; + } + + public static implicit operator byte[](JSONNode aNode) + { + return aNode.AsByteArray; + } + #endregion ByteArray + + #region ByteList + public virtual List AsByteList + { + get + { + if (this.IsNull || !this.IsArray) + return null; + int count = Count; + List result = new List(count); + for (int i = 0; i < count; i++) + result.Add(this[i].AsByte); + return result; + } + set + { + if (!IsArray || value == null) + return; + Clear(); + for (int i = 0; i < value.Count; i++) + Add(value[i]); + } + } + + public static implicit operator JSONNode(List aByteList) + { + return new JSONArray { AsByteList = aByteList }; + } + + public static implicit operator List(JSONNode aNode) + { + return aNode.AsByteList; + } + #endregion ByteList + + #region StringArray + public virtual string[] AsStringArray + { + get + { + if (this.IsNull || !this.IsArray) + return null; + int count = Count; + string[] result = new string[count]; + for (int i = 0; i < count; i++) + result[i] = this[i].Value; + return result; + } + set + { + if (!IsArray || value == null) + return; + Clear(); + for (int i = 0; i < value.Length; i++) + Add(value[i]); + } + } + + public static implicit operator JSONNode(string[] aStringArray) + { + return new JSONArray { AsStringArray = aStringArray }; + } + + public static implicit operator string[](JSONNode aNode) + { + return aNode.AsStringArray; + } + #endregion StringArray + + #region StringList + public virtual List AsStringList + { + get + { + if (this.IsNull || !this.IsArray) + return null; + int count = Count; + List result = new List(count); + for (int i = 0; i < count; i++) + result.Add(this[i].Value); + return result; + } + set + { + if (!IsArray || value == null) + return; + Clear(); + for (int i = 0; i < value.Count; i++) + Add(value[i]); + } + } + + public static implicit operator JSONNode(List aStringList) + { + return new JSONArray { AsStringList = aStringList }; + } + + public static implicit operator List(JSONNode aNode) + { + return aNode.AsStringList; + } + #endregion StringList + + #region NullableTypes + public static implicit operator JSONNode(int? aValue) + { + if (aValue == null) + return JSONNull.CreateOrGet(); + return new JSONNumber((int)aValue); + } + + public static implicit operator int?(JSONNode aNode) + { + if (aNode == null || aNode.IsNull) + return null; + return aNode.AsInt; + } + + public static implicit operator JSONNode(float? aValue) + { + if (aValue == null) + return JSONNull.CreateOrGet(); + return new JSONNumber((float)aValue); + } + + public static implicit operator float?(JSONNode aNode) + { + if (aNode == null || aNode.IsNull) + return null; + return aNode.AsFloat; + } + + public static implicit operator JSONNode(double? aValue) + { + if (aValue == null) + return JSONNull.CreateOrGet(); + return new JSONNumber((double)aValue); + } + + public static implicit operator double?(JSONNode aNode) + { + if (aNode == null || aNode.IsNull) + return null; + return aNode.AsDouble; + } + + public static implicit operator JSONNode(bool? aValue) + { + if (aValue == null) + return JSONNull.CreateOrGet(); + return new JSONBool((bool)aValue); + } + + public static implicit operator bool?(JSONNode aNode) + { + if (aNode == null || aNode.IsNull) + return null; + return aNode.AsBool; + } + + public static implicit operator JSONNode(long? aValue) + { + if (aValue == null) + return JSONNull.CreateOrGet(); + return new JSONNumber((long)aValue); + } + + public static implicit operator long?(JSONNode aNode) + { + if (aNode == null || aNode.IsNull) + return null; + return aNode.AsLong; + } + + public static implicit operator JSONNode(short? aValue) + { + if (aValue == null) + return JSONNull.CreateOrGet(); + return new JSONNumber((short)aValue); + } + + public static implicit operator short?(JSONNode aNode) + { + if (aNode == null || aNode.IsNull) + return null; + return aNode.AsShort; + } + #endregion NullableTypes + } +} diff --git a/Runtime/SimpleJSONDotNetTypes.cs.meta b/Runtime/SimpleJSONDotNetTypes.cs.meta new file mode 100644 index 0000000..12e82b9 --- /dev/null +++ b/Runtime/SimpleJSONDotNetTypes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b57839459615c4f9983a5ad1097f3c0b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/SimpleJSONTests.cs b/Tests/Editor/SimpleJSONTests.cs index 1eae229..b5cfcb9 100644 --- a/Tests/Editor/SimpleJSONTests.cs +++ b/Tests/Editor/SimpleJSONTests.cs @@ -352,5 +352,17 @@ public void SerializeTest() Debug.LogFormat("Serialized result:\n{0}", jsonString); } + + [Test] + public void NumericTest() + { + Assert.True(JSONNumber.IsNumeric(0)); + Assert.True(JSONNumber.IsNumeric(1L)); + Assert.True(JSONNumber.IsNumeric(2.0f)); + Assert.True(JSONNumber.IsNumeric(3.0)); + + Assert.False(JSONNumber.IsNumeric('2')); + Assert.False(JSONNumber.IsNumeric("two")); + } } } From 93c1bee9ab894e8cd67ed40402d97dd81cfd371d Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Thu, 1 Oct 2020 19:55:42 -0700 Subject: [PATCH 13/23] Only serialize long and ulong as strings. Throw an exception if user tries to assign a large long/ulong to a JSONNumber. Tests for serializing/deserializing large numbers. --- Runtime/SimpleJSON.cs | 55 +++++++++++++++------------- Runtime/SimpleJSONSerializer.cs | 4 +++ Tests/Editor/SimpleJSONTests.cs | 64 +++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 25 deletions(-) diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index c816120..7545f4d 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -52,6 +52,7 @@ public enum JSONNodeType None = 7, Custom = 0xFF, } + public enum JSONTextMode { Compact, @@ -175,7 +176,6 @@ IEnumerator IEnumerable.GetEnumerator() #region common interface public static bool forceASCII = false; // Use Unicode by default - public static bool longAsString = false; // lazy creator creates a JSONString instead of JSONNumber public static bool allowLineComments = true; // allow "//"-style comments at the end of a line public abstract JSONNodeType Tag { get; } @@ -415,9 +415,7 @@ public static implicit operator int(JSONNode d) public static implicit operator JSONNode(long n) { - if (longAsString) - return new JSONString(n.ToString()); - return new JSONNumber(n); + return new JSONString(n.ToString()); } public static implicit operator long(JSONNode d) @@ -427,9 +425,7 @@ public static implicit operator long(JSONNode d) public static implicit operator JSONNode(ulong n) { - if (longAsString) - return new JSONString(n.ToString()); - return new JSONNumber(n); + return new JSONString(n.ToString()); } public static implicit operator ulong(JSONNode d) @@ -1181,6 +1177,9 @@ public override int GetHashCode() public partial class JSONNumber : JSONNode { + private const long MAX_SAFE_INTEGER = (long)1 << 53; + private const long MIN_SAFE_INTEGER = -(long)1 << 53; + private double m_Data; public override JSONNodeType Tag { get { return JSONNodeType.Number; } } @@ -1193,7 +1192,9 @@ public override string Value protected set { if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out double v)) + { m_Data = v; + } } } @@ -1206,13 +1207,29 @@ public override double AsDouble public override long AsLong { get { return (long)m_Data; } - set { m_Data = value; } + set + { + if (value > MAX_SAFE_INTEGER || value < MIN_SAFE_INTEGER) + { + throw new ArgumentException("JSONumber cannot store an INT64 this large without losing precision"); + } + + m_Data = value; + } } public override ulong AsULong { get { return (ulong)m_Data; } - set { m_Data = value; } + set + { + if (value > MAX_SAFE_INTEGER) + { + throw new ArgumentException("JSONumber cannot store an INT64 this large without losing precision"); + } + + m_Data = value; + } } public JSONNumber(double aData) @@ -1496,18 +1513,12 @@ public override long AsLong { get { - if (longAsString) - Set(new JSONString("0")); - else - Set(new JSONNumber(0.0)); + Set(new JSONString("0")); return 0L; } set { - if (longAsString) - Set(new JSONString(value.ToString())); - else - Set(new JSONNumber(value)); + Set(new JSONString(value.ToString())); } } @@ -1515,18 +1526,12 @@ public override ulong AsULong { get { - if (longAsString) - Set(new JSONString("0")); - else - Set(new JSONNumber(0.0)); + Set(new JSONString("0")); return 0L; } set { - if (longAsString) - Set(new JSONString(value.ToString())); - else - Set(new JSONNumber(value)); + Set(new JSONString(value.ToString())); } } diff --git a/Runtime/SimpleJSONSerializer.cs b/Runtime/SimpleJSONSerializer.cs index b059931..dc14732 100644 --- a/Runtime/SimpleJSONSerializer.cs +++ b/Runtime/SimpleJSONSerializer.cs @@ -57,6 +57,10 @@ public static JSONNode ToJSONNode(object value) return color32Value; #endif + case long _: + case ulong _: + case decimal _: + return new JSONString(value.ToString()); default: if (JSONNumber.IsNumeric(value)) { diff --git a/Tests/Editor/SimpleJSONTests.cs b/Tests/Editor/SimpleJSONTests.cs index b5cfcb9..68c3fb3 100644 --- a/Tests/Editor/SimpleJSONTests.cs +++ b/Tests/Editor/SimpleJSONTests.cs @@ -7,6 +7,9 @@ namespace Tests { public class SimpleJSONTests { + private const long MAX_SAFE_INTEGER = (long)1 << 53; + private const long MIN_SAFE_INTEGER = -(long)1 << 53; + private const string jsonString = "{ \"array\": [1.44,2,3], " + "\"object\": {\"key1\":\"value1\", \"key2\":256}, " + "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " + @@ -364,5 +367,66 @@ public void NumericTest() Assert.False(JSONNumber.IsNumeric('2')); Assert.False(JSONNumber.IsNumeric("two")); } + + [Test] + public void MinMaxTest() + { + Debug.Log($"max safe int64: {MAX_SAFE_INTEGER} min safe int64: {MIN_SAFE_INTEGER}"); + + var jsonObject = new JSONObject(); + jsonObject["maxLong"] = long.MaxValue; + jsonObject["minLong"] = long.MinValue; + jsonObject["maxULong"] = ulong.MaxValue; + jsonObject["minULong"] = ulong.MinValue; + jsonObject["maxDecimal"] = decimal.MaxValue; + jsonObject["minDecimal"] = decimal.MinValue; + + jsonObject["maxSafeInt"] = MAX_SAFE_INTEGER; + jsonObject["maxSafeInt+1"] = MAX_SAFE_INTEGER + 1; + + jsonObject["minSafeInt"] = MIN_SAFE_INTEGER; + jsonObject["minSafeInt-1"] = MIN_SAFE_INTEGER - 1; + + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log($"minmax values: {jsonObjectString}"); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(deserializedObject["maxLong"].AsLong, long.MaxValue); + Assert.AreEqual(deserializedObject["minLong"].AsLong, long.MinValue); + Assert.AreEqual(deserializedObject["maxULong"].AsULong, ulong.MaxValue); + Assert.AreEqual(deserializedObject["minULong"].AsULong, ulong.MinValue); + Assert.AreEqual(deserializedObject["maxDecimal"].AsDecimal, decimal.MaxValue); + Assert.AreEqual(deserializedObject["minDecimal"].AsDecimal, decimal.MinValue); + + Assert.AreNotEqual(jsonObject["maxSafeInt"].AsLong, jsonObject["maxSafeInt+1"].AsLong); + Assert.AreEqual(jsonObject["maxSafeInt"].AsDouble, jsonObject["maxSafeInt+1"].AsDouble); + + Assert.AreNotEqual(jsonObject["minSafeInt"].AsLong, jsonObject["minSafeInt-1"].AsLong); + Assert.AreEqual(jsonObject["minSafeInt"].AsDouble, jsonObject["minSafeInt-1"].AsDouble); + + var bigInt64 = new JSONNumber(0); + var smallInt64 = new JSONNumber(0); + + bigInt64.AsULong = MAX_SAFE_INTEGER; + smallInt64.AsLong = MIN_SAFE_INTEGER; + + Assert.Throws(() => + { + bigInt64.AsLong = (long)(MAX_SAFE_INTEGER + 1); + }); + + Assert.Throws(() => + { + smallInt64.AsLong = (long)(MIN_SAFE_INTEGER - 1); + }); + + Assert.Throws(() => + { + bigInt64.AsULong = (long)(MAX_SAFE_INTEGER + 1); + }); + } } } From 8c970ecdb53f3225acae6ef14566e838f43c9d61 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Thu, 1 Oct 2020 21:05:16 -0700 Subject: [PATCH 14/23] Remove enumerator code. --- Runtime/SimpleJSON.cs | 188 +++++++++----------------------- Tests/Editor/SimpleJSONTests.cs | 31 +++++- 2 files changed, 78 insertions(+), 141 deletions(-) diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index 7545f4d..98fc962 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -65,114 +65,6 @@ public abstract partial class JSONNode protected const string TOKEN_TRUE = "true"; protected const string TOKEN_FALSE = "false"; - #region Enumerators - public struct Enumerator - { - private enum Type { None, Array, Object } - private Type type; - private Dictionary.Enumerator m_Object; - private List.Enumerator m_Array; - public bool IsValid { get { return type != Type.None; } } - - public Enumerator(List.Enumerator aArrayEnum) - { - type = Type.Array; - m_Object = default(Dictionary.Enumerator); - m_Array = aArrayEnum; - } - - public Enumerator(Dictionary.Enumerator aDictEnum) - { - type = Type.Object; - m_Object = aDictEnum; - m_Array = default(List.Enumerator); - } - - public KeyValuePair Current - { - get - { - if (type == Type.Array) - return new KeyValuePair(string.Empty, m_Array.Current); - else if (type == Type.Object) - return m_Object.Current; - return new KeyValuePair(string.Empty, null); - } - } - - public bool MoveNext() - { - if (type == Type.Array) - return m_Array.MoveNext(); - else if (type == Type.Object) - return m_Object.MoveNext(); - return false; - } - } - - public struct ValueEnumerator - { - private Enumerator m_Enumerator; - public ValueEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } - public ValueEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } - public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } - public JSONNode Current { get { return m_Enumerator.Current.Value; } } - public bool MoveNext() { return m_Enumerator.MoveNext(); } - public ValueEnumerator GetEnumerator() { return this; } - } - - public struct KeyEnumerator - { - private Enumerator m_Enumerator; - public KeyEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } - public KeyEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } - public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } - public string Current { get { return m_Enumerator.Current.Key; } } - public bool MoveNext() { return m_Enumerator.MoveNext(); } - public KeyEnumerator GetEnumerator() { return this; } - } - - public class LinqEnumerator : IEnumerator>, IEnumerable> - { - private JSONNode m_Node; - private Enumerator m_Enumerator; - - internal LinqEnumerator(JSONNode aNode) - { - m_Node = aNode; - if (m_Node != null) - m_Enumerator = m_Node.GetEnumerator(); - } - - public KeyValuePair Current { get { return m_Enumerator.Current; } } - object IEnumerator.Current { get { return m_Enumerator.Current; } } - public bool MoveNext() { return m_Enumerator.MoveNext(); } - - public void Dispose() - { - m_Node = null; - m_Enumerator = new Enumerator(); - } - - public IEnumerator> GetEnumerator() - { - return new LinqEnumerator(m_Node); - } - - public void Reset() - { - if (m_Node != null) - m_Enumerator = m_Node.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return new LinqEnumerator(m_Node); - } - } - - #endregion Enumerators - #region common interface public static bool forceASCII = false; // Use Unicode by default @@ -239,16 +131,6 @@ public virtual IEnumerable Children } } - public IEnumerable DeepChildren - { - get - { - foreach (var C in Children) - foreach (var D in C.DeepChildren) - yield return D; - } - } - public virtual bool HasKey(string aKey) { return false; @@ -275,11 +157,6 @@ public virtual string ToString(int aIndent) internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); - public abstract Enumerator GetEnumerator(); - public IEnumerable> Linq { get { return new LinqEnumerator(this); } } - public KeyEnumerator Keys { get { return new KeyEnumerator(GetEnumerator()); } } - public ValueEnumerator Values { get { return new ValueEnumerator(GetEnumerator()); } } - #endregion common interface #region typecasting properties @@ -342,8 +219,7 @@ public virtual ulong AsULong { get { - ulong val = 0; - if (ulong.TryParse(Value, out val)) + if (ulong.TryParse(Value, out ulong val)) return val; return 0; } @@ -736,7 +612,6 @@ public override bool Inline public override JSONNodeType Tag { get { return JSONNodeType.Array; } } public override bool IsArray { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } public bool IsReadOnly => false; public override JSONNode this[int aIndex] @@ -744,22 +619,39 @@ public override JSONNode this[int aIndex] get { if (aIndex < 0 || aIndex >= m_List.Count) + { throw new IndexOutOfRangeException(); + } return m_List[aIndex]; } set { if (aIndex < 0 || aIndex >= m_List.Count) + { throw new IndexOutOfRangeException(); + } if (value == null) + { value = JSONNull.CreateOrGet(); + } m_List[aIndex] = value; } } + public override JSONArray AsArray + { + get => this; + } + + public int Capacity + { + get { return m_List.Capacity; } + set { m_List.Capacity = value; } + } + public override int Count { get { return m_List.Count; } @@ -773,7 +665,10 @@ public override void Add(string aKey, JSONNode aItem) } if (aItem == null) + { aItem = JSONNull.CreateOrGet(); + } + m_List.Add(aItem); } @@ -818,8 +713,10 @@ public override IEnumerable Children { get { - foreach (JSONNode N in m_List) - yield return N; + foreach (var node in m_List) + { + yield return node; + } } } @@ -907,8 +804,6 @@ public override bool Inline public override JSONNodeType Tag { get { return JSONNodeType.Object; } } public override bool IsObject { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); } - ICollection IDictionary.Keys { get @@ -917,6 +812,14 @@ ICollection IDictionary.Keys } } + public ICollection Keys + { + get + { + return m_Dict.Keys; + } + } + ICollection IDictionary.Values { get @@ -925,6 +828,14 @@ ICollection IDictionary.Values } } + public ICollection Values + { + get + { + return m_Dict.Values; + } + } + public bool IsReadOnly => false; public override JSONNode this[string aKey] @@ -947,6 +858,11 @@ public override JSONNode this[string aKey] } } + public override JSONObject AsObject + { + get => this; + } + public override int Count { get { return m_Dict.Count; } @@ -1019,8 +935,10 @@ public override IEnumerable Children { get { - foreach (KeyValuePair N in m_Dict) - yield return N.Value; + foreach (var node in m_Dict.Values) + { + yield return node; + } } } @@ -1111,8 +1029,6 @@ public partial class JSONString : JSONNode public override bool IsString { get { return true; } } public override bool IsNull { get { return m_Data == null; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } - public override string Value { get { return m_Data; } @@ -1184,7 +1100,6 @@ public partial class JSONNumber : JSONNode public override JSONNodeType Tag { get { return JSONNodeType.Number; } } public override bool IsNumber { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } public override string Value { @@ -1301,7 +1216,6 @@ public partial class JSONBool : JSONNode public override JSONNodeType Tag { get { return JSONNodeType.Boolean; } } public override bool IsBoolean { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } public override string Value { @@ -1376,7 +1290,6 @@ private JSONNull() { } public override JSONNodeType Tag { get { return JSONNodeType.NullValue; } } public override bool IsNull { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } public override string Value { @@ -1420,7 +1333,6 @@ internal partial class JSONLazyCreator : JSONNode private string m_Key = null; public override JSONNodeType Tag { get { return JSONNodeType.None; } } public override bool IsNull { get { return true; } } - public override Enumerator GetEnumerator() { return new Enumerator(); } public JSONLazyCreator(JSONNode aNode) { diff --git a/Tests/Editor/SimpleJSONTests.cs b/Tests/Editor/SimpleJSONTests.cs index 68c3fb3..a5dff35 100644 --- a/Tests/Editor/SimpleJSONTests.cs +++ b/Tests/Editor/SimpleJSONTests.cs @@ -46,7 +46,7 @@ public void SetUp() doubleArray[i] = jsonArray[i].AsDouble; } - var jsonObject = parsedJSON["object"]; + var jsonObject = parsedJSON["object"].AsObject; objectDictionary = new Dictionary(); @@ -68,7 +68,7 @@ public void SetUp() [Test] public void ArrayTest() { - var jsonArray = JSON.ToJSONNode(doubleArray); + var jsonArray = JSON.ToJSONNode(doubleArray).AsArray; for (int i = 0; i < jsonArray.Count; i++) { @@ -76,17 +76,42 @@ public void ArrayTest() Assert.AreEqual(jsonArray[i].AsDouble, doubleArray[i]); } + + var index = 0; + foreach (var node in jsonArray) + { + Assert.AreEqual(node, parsedJSON["array"][index]); + + Assert.AreEqual(node.AsDouble, doubleArray[index]); + index++; + } + + index = 0; + foreach (var node in jsonArray.Children) + { + Assert.AreEqual(node, parsedJSON["array"][index]); + + Assert.AreEqual(node.AsDouble, doubleArray[index]); + index++; + } } [Test] public void DictionaryTest() { - var jsonObject = JSON.ToJSONNode(objectDictionary); + var jsonObject = JSON.ToJSONNode(objectDictionary).AsObject; foreach (var key in jsonObject.Keys) { Assert.AreEqual(jsonObject[key], parsedJSON["object"][key]); } + + foreach (var kvp in jsonObject) + { + Assert.AreEqual(jsonObject[kvp.Key], parsedJSON["object"][kvp.Key]); + + Assert.AreEqual(kvp.Value, parsedJSON["object"][kvp.Key]); + } } [Test] From 2cfc6c671c3bdcd1a20aaf0f3c79f66ca92b5056 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Sat, 10 Oct 2020 22:36:38 -0700 Subject: [PATCH 15/23] Strict bracketing. Serialize/deserialize tests for DotNetTypes. Use round trip format for DateTime. --- Runtime/SimpleJSON.cs | 117 +++++- Runtime/SimpleJSONBinary.cs | 25 +- Runtime/SimpleJSONDotNetTypes.cs | 108 ++++- Runtime/SimpleJSONUnity.cs | 28 ++ Tests/Editor/SimpleJSONDotNetTests.cs | 464 +++++++++++++++++++++ Tests/Editor/SimpleJSONDotNetTests.cs.meta | 11 + 6 files changed, 702 insertions(+), 51 deletions(-) create mode 100644 Tests/Editor/SimpleJSONDotNetTests.cs create mode 100644 Tests/Editor/SimpleJSONDotNetTests.cs.meta diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index 98fc962..41ea915 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -166,7 +166,9 @@ public virtual double AsDouble get { if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double v)) + { return v; + } return 0.0; } set @@ -192,7 +194,9 @@ public virtual bool AsBool get { if (bool.TryParse(Value, out bool v)) + { return v; + } return !string.IsNullOrEmpty(Value); } set @@ -206,7 +210,9 @@ public virtual long AsLong get { if (long.TryParse(Value, out long val)) + { return val; + } return 0L; } set @@ -220,7 +226,9 @@ public virtual ulong AsULong get { if (ulong.TryParse(Value, out ulong val)) + { return val; + } return 0; } set @@ -327,11 +335,15 @@ public static implicit operator JSONNode(KeyValuePair aKeyValu public static bool operator ==(JSONNode a, object b) { if (ReferenceEquals(a, b)) + { return true; + } bool aIsNull = a is JSONNull || ReferenceEquals(a, null) || a is JSONLazyCreator; bool bIsNull = b is JSONNull || ReferenceEquals(b, null) || b is JSONLazyCreator; if (aIsNull && bIsNull) + { return true; + } return !aIsNull && a.Equals(b); } @@ -360,7 +372,9 @@ internal static StringBuilder EscapeBuilder get { if (m_EscapeBuilder == null) + { m_EscapeBuilder = new StringBuilder(); + } return m_EscapeBuilder; } } @@ -370,7 +384,9 @@ internal static string Escape(string aText) var sb = EscapeBuilder; sb.Length = 0; if (sb.Capacity < aText.Length + aText.Length / 10) + { sb.Capacity = aText.Length + aText.Length / 10; + } foreach (char c in aText) { switch (c) @@ -403,7 +419,9 @@ internal static string Escape(string aText) sb.Append("\\u").Append(val.ToString("X4")); } else + { sb.Append(c); + } break; } } @@ -415,17 +433,34 @@ internal static string Escape(string aText) private static JSONNode ParseElement(string token, bool quoted) { if (quoted) + { return token; - if (token.Equals(TOKEN_FALSE, StringComparison.InvariantCultureIgnoreCase) - || token.Equals(TOKEN_TRUE, StringComparison.InvariantCultureIgnoreCase)) - return token.Equals(TOKEN_TRUE, StringComparison.InvariantCultureIgnoreCase); - if (token.Equals(TOKEN_NULL, StringComparison.InvariantCultureIgnoreCase)) - return JSONNull.CreateOrGet(); + } + + if (token.Length <= 5) + { + if (token.Equals(TOKEN_FALSE, StringComparison.InvariantCultureIgnoreCase)) + { + return false; + } + if (token.Equals(TOKEN_TRUE, StringComparison.InvariantCultureIgnoreCase)) + { + return true; + } + if (token.Equals(TOKEN_NULL, StringComparison.InvariantCultureIgnoreCase)) + { + return JSONNull.CreateOrGet(); + } + } if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out double val)) + { return val; + } else + { return token; + } } public static JSONNode Parse(string aJSON) @@ -532,7 +567,9 @@ public static JSONNode Parse(string aJSON) case ' ': case '\t': if (QuoteMode) + { Token.Append(aJSON[i]); + } break; case '\\': @@ -558,14 +595,12 @@ public static JSONNode Parse(string aJSON) Token.Append('\f'); break; case 'u': - { - string s = aJSON.Substring(i + 1, 4); - Token.Append((char)int.Parse( - s, - System.Globalization.NumberStyles.AllowHexSpecifier)); - i += 4; - break; - } + string s = aJSON.Substring(i + 1, 4); + Token.Append((char)int.Parse( + s, + System.Globalization.NumberStyles.AllowHexSpecifier)); + i += 4; + break; default: Token.Append(C); break; @@ -594,7 +629,9 @@ public static JSONNode Parse(string aJSON) throw new Exception("JSON Parse: Quotation marks seems to be messed up."); } if (ctx == null) + { return ParseElement(Token.ToString(), TokenIsQuoted); + } return ctx; } } @@ -702,9 +739,13 @@ public override JSONNode Clone() foreach (var n in m_List) { if (n != null) + { node.Add(n.Clone()); + } else + { node.Add(null); + } } return node; } @@ -725,20 +766,30 @@ internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aSB.Append('['); int count = m_List.Count; if (inline) + { aMode = JSONTextMode.Compact; + } for (int i = 0; i < count; i++) { if (i > 0) + { aSB.Append(','); + } if (aMode == JSONTextMode.Indent) + { aSB.AppendLine(); + } if (aMode == JSONTextMode.Indent) + { aSB.Append(' ', aIndent + aIndentInc); + } m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); } if (aMode == JSONTextMode.Indent) + { aSB.AppendLine().Append(' ', aIndent); + } aSB.Append(']'); } @@ -843,18 +894,28 @@ public override JSONNode this[string aKey] get { if (m_Dict.ContainsKey(aKey)) + { return m_Dict[aKey]; + } else + { return new JSONLazyCreator(this, aKey); + } } set { if (value == null) + { value = JSONNull.CreateOrGet(); + } if (m_Dict.ContainsKey(aKey)) + { m_Dict[aKey] = value; + } else + { m_Dict.Add(aKey, value); + } } } @@ -876,7 +937,9 @@ public override void Add(string aKey, JSONNode aItem) } if (aItem == null) + { aItem = JSONNull.CreateOrGet(); + } m_Dict[aKey] = aItem; } @@ -884,7 +947,9 @@ public override void Add(string aKey, JSONNode aItem) public override JSONNode Remove(string aKey) { if (!m_Dict.ContainsKey(aKey)) + { return null; + } JSONNode tmp = m_Dict[aKey]; m_Dict.Remove(aKey); return tmp; @@ -927,7 +992,9 @@ public override bool HasKey(string aKey) public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) { if (m_Dict.TryGetValue(aKey, out JSONNode res)) + { return res; + } return aDefault; } @@ -947,7 +1014,9 @@ internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aSB.Append('{'); bool first = true; if (inline) + { aMode = JSONTextMode.Compact; + } foreach (var k in m_Dict) { if (!first) @@ -965,7 +1034,9 @@ internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int k.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); } if (aMode == JSONTextMode.Indent) + { aSB.AppendLine().Append(' ', aIndent); + } aSB.Append('}'); } @@ -1126,7 +1197,7 @@ public override long AsLong { if (value > MAX_SAFE_INTEGER || value < MIN_SAFE_INTEGER) { - throw new ArgumentException("JSONumber cannot store an INT64 this large without losing precision"); + throw new ArgumentException("JSONNumber cannot store an INT64 this large without losing precision"); } m_Data = value; @@ -1140,7 +1211,7 @@ public override ulong AsULong { if (value > MAX_SAFE_INTEGER) { - throw new ArgumentException("JSONumber cannot store an INT64 this large without losing precision"); + throw new ArgumentException("JSONNumber cannot store an INT64 this large without losing precision"); } m_Data = value; @@ -1198,7 +1269,9 @@ public override bool Equals(object obj) return m_Data == jsonNumber.m_Data; default: if (IsNumeric(obj)) + { return Convert.ToDouble(obj) == m_Data; + } return base.Equals(obj); } } @@ -1223,7 +1296,9 @@ public override string Value protected set { if (bool.TryParse(value, out bool v)) + { m_Data = v; + } } } @@ -1282,7 +1357,9 @@ public partial class JSONNull : JSONNode public static JSONNull CreateOrGet() { if (reuseSameInstance) + { return m_StaticInstance; + } return new JSONNull(); } @@ -1311,7 +1388,9 @@ public override JSONNode Clone() public override bool Equals(object obj) { if (object.ReferenceEquals(this, obj)) + { return true; + } return (obj is JSONNull); } @@ -1349,9 +1428,13 @@ public JSONLazyCreator(JSONNode aNode, string aKey) private T Set(T aVal) where T : JSONNode { if (m_Key == null) + { m_Node.Add(aVal); + } else + { m_Node.Add(m_Key, aVal); + } m_Node = null; // Be GC friendly. return aVal; } @@ -1391,9 +1474,13 @@ public override void Add(string aKey, JSONNode aItem) public override bool Equals(object obj) { if (obj == null) + { return true; + } if (obj is JSONNull) + { return true; + } return System.Object.ReferenceEquals(this, obj); } diff --git a/Runtime/SimpleJSONBinary.cs b/Runtime/SimpleJSONBinary.cs index 6308e67..36108fe 100644 --- a/Runtime/SimpleJSONBinary.cs +++ b/Runtime/SimpleJSONBinary.cs @@ -74,6 +74,7 @@ public void SaveToCompressedFile(string aFileName) SaveToCompressedStream(F); } } + public string SaveToCompressedBase64() { using (var stream = new System.IO.MemoryStream()) @@ -146,25 +147,15 @@ public static JSONNode DeserializeBinary(System.IO.BinaryReader aReader) return tmp; } case JSONNodeType.String: - { - return new JSONString(aReader.ReadString()); - } + return new JSONString(aReader.ReadString()); case JSONNodeType.Number: - { - return new JSONNumber(aReader.ReadDouble()); - } + return new JSONNumber(aReader.ReadDouble()); case JSONNodeType.Boolean: - { - return new JSONBool(aReader.ReadBoolean()); - } + return new JSONBool(aReader.ReadBoolean()); case JSONNodeType.NullValue: - { - return JSONNull.CreateOrGet(); - } + return JSONNull.CreateOrGet(); default: - { - throw new Exception("Error deserializing JSON. Unknown tag: " + type); - } + throw new Exception("Error deserializing JSON. Unknown tag: " + type); } } @@ -174,6 +165,7 @@ public static JSONNode LoadFromCompressedStream(System.IO.Stream aData) var zin = new ICSharpCode.SharpZipLib.BZip2.BZip2InputStream(aData); return LoadFromBinaryStream(zin); } + public static JSONNode LoadFromCompressedFile(string aFileName) { using(var F = System.IO.File.OpenRead(aFileName)) @@ -181,6 +173,7 @@ public static JSONNode LoadFromCompressedFile(string aFileName) return LoadFromCompressedStream(F); } } + public static JSONNode LoadFromCompressedBase64(string aBase64) { var tmp = System.Convert.FromBase64String(aBase64); @@ -283,6 +276,7 @@ public override void SerializeBinary(System.IO.BinaryWriter aWriter) aWriter.Write(m_Data); } } + public partial class JSONNull : JSONNode { public override void SerializeBinary(System.IO.BinaryWriter aWriter) @@ -290,6 +284,7 @@ public override void SerializeBinary(System.IO.BinaryWriter aWriter) aWriter.Write((byte)JSONNodeType.NullValue); } } + internal partial class JSONLazyCreator : JSONNode { public override void SerializeBinary(System.IO.BinaryWriter aWriter) diff --git a/Runtime/SimpleJSONDotNetTypes.cs b/Runtime/SimpleJSONDotNetTypes.cs index e4b8fde..cc55878 100644 --- a/Runtime/SimpleJSONDotNetTypes.cs +++ b/Runtime/SimpleJSONDotNetTypes.cs @@ -1,6 +1,6 @@ #region License and information /* * * * * - * + * * Extension file for the SimpleJSON framework for better support of some common * .NET types. It does only work together with the SimpleJSON.cs * It provides direct conversion support for types like decimal, char, byte, @@ -11,21 +11,21 @@ * Finally there are some additional type conversion operators for some nullable * types like short?, int?, float?, double?, long? and bool?. They will actually * assign a JSONNull value when it's null or a JSONNumber when it's not. - * + * * The MIT License (MIT) - * + * * Copyright (c) 2020 Markus Göbel (Bunny83) - * + * * 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 @@ -33,7 +33,7 @@ * 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 License and information @@ -49,9 +49,10 @@ public virtual decimal AsDecimal { get { - decimal result; - if (!decimal.TryParse(Value, out result)) + if (!decimal.TryParse(Value, out decimal result)) + { result = 0; + } return result; } set @@ -77,17 +78,25 @@ public virtual char AsChar get { if (IsString && Value.Length > 0) + { return Value[0]; + } if (IsNumber) + { return (char)AsInt; + } return '\0'; } set { if (IsString) + { Value = value.ToString(); + } else if (IsNumber) + { AsInt = (int)value; + } } } @@ -227,20 +236,21 @@ public virtual System.DateTime AsDateTime { get { - System.DateTime result; - if (!System.DateTime.TryParse(Value, CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) + if (!System.DateTime.TryParse(Value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out System.DateTime result)) + { result = new System.DateTime(0); + } return result; } set { - Value = value.ToString(CultureInfo.InvariantCulture); + Value = value.ToString("O"); } } public static implicit operator JSONNode(System.DateTime aDateTime) { - return new JSONString(aDateTime.ToString(CultureInfo.InvariantCulture)); + return new JSONString(aDateTime.ToString("O")); } public static implicit operator System.DateTime(JSONNode aNode) @@ -254,9 +264,10 @@ public virtual System.TimeSpan AsTimeSpan { get { - System.TimeSpan result; - if (!System.TimeSpan.TryParse(Value, CultureInfo.InvariantCulture, out result)) + if (!System.TimeSpan.TryParse(Value, CultureInfo.InvariantCulture, out System.TimeSpan result)) + { result = new System.TimeSpan(0); + } return result; } set @@ -281,8 +292,7 @@ public virtual System.Guid AsGuid { get { - System.Guid result; - System.Guid.TryParse(Value, out result); + System.Guid.TryParse(Value, out System.Guid result); return result; } set @@ -307,21 +317,29 @@ public virtual byte[] AsByteArray { get { - if (this.IsNull || !this.IsArray) + if (IsNull || !IsArray) + { return null; + } int count = Count; byte[] result = new byte[count]; for (int i = 0; i < count; i++) + { result[i] = this[i].AsByte; + } return result; } set { if (!IsArray || value == null) + { return; + } Clear(); for (int i = 0; i < value.Length; i++) + { Add(value[i]); + } } } @@ -341,21 +359,29 @@ public virtual List AsByteList { get { - if (this.IsNull || !this.IsArray) + if (IsNull || !IsArray) + { return null; + } int count = Count; List result = new List(count); for (int i = 0; i < count; i++) + { result.Add(this[i].AsByte); + } return result; } set { if (!IsArray || value == null) + { return; + } Clear(); for (int i = 0; i < value.Count; i++) + { Add(value[i]); + } } } @@ -375,21 +401,29 @@ public virtual string[] AsStringArray { get { - if (this.IsNull || !this.IsArray) + if (IsNull || !IsArray) + { return null; + } int count = Count; string[] result = new string[count]; for (int i = 0; i < count; i++) + { result[i] = this[i].Value; + } return result; } set { if (!IsArray || value == null) + { return; + } Clear(); for (int i = 0; i < value.Length; i++) + { Add(value[i]); + } } } @@ -409,21 +443,29 @@ public virtual List AsStringList { get { - if (this.IsNull || !this.IsArray) + if (IsNull || !IsArray) + { return null; + } int count = Count; List result = new List(count); for (int i = 0; i < count; i++) + { result.Add(this[i].Value); + } return result; } set { if (!IsArray || value == null) + { return; + } Clear(); for (int i = 0; i < value.Count; i++) + { Add(value[i]); + } } } @@ -442,84 +484,108 @@ public static implicit operator List(JSONNode aNode) public static implicit operator JSONNode(int? aValue) { if (aValue == null) + { return JSONNull.CreateOrGet(); + } return new JSONNumber((int)aValue); } public static implicit operator int?(JSONNode aNode) { if (aNode == null || aNode.IsNull) + { return null; + } return aNode.AsInt; } public static implicit operator JSONNode(float? aValue) { if (aValue == null) + { return JSONNull.CreateOrGet(); + } return new JSONNumber((float)aValue); } public static implicit operator float?(JSONNode aNode) { if (aNode == null || aNode.IsNull) + { return null; + } return aNode.AsFloat; } public static implicit operator JSONNode(double? aValue) { if (aValue == null) + { return JSONNull.CreateOrGet(); + } return new JSONNumber((double)aValue); } public static implicit operator double?(JSONNode aNode) { if (aNode == null || aNode.IsNull) + { return null; + } return aNode.AsDouble; } public static implicit operator JSONNode(bool? aValue) { if (aValue == null) + { return JSONNull.CreateOrGet(); + } return new JSONBool((bool)aValue); } public static implicit operator bool?(JSONNode aNode) { if (aNode == null || aNode.IsNull) + { return null; + } return aNode.AsBool; } public static implicit operator JSONNode(long? aValue) { if (aValue == null) + { return JSONNull.CreateOrGet(); + } return new JSONNumber((long)aValue); } public static implicit operator long?(JSONNode aNode) { if (aNode == null || aNode.IsNull) + { return null; + } return aNode.AsLong; } public static implicit operator JSONNode(short? aValue) { if (aValue == null) + { return JSONNull.CreateOrGet(); + } return new JSONNumber((short)aValue); } public static implicit operator short?(JSONNode aNode) { if (aNode == null || aNode.IsNull) + { return null; + } return aNode.AsShort; } #endregion NullableTypes diff --git a/Runtime/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs index 606b008..2a34f83 100644 --- a/Runtime/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -53,7 +53,9 @@ public partial class JSONNode private static JSONNode GetContainer(JSONContainerType aType) { if (aType == JSONContainerType.Array) + { return new JSONArray(); + } return new JSONObject(); } @@ -172,9 +174,13 @@ public static implicit operator Color32(JSONNode aNode) public Vector2 ReadVector2(Vector2 aDefault) { if (IsObject) + { return new Vector2(this["x"].AsFloat, this["y"].AsFloat); + } if (IsArray) + { return new Vector2(this[0].AsFloat, this[1].AsFloat); + } return aDefault; } @@ -219,16 +225,22 @@ public JSONNode WriteVector2(Vector2 aVec, string aXName = "x", string aYName = public Vector3 ReadVector3(Vector3 aDefault) { if (IsObject) + { return new Vector3(this["x"].AsFloat, this["y"].AsFloat, this["z"].AsFloat); + } if (IsArray) + { return new Vector3(this[0].AsFloat, this[1].AsFloat, this[2].AsFloat); + } return aDefault; } public Vector3 ReadVector3(string aXName, string aYName, string aZName) { if (IsObject) + { return new Vector3(this[aXName].AsFloat, this[aYName].AsFloat, this[aZName].AsFloat); + } return Vector3.zero; } @@ -264,9 +276,13 @@ public JSONNode WriteVector3(Vector3 aVec, string aXName = "x", string aYName = public Vector4 ReadVector4(Vector4 aDefault) { if (IsObject) + { return new Vector4(this["x"].AsFloat, this["y"].AsFloat, this["z"].AsFloat, this["w"].AsFloat); + } if (IsArray) + { return new Vector4(this[0].AsFloat, this[1].AsFloat, this[2].AsFloat, this[3].AsFloat); + } return aDefault; } @@ -304,9 +320,13 @@ public JSONNode WriteVector4(Vector4 aVec) public Quaternion ReadQuaternion(Quaternion aDefault) { if (IsObject) + { return new Quaternion(this["x"].AsFloat, this["y"].AsFloat, this["z"].AsFloat, this["w"].AsFloat); + } if (IsArray) + { return new Quaternion(this[0].AsFloat, this[1].AsFloat, this[2].AsFloat, this[3].AsFloat); + } return aDefault; } @@ -344,9 +364,13 @@ public JSONNode WriteQuaternion(Quaternion aRot) public Rect ReadRect(Rect aDefault) { if (IsObject) + { return new Rect(this["x"].AsFloat, this["y"].AsFloat, this["width"].AsFloat, this["height"].AsFloat); + } if (IsArray) + { return new Rect(this[0].AsFloat, this[1].AsFloat, this[2].AsFloat, this[3].AsFloat); + } return aDefault; } @@ -383,9 +407,13 @@ public JSONNode WriteRect(Rect aRect) public RectOffset ReadRectOffset(RectOffset aDefault) { if (this is JSONObject) + { return new RectOffset(this["left"].AsInt, this["right"].AsInt, this["top"].AsInt, this["bottom"].AsInt); + } if (this is JSONArray) + { return new RectOffset(this[0].AsInt, this[1].AsInt, this[2].AsInt, this[3].AsInt); + } return aDefault; } diff --git a/Tests/Editor/SimpleJSONDotNetTests.cs b/Tests/Editor/SimpleJSONDotNetTests.cs new file mode 100644 index 0000000..9d842f4 --- /dev/null +++ b/Tests/Editor/SimpleJSONDotNetTests.cs @@ -0,0 +1,464 @@ +using NUnit.Framework; +using SimpleJSON; +using UnityEngine; +using System.Collections.Generic; + +namespace Tests +{ + public class SimpleJSONDotNetTests + { + private static System.Random rng; + private static double RandomDouble + { + get + { + return rng.NextDouble(); + } + } + + private static int RandomInt + { + get + { + return rng.Next(); + } + } + + private static Dictionary dictionary; + private JSONObject jsonObject; + + [OneTimeSetUp] + public void OneTimeSetUp() + { + rng = new System.Random(); + + dictionary = new Dictionary(); + dictionary["decimal"] = decimal.MaxValue * (decimal)RandomDouble; + dictionary["char"] = (char)(char.MaxValue * RandomDouble); + dictionary["uint"] = (uint)(uint.MaxValue * RandomDouble); + dictionary["byte"] = (byte)(byte.MaxValue * RandomDouble); + dictionary["sbyte"] = (sbyte)(sbyte.MaxValue * RandomDouble); + dictionary["short"] = (short)(short.MaxValue * RandomDouble); + dictionary["ushort"] = (ushort)(ushort.MaxValue * RandomDouble); + dictionary["utcdatetime"] = System.DateTime.UtcNow; + dictionary["datetime"] = System.DateTime.Now; + dictionary["timespan"] = new System.TimeSpan((long)(long.MaxValue * RandomDouble)); + dictionary["guid"] = System.Guid.NewGuid(); + + var bytes = new byte[64]; + rng.NextBytes(bytes); + + dictionary["bytearray"] = bytes; + dictionary["bytelist"] = new List(bytes); + + var str = "abcdefghijklmnopqrstuvwxyz0123456789"; + + var stringArray = new string[str.Length]; + var stringList = new List(); + + for (int i = 0; i < str.Length; i++) + { + var character = str[i].ToString(); + stringArray[i] = character; + stringList.Add(character); + } + + dictionary["stringarray"] = stringArray; + dictionary["stringlist"] = stringList; + + dictionary["int?"] = (int?)RandomInt; + dictionary["float?"] = (float?)(float.MaxValue * RandomDouble); + dictionary["double?"] = (double?)(double.MaxValue * RandomDouble); + dictionary["bool?"] = (bool?)(RandomDouble > 0.5 ? true : false); + dictionary["long?"] = (long?)(long.MaxValue * RandomDouble); + dictionary["short?"] = (short?)(short.MaxValue * RandomDouble); + } + + [SetUp] + public void SetUp() + { + jsonObject = new JSONObject(); + } + + [Test] + public void DecimalTest() + { + var key = "decimal"; + var val = (decimal)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (decimal)deserializedObject[key]); + } + + [Test] + public void CharTest() + { + var key = "char"; + var val = (char)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (char)deserializedObject[key]); + } + + [Test] + public void UIntTest() + { + var key = "uint"; + var val = (uint)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (uint)deserializedObject[key]); + } + + [Test] + public void ByteTest() + { + var key = "byte"; + var val = (byte)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (byte)deserializedObject[key]); + } + + [Test] + public void SByteTest() + { + var key = "sbyte"; + var val = (sbyte)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (sbyte)deserializedObject[key]); + } + + [Test] + public void ShortTest() + { + var key = "short"; + var val = (short)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (short)deserializedObject[key]); + } + + [Test] + public void UShortTest() + { + var key = "ushort"; + var val = (ushort)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (ushort)deserializedObject[key]); + } + + [Test] + public void UTCDateTimeTest() + { + var key = "utcdatetime"; + var val = (System.DateTime)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (System.DateTime)deserializedObject[key]); + } + + [Test] + public void DateTimeTest() + { + var key = "datetime"; + var val = (System.DateTime)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (System.DateTime)deserializedObject[key]); + } + + [Test] + public void TimeSpanTest() + { + var key = "timespan"; + var val = (System.TimeSpan)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (System.TimeSpan)deserializedObject[key]); + } + + [Test] + public void GuidTest() + { + var key = "guid"; + var val = (System.Guid)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (System.Guid)deserializedObject[key]); + } + + [Test] + public void ByteArrayTest() + { + var key = "bytearray"; + var val = (byte[])dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (byte[])deserializedObject[key]); + } + + [Test] + public void ByteListTest() + { + var key = "bytelist"; + var val = (List)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (List)deserializedObject[key]); + } + + [Test] + public void StringArrayTest() + { + var key = "stringarray"; + var val = (string[])dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (string[])deserializedObject[key]); + } + + [Test] + public void StringListTest() + { + var key = "stringlist"; + var val = (List)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (List)deserializedObject[key]); + } + + [Test] + public void NullableIntTest() + { + var key = "int?"; + var val = (int?)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (int?)deserializedObject[key]); + + deserializedObject[key] = null; + + Assert.AreEqual(null, (int?)deserializedObject[key]); + } + + [Test] + public void NullableFloatTest() + { + var key = "float?"; + var val = (float?)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (float?)deserializedObject[key]); + + deserializedObject[key] = null; + + Assert.AreEqual(null, (float?)deserializedObject[key]); + } + + [Test] + public void NullableDoubleTest() + { + var key = "double?"; + var val = (double?)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (double?)deserializedObject[key]); + + deserializedObject[key] = null; + + Assert.AreEqual(null, (double?)deserializedObject[key]); + } + + [Test] + public void NullableBoolTest() + { + var key = "bool?"; + var val = (bool?)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (bool?)deserializedObject[key]); + + deserializedObject[key] = null; + + Assert.AreEqual(null, (bool?)deserializedObject[key]); + } + + [Test] + public void NullableLongTest() + { + var key = "long?"; + var val = (long?)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (long?)deserializedObject[key]); + + deserializedObject[key] = null; + + Assert.AreEqual(null, (long?)deserializedObject[key]); + } + + [Test] + public void NullableShortTest() + { + var key = "short?"; + var val = (short?)dictionary[key]; + + jsonObject[key] = val; + + var jsonObjectString = jsonObject.ToString(); + + Debug.Log(jsonObjectString); + + var deserializedObject = JSON.Parse(jsonObjectString); + + Assert.AreEqual(val, (short?)deserializedObject[key]); + + deserializedObject[key] = null; + + Assert.AreEqual(null, (short?)deserializedObject[key]); + } + } +} diff --git a/Tests/Editor/SimpleJSONDotNetTests.cs.meta b/Tests/Editor/SimpleJSONDotNetTests.cs.meta new file mode 100644 index 0000000..7216a57 --- /dev/null +++ b/Tests/Editor/SimpleJSONDotNetTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9bdc368cbe590444b80e20e337d45e62 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From a2b11bc7191ba915925609d38e4c49d89f2b4df3 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Thu, 15 Oct 2020 22:12:17 -0700 Subject: [PATCH 16/23] Properly(?) set up the assembly definition file for the unit tests --- .../Unity.SimpleJSON.Editor.Tests.asmdef | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef b/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef index f85242d..3f2234c 100644 --- a/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef +++ b/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef @@ -1,18 +1,23 @@ { "name": "Unity.SimpleJSON.Editor.Tests", "references": [ - "Unity.SimpleJSON" - ], - "optionalUnityReferences": [ - "TestAssemblies" + "Unity.SimpleJSON", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner" ], "includePlatforms": [ "Editor" ], "excludePlatforms": [], "allowUnsafeCode": false, - "overrideReferences": false, - "precompiledReferences": [], - "autoReferenced": true, - "defineConstraints": [] + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file From 9f688c93c599ec73ab8b2955455d03af7e1d7636 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Thu, 15 Sep 2022 17:36:00 -0700 Subject: [PATCH 17/23] Fixes from the main repository Update version number --- Runtime/SimpleJSON.cs | 10 +++++----- Runtime/SimpleJSONSerializer.cs | 11 +++++++---- Runtime/SimpleJSONUnity.cs | 2 ++ package.json | 29 +++++++++++++++-------------- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index 41ea915..5696fd4 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -209,7 +209,7 @@ public virtual long AsLong { get { - if (long.TryParse(Value, out long val)) + if (long.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out long val)) { return val; } @@ -217,7 +217,7 @@ public virtual long AsLong } set { - Value = value.ToString(); + Value = value.ToString(CultureInfo.InvariantCulture); } } @@ -225,7 +225,7 @@ public virtual ulong AsULong { get { - if (ulong.TryParse(Value, out ulong val)) + if (ulong.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out ulong val)) { return val; } @@ -233,7 +233,7 @@ public virtual ulong AsULong } set { - Value = value.ToString(); + Value = value.ToString(CultureInfo.InvariantCulture); } } @@ -259,7 +259,7 @@ public virtual JSONObject AsObject public static implicit operator JSONNode(string s) { - return new JSONString(s); + return (s == null) ? (JSONNode)JSONNull.CreateOrGet() : new JSONString(s); } public static implicit operator string(JSONNode d) diff --git a/Runtime/SimpleJSONSerializer.cs b/Runtime/SimpleJSONSerializer.cs index dc14732..3768940 100644 --- a/Runtime/SimpleJSONSerializer.cs +++ b/Runtime/SimpleJSONSerializer.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Generic; +using System.Globalization; using UnityEngine; namespace SimpleJSON @@ -57,10 +58,12 @@ public static JSONNode ToJSONNode(object value) return color32Value; #endif - case long _: - case ulong _: - case decimal _: - return new JSONString(value.ToString()); + case long longValue: + return longValue.ToString(CultureInfo.InvariantCulture); + case ulong uLongValue: + return uLongValue.ToString(CultureInfo.InvariantCulture); + case decimal decimalValue: + return decimalValue.ToString(CultureInfo.InvariantCulture); default: if (JSONNumber.IsNumeric(value)) { diff --git a/Runtime/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs index 2a34f83..c161da5 100644 --- a/Runtime/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -1,3 +1,4 @@ +#if UNITY_5_3_OR_NEWER #region License and information /* * * * * * @@ -572,3 +573,4 @@ public JSONNode WriteColor32(Color32 aColor32) #endregion Color32 } } +#endif diff --git a/package.json b/package.json index 3f74402..7b7bf89 100755 --- a/package.json +++ b/package.json @@ -1,16 +1,17 @@ { - "name": "com.github.bunny83.simplejson", - "version": "0.1.0", - "displayName": "SimpleJSON", - "description": "JSON Parser (A simple one)", - "unity": "2018.4", - "keywords": [ - "utilities", - "JSON" - ], - "author": { - "name": "Markus Göbel", - "email": "", - "url": "https://github.com/Bunny83" - } + "name": "com.github.bunny83.simplejson", + "version": "1.0.0", + "displayName": "SimpleJSON", + "description": "JSON Parser (A simple one)", + "unity": "2018.4", + "keywords": [ + "utilities", + "JSON" + ], + "author": { + "name": "Markus G\u00f6bel", + "email": "", + "url": "https://github.com/Bunny83" + }, + "type": "library" } \ No newline at end of file From 5ebb38aa64965b8b8edc8c134da2c8cb7ebd0f70 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Sat, 5 Nov 2022 17:34:09 -0700 Subject: [PATCH 18/23] Add support for UnityEngine.Pose serialization --- Runtime/SimpleJSONUnity.cs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Runtime/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs index c161da5..e17d5f2 100644 --- a/Runtime/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -571,6 +571,37 @@ public JSONNode WriteColor32(Color32 aColor32) return this; } #endregion Color32 + + #region Pose + public static implicit operator JSONNode(Pose aPose) + { + JSONNode n = new JSONObject(); + n.WritePose(aPose); + return n; + } + + public static implicit operator Pose(JSONNode aNode) + { + return aNode.ReadPose(); + } + + public Pose ReadPose() + { + return new Pose(this["position"].ReadVector3(), + Quaternion.Euler(this["rotation"].ReadVector3())); + } + + public JSONNode WritePose(Pose aPose) + { + Clear(); + + this["position"] = aPose.position; + this["rotation"] = aPose.rotation.eulerAngles; + + return this; + } + + #endregion Pose } } #endif From 34909805848d11c4a25ca9f3ebf0a7c33ce0c3fc Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Thu, 13 Apr 2023 19:26:12 -0700 Subject: [PATCH 19/23] Add generic TryGetValue method Add generic GetValueOrDefault method. Fix Pose serialization. Add tests. --- Runtime/SimpleJSON.cs | 49 ++++++++++++++++++++++++---- Runtime/SimpleJSONUnity.cs | 26 ++++++++++++--- Tests/Editor/SimpleJSONTests.cs | 34 ++++++++++++++++++- Tests/Editor/SimpleJSONUnityTests.cs | 25 +++++++++++++- 4 files changed, 121 insertions(+), 13 deletions(-) diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index 5696fd4..e21270b 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -141,6 +141,23 @@ public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) return aDefault; } + public virtual T GetValueOrDefault(string aKey, T aDefault) where T : JSONNode + { + return aDefault; + } + + public virtual bool TryGetValue(string aKey, out JSONNode value) + { + value = null; + return false; + } + + public virtual bool TryGetValue(string key, out T value) where T : JSONNode + { + value = null; + return false; + } + public override string ToString() { StringBuilder sb = new StringBuilder(); @@ -991,13 +1008,36 @@ public override bool HasKey(string aKey) public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) { - if (m_Dict.TryGetValue(aKey, out JSONNode res)) + return GetValueOrDefault(aKey, aDefault); + } + + public override T GetValueOrDefault(string aKey, T aDefault) + { + if (TryGetValue(aKey, out T value)) { - return res; + return value; } + return aDefault; } + public override bool TryGetValue(string key, out JSONNode value) + { + return TryGetValue(key, out value); + } + + public override bool TryGetValue(string key, out T value) + { + if (m_Dict.TryGetValue(key, out var node) && node is T result) + { + value = result; + return true; + } + + value = null; + return false; + } + public override IEnumerable Children { get @@ -1050,11 +1090,6 @@ bool IDictionary.Remove(string key) return m_Dict.Remove(key); } - public bool TryGetValue(string key, out JSONNode value) - { - return m_Dict.TryGetValue(key, out value); - } - public void Add(KeyValuePair item) { ((IDictionary)m_Dict).Add(item); diff --git a/Runtime/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs index e17d5f2..0eb17a6 100644 --- a/Runtime/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -587,16 +587,34 @@ public static implicit operator Pose(JSONNode aNode) public Pose ReadPose() { - return new Pose(this["position"].ReadVector3(), - Quaternion.Euler(this["rotation"].ReadVector3())); + if (IsObject) + { + return new Pose(this["position"].ReadVector3(), + this["rotation"].ReadQuaternion()); + } + if (IsArray) + { + return new Pose(this[0].ReadVector3(), + this[1].ReadQuaternion()); + } + + return new Pose(); } public JSONNode WritePose(Pose aPose) { Clear(); - this["position"] = aPose.position; - this["rotation"] = aPose.rotation.eulerAngles; + if (IsObject) + { + this["position"] = aPose.position; + this["rotation"] = aPose.rotation; + } + else if (IsArray) + { + Add(aPose.position); + Add(aPose.rotation); + } return this; } diff --git a/Tests/Editor/SimpleJSONTests.cs b/Tests/Editor/SimpleJSONTests.cs index a5dff35..f374d47 100644 --- a/Tests/Editor/SimpleJSONTests.cs +++ b/Tests/Editor/SimpleJSONTests.cs @@ -10,7 +10,7 @@ public class SimpleJSONTests private const long MAX_SAFE_INTEGER = (long)1 << 53; private const long MIN_SAFE_INTEGER = -(long)1 << 53; - private const string jsonString = "{ \"array\": [1.44,2,3], " + + private const string jsonString = "{ \"array\": [1.44,2,3, 5.12345678], " + "\"object\": {\"key1\":\"value1\", \"key2\":256}, " + "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " + "\"unicode\": \"\\u3041 Men\u00fa sesi\u00f3n\", " + @@ -453,5 +453,37 @@ public void MinMaxTest() bigInt64.AsULong = (long)(MAX_SAFE_INTEGER + 1); }); } + + [Test] + public void TryGetValueTest() + { + Assert.IsTrue(parsedJSON.TryGetValue("string", out var temp)); + Assert.AreEqual(temp.Value, stringValue); + + Assert.IsTrue(parsedJSON.TryGetValue("string", out var stringResult)); + Assert.AreEqual(stringResult.Value, stringValue); + + Assert.IsTrue(parsedJSON.TryGetValue("array", out var array)); + for (var i = 0; i < array.Count; i++) + { + Assert.AreEqual(doubleArray[i], array[i].AsDouble); + } + + Assert.IsFalse(parsedJSON.TryGetValue("array_fail", out var array_fail)); + Assert.IsNull(array_fail); + + var result = parsedJSON.GetValueOrDefault("unicode", "not found"); + Assert.AreEqual(result.Value, unicodeValue); + + result = parsedJSON.GetValueOrDefault("unicode_fail", "not found"); + Assert.AreEqual(result.Value, "not found"); + + result = parsedJSON.GetValueOrDefault("int", (JSONNumber)42); + Assert.AreEqual(result.AsInt, intValue); + + result = parsedJSON.GetValueOrDefault("int_fail", (JSONNumber)42); + Assert.AreNotEqual(result.AsInt, intValue); + Assert.AreEqual(result.AsInt, 42); + } } } diff --git a/Tests/Editor/SimpleJSONUnityTests.cs b/Tests/Editor/SimpleJSONUnityTests.cs index 6701747..2d8ab75 100644 --- a/Tests/Editor/SimpleJSONUnityTests.cs +++ b/Tests/Editor/SimpleJSONUnityTests.cs @@ -87,6 +87,29 @@ public void QuaternionTest() Assert.AreEqual(quat, deserializedArray); } + [Test] + public void PoseTest() + { + Vector3 position = Random.insideUnitSphere * 256.0f; + Quaternion rotation = Random.rotation; + + Pose pose = new Pose(position, rotation); + + var jsonObject = new JSONObject().WritePose(pose); + var jsonArray = new JSONArray().WritePose(pose); + + var jsonObjectString = jsonObject.ToString(); + var jsonArrayString = jsonArray.ToString(); + + Debug.Log($"{pose.GetType().Name} object: {jsonObjectString} array: {jsonArrayString}"); + + var deserializedObject = JSON.Parse(jsonObjectString).ReadPose(); + var deserializedArray = JSON.Parse(jsonArrayString).ReadPose(); + + Assert.AreEqual(pose, deserializedObject); + Assert.AreEqual(pose, deserializedArray); + } + [Test] public void RectTest() { @@ -203,4 +226,4 @@ public void Color32Test() Assert.AreEqual(deserializedString, deserializedArray); } } -} \ No newline at end of file +} From b52d257edffe002f14398c79b190359982251f24 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Sun, 24 Sep 2023 15:03:39 -0700 Subject: [PATCH 20/23] Change namespace to avoid collision with main SimpleJSON --- Runtime/SimpleJSON.cs | 2 +- Runtime/SimpleJSONBinary.cs | 2 +- Runtime/SimpleJSONDotNetTypes.cs | 2 +- Runtime/SimpleJSONSerializer.cs | 2 +- Runtime/SimpleJSONUnity.cs | 2 +- Tests/Editor/SimpleJSONDotNetTests.cs | 2 +- Tests/Editor/SimpleJSONTests.cs | 2 +- Tests/Editor/SimpleJSONUnityTests.cs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index e21270b..146ada9 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -39,7 +39,7 @@ using System.Globalization; using System.Text; -namespace SimpleJSON +namespace Utilties.SimpleJSON { public enum JSONNodeType { diff --git a/Runtime/SimpleJSONBinary.cs b/Runtime/SimpleJSONBinary.cs index 36108fe..fe55d42 100644 --- a/Runtime/SimpleJSONBinary.cs +++ b/Runtime/SimpleJSONBinary.cs @@ -41,7 +41,7 @@ * * * * */ using System; -namespace SimpleJSON +namespace Utilties.SimpleJSON { #if !SimpleJSON_ExcludeBinary public abstract partial class JSONNode diff --git a/Runtime/SimpleJSONDotNetTypes.cs b/Runtime/SimpleJSONDotNetTypes.cs index cc55878..0eab295 100644 --- a/Runtime/SimpleJSONDotNetTypes.cs +++ b/Runtime/SimpleJSONDotNetTypes.cs @@ -38,7 +38,7 @@ #endregion License and information -namespace SimpleJSON +namespace Utilties.SimpleJSON { using System.Globalization; using System.Collections.Generic; diff --git a/Runtime/SimpleJSONSerializer.cs b/Runtime/SimpleJSONSerializer.cs index 3768940..b1a52e9 100644 --- a/Runtime/SimpleJSONSerializer.cs +++ b/Runtime/SimpleJSONSerializer.cs @@ -3,7 +3,7 @@ using System.Globalization; using UnityEngine; -namespace SimpleJSON +namespace Utilties.SimpleJSON { public interface ISimpleJSONSerializable { diff --git a/Runtime/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs index 0eb17a6..9b4c393 100644 --- a/Runtime/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -42,7 +42,7 @@ using UnityEngine; -namespace SimpleJSON +namespace Utilties.SimpleJSON { public enum JSONContainerType { Array, Object } public partial class JSONNode diff --git a/Tests/Editor/SimpleJSONDotNetTests.cs b/Tests/Editor/SimpleJSONDotNetTests.cs index 9d842f4..4f1de1a 100644 --- a/Tests/Editor/SimpleJSONDotNetTests.cs +++ b/Tests/Editor/SimpleJSONDotNetTests.cs @@ -1,5 +1,5 @@ using NUnit.Framework; -using SimpleJSON; +using Utilties.SimpleJSON; using UnityEngine; using System.Collections.Generic; diff --git a/Tests/Editor/SimpleJSONTests.cs b/Tests/Editor/SimpleJSONTests.cs index f374d47..d1c6d10 100644 --- a/Tests/Editor/SimpleJSONTests.cs +++ b/Tests/Editor/SimpleJSONTests.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using NUnit.Framework; -using SimpleJSON; +using Utilties.SimpleJSON; using UnityEngine; namespace Tests diff --git a/Tests/Editor/SimpleJSONUnityTests.cs b/Tests/Editor/SimpleJSONUnityTests.cs index 2d8ab75..4c2bd91 100644 --- a/Tests/Editor/SimpleJSONUnityTests.cs +++ b/Tests/Editor/SimpleJSONUnityTests.cs @@ -1,5 +1,5 @@ using NUnit.Framework; -using SimpleJSON; +using Utilties.SimpleJSON; using UnityEngine; namespace Tests From b8edacd0b24a4ab6eb6513d6517725cfa9f25dc0 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Sun, 24 Sep 2023 15:04:37 -0700 Subject: [PATCH 21/23] bump version --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7b7bf89..1e776a7 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.github.bunny83.simplejson", - "version": "1.0.0", + "version": "1.0.1", "displayName": "SimpleJSON", "description": "JSON Parser (A simple one)", "unity": "2018.4", @@ -14,4 +14,4 @@ "url": "https://github.com/Bunny83" }, "type": "library" -} \ No newline at end of file +} From 6deead5cc3c025777f5f6e33e8661f5083923f72 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Thu, 2 May 2024 10:51:54 -0700 Subject: [PATCH 22/23] Fix misspelled namespace (facepalm) --- Runtime/SimpleJSON.cs | 2 +- Runtime/SimpleJSONBinary.cs | 2 +- Runtime/SimpleJSONDotNetTypes.cs | 2 +- Runtime/SimpleJSONSerializer.cs | 2 +- Runtime/SimpleJSONUnity.cs | 2 +- Tests/Editor/SimpleJSONDotNetTests.cs | 2 +- Tests/Editor/SimpleJSONTests.cs | 2 +- Tests/Editor/SimpleJSONUnityTests.cs | 2 +- package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index 146ada9..2be6ae6 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -39,7 +39,7 @@ using System.Globalization; using System.Text; -namespace Utilties.SimpleJSON +namespace Utilities.SimpleJSON { public enum JSONNodeType { diff --git a/Runtime/SimpleJSONBinary.cs b/Runtime/SimpleJSONBinary.cs index fe55d42..dc8639a 100644 --- a/Runtime/SimpleJSONBinary.cs +++ b/Runtime/SimpleJSONBinary.cs @@ -41,7 +41,7 @@ * * * * */ using System; -namespace Utilties.SimpleJSON +namespace Utilities.SimpleJSON { #if !SimpleJSON_ExcludeBinary public abstract partial class JSONNode diff --git a/Runtime/SimpleJSONDotNetTypes.cs b/Runtime/SimpleJSONDotNetTypes.cs index 0eab295..1f83b86 100644 --- a/Runtime/SimpleJSONDotNetTypes.cs +++ b/Runtime/SimpleJSONDotNetTypes.cs @@ -38,7 +38,7 @@ #endregion License and information -namespace Utilties.SimpleJSON +namespace Utilities.SimpleJSON { using System.Globalization; using System.Collections.Generic; diff --git a/Runtime/SimpleJSONSerializer.cs b/Runtime/SimpleJSONSerializer.cs index b1a52e9..585d16f 100644 --- a/Runtime/SimpleJSONSerializer.cs +++ b/Runtime/SimpleJSONSerializer.cs @@ -3,7 +3,7 @@ using System.Globalization; using UnityEngine; -namespace Utilties.SimpleJSON +namespace Utilities.SimpleJSON { public interface ISimpleJSONSerializable { diff --git a/Runtime/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs index 9b4c393..9f201db 100644 --- a/Runtime/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -42,7 +42,7 @@ using UnityEngine; -namespace Utilties.SimpleJSON +namespace Utilities.SimpleJSON { public enum JSONContainerType { Array, Object } public partial class JSONNode diff --git a/Tests/Editor/SimpleJSONDotNetTests.cs b/Tests/Editor/SimpleJSONDotNetTests.cs index 4f1de1a..123f4f1 100644 --- a/Tests/Editor/SimpleJSONDotNetTests.cs +++ b/Tests/Editor/SimpleJSONDotNetTests.cs @@ -1,5 +1,5 @@ using NUnit.Framework; -using Utilties.SimpleJSON; +using Utilities.SimpleJSON; using UnityEngine; using System.Collections.Generic; diff --git a/Tests/Editor/SimpleJSONTests.cs b/Tests/Editor/SimpleJSONTests.cs index d1c6d10..293aa3f 100644 --- a/Tests/Editor/SimpleJSONTests.cs +++ b/Tests/Editor/SimpleJSONTests.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using NUnit.Framework; -using Utilties.SimpleJSON; +using Utilities.SimpleJSON; using UnityEngine; namespace Tests diff --git a/Tests/Editor/SimpleJSONUnityTests.cs b/Tests/Editor/SimpleJSONUnityTests.cs index 4c2bd91..64710d9 100644 --- a/Tests/Editor/SimpleJSONUnityTests.cs +++ b/Tests/Editor/SimpleJSONUnityTests.cs @@ -1,5 +1,5 @@ using NUnit.Framework; -using Utilties.SimpleJSON; +using Utilities.SimpleJSON; using UnityEngine; namespace Tests diff --git a/package.json b/package.json index 1e776a7..6a22646 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.github.bunny83.simplejson", - "version": "1.0.1", + "version": "1.0.2", "displayName": "SimpleJSON", "description": "JSON Parser (A simple one)", "unity": "2018.4", From bacbbdcc4ab800752c224f94032baeb0f9ceef13 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Thu, 2 May 2024 12:03:16 -0700 Subject: [PATCH 23/23] Optimization from navidbigdeli54 commit 81d9a34 Use TryGetValue instead of separate ContainsKey and Get. --- Runtime/SimpleJSON.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Runtime/SimpleJSON.cs b/Runtime/SimpleJSON.cs index 2be6ae6..35eeda3 100644 --- a/Runtime/SimpleJSON.cs +++ b/Runtime/SimpleJSON.cs @@ -910,9 +910,9 @@ public override JSONNode this[string aKey] { get { - if (m_Dict.ContainsKey(aKey)) + if (m_Dict.TryGetValue(aKey, out var value)) { - return m_Dict[aKey]; + return value; } else { @@ -925,14 +925,7 @@ public override JSONNode this[string aKey] { value = JSONNull.CreateOrGet(); } - if (m_Dict.ContainsKey(aKey)) - { - m_Dict[aKey] = value; - } - else - { - m_Dict.Add(aKey, value); - } + m_Dict[aKey] = value; } } @@ -963,11 +956,11 @@ public override void Add(string aKey, JSONNode aItem) public override JSONNode Remove(string aKey) { - if (!m_Dict.ContainsKey(aKey)) + if (!m_Dict.TryGetValue(aKey, out var value)) { return null; } - JSONNode tmp = m_Dict[aKey]; + JSONNode tmp = value; m_Dict.Remove(aKey); return tmp; }