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/Changelog.txt b/CHANGELOG.md similarity index 100% rename from Changelog.txt rename to CHANGELOG.md 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 b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md 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 b/README deleted file mode 100644 index e69de29..0000000 diff --git a/README.md b/README.md new file mode 100644 index 0000000..f9b699f --- /dev/null +++ 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/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/SimpleJSON.cs b/Runtime/SimpleJSON.cs similarity index 68% rename from SimpleJSON.cs rename to Runtime/SimpleJSON.cs index 0eee026..35eeda3 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,16 +31,15 @@ * 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; using System.Collections.Generic; using System.Globalization; -using System.Linq; using System.Text; -namespace SimpleJSON +namespace Utilities.SimpleJSON { public enum JSONNodeType { @@ -53,6 +52,7 @@ public enum JSONNodeType None = 7, Custom = 0xFF, } + public enum JSONTextMode { Compact, @@ -61,119 +61,21 @@ public enum JSONTextMode 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 + protected const string TOKEN_NULL = "null"; + protected const string TOKEN_TRUE = "true"; + protected const string TOKEN_FALSE = "false"; #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[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 JSONNode this[string aKey] { get { return null; } set { } } - - public virtual string Value { get { return ""; } set { } } + public virtual string Value { get { return null; } protected set { } } public virtual int Count { get { return 0; } } @@ -188,47 +90,44 @@ IEnumerator IEnumerable.GetEnumerator() public virtual void Add(string aKey, JSONNode aItem) { + throw new NotImplementedException(); } + public virtual void Add(JSONNode aItem) { - Add("", aItem); + Add(null, aItem); } public virtual JSONNode Remove(string aKey) { - return null; + throw new NotImplementedException(); } public virtual JSONNode Remove(int aIndex) { - return null; + throw new NotImplementedException(); } public virtual JSONNode Remove(JSONNode aNode) { - return aNode; + throw new NotImplementedException(); } - public virtual JSONNode Clone() + public virtual void Clear() { - return null; + throw new NotImplementedException(); } - public virtual IEnumerable Children + public virtual JSONNode Clone() { - get - { - yield break; - } + throw new NotImplementedException(); } - public IEnumerable DeepChildren + public virtual IEnumerable Children { get { - foreach (var C in Children) - foreach (var D in C.DeepChildren) - yield return D; + yield break; } } @@ -242,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(); @@ -252,28 +168,24 @@ public override string ToString() public virtual string ToString(int aIndent) { StringBuilder sb = new StringBuilder(); - WriteToStringBuilder(sb, 0, aIndent, JSONTextMode.Indent); + 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()); } } + internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); #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)) + if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double v)) + { return v; + } return 0.0; } set @@ -298,14 +210,15 @@ 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); } set { - Value = (value) ? "true" : "false"; + Value = (value) ? TOKEN_TRUE : TOKEN_FALSE; } } @@ -313,14 +226,31 @@ public virtual long AsLong { get { - long val = 0; - if (long.TryParse(Value, out val)) + if (long.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out long val)) + { return val; + } return 0L; } set { - Value = value.ToString(); + Value = value.ToString(CultureInfo.InvariantCulture); + } + } + + public virtual ulong AsULong + { + get + { + if (ulong.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out ulong val)) + { + return val; + } + return 0; + } + set + { + Value = value.ToString(CultureInfo.InvariantCulture); } } @@ -340,15 +270,15 @@ public virtual JSONObject AsObject } } - #endregion typecasting properties #region operators 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) { return (d == null) ? null : d.Value; @@ -358,6 +288,7 @@ public static implicit operator JSONNode(double n) { return new JSONNumber(n); } + public static implicit operator double(JSONNode d) { return (d == null) ? 0 : d.AsDouble; @@ -367,6 +298,7 @@ public static implicit operator JSONNode(float n) { return new JSONNumber(n); } + public static implicit operator float(JSONNode d) { return (d == null) ? 0 : d.AsFloat; @@ -376,6 +308,7 @@ public static implicit operator JSONNode(int n) { return new JSONNumber(n); } + public static implicit operator int(JSONNode d) { return (d == null) ? 0 : d.AsInt; @@ -383,19 +316,29 @@ 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) { return (d == null) ? 0L : d.AsLong; } + public static implicit operator JSONNode(ulong n) + { + return new JSONString(n.ToString()); + } + + public static implicit operator ulong(JSONNode d) + { + return (d == null) ? 0 : d.AsULong; + } + public static implicit operator JSONNode(bool b) { return new JSONBool(b); } + public static implicit operator bool(JSONNode d) { return (d == null) ? false : d.AsBool; @@ -409,11 +352,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); } @@ -436,21 +383,27 @@ public override int GetHashCode() [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) @@ -483,7 +436,9 @@ internal static string Escape(string aText) sb.Append("\\u").Append(val.ToString("X4")); } else + { sb.Append(c); + } break; } } @@ -495,17 +450,34 @@ internal static string Escape(string aText) 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)) + } + + 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) @@ -514,7 +486,7 @@ public static JSONNode Parse(string aJSON) JSONNode ctx = null; int i = 0; StringBuilder Token = new StringBuilder(); - string TokenName = ""; + string TokenName = null; bool QuoteMode = false; bool TokenIsQuoted = false; while (i < aJSON.Length) @@ -532,7 +504,7 @@ public static JSONNode Parse(string aJSON) { ctx.Add(TokenName, stack.Peek()); } - TokenName = ""; + TokenName = null; Token.Length = 0; ctx = stack.Peek(); break; @@ -549,7 +521,7 @@ public static JSONNode Parse(string aJSON) { ctx.Add(TokenName, stack.Peek()); } - TokenName = ""; + TokenName = null; Token.Length = 0; ctx = stack.Peek(); break; @@ -569,7 +541,7 @@ public static JSONNode Parse(string aJSON) if (Token.Length > 0 || TokenIsQuoted) ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); TokenIsQuoted = false; - TokenName = ""; + TokenName = null; Token.Length = 0; if (stack.Count > 0) ctx = stack.Peek(); @@ -600,7 +572,7 @@ public static JSONNode Parse(string aJSON) if (Token.Length > 0 || TokenIsQuoted) ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); TokenIsQuoted = false; - TokenName = ""; + TokenName = null; Token.Length = 0; TokenIsQuoted = false; break; @@ -612,7 +584,9 @@ public static JSONNode Parse(string aJSON) case ' ': case '\t': if (QuoteMode) + { Token.Append(aJSON[i]); + } break; case '\\': @@ -638,14 +612,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; @@ -674,14 +646,15 @@ 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; } - } // End of JSONNode - public partial class JSONArray : JSONNode + public partial class JSONArray : JSONNode, IList { private List m_List = new List(); private bool inline = false; @@ -693,36 +666,44 @@ 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] { get { if (aIndex < 0 || aIndex >= m_List.Count) - return new JSONLazyCreator(this); + { + throw new IndexOutOfRangeException(); + } + return m_List[aIndex]; } set { + if (aIndex < 0 || aIndex >= m_List.Count) + { + throw new IndexOutOfRangeException(); + } + if (value == null) + { value = JSONNull.CreateOrGet(); - if (aIndex < 0 || aIndex >= m_List.Count) - m_List.Add(value); - else - m_List[aIndex] = value; + } + + m_List[aIndex] = value; } } - public override JSONNode this[string aKey] + public override JSONArray AsArray { - get { return new JSONLazyCreator(this); } - set - { - if (value == null) - value = JSONNull.CreateOrGet(); - m_List.Add(value); - } + get => this; + } + + public int Capacity + { + get { return m_List.Capacity; } + set { m_List.Capacity = value; } } public override int Count @@ -732,15 +713,31 @@ public override int 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) - return null; + { + throw new IndexOutOfRangeException(); + } + JSONNode tmp = m_List[aIndex]; m_List.RemoveAt(aIndex); return tmp; @@ -756,12 +753,16 @@ 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()); + } else + { node.Add(null); + } } return node; } @@ -770,41 +771,98 @@ public override IEnumerable Children { get { - foreach (JSONNode N in m_List) - yield return N; + foreach (var node in m_List) + { + yield return node; + } } } - 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 override 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 + public partial class JSONObject : JSONNode, IDictionary { private Dictionary m_Dict = new Dictionary(); private bool inline = false; + public override bool Inline { get { return inline; } @@ -814,48 +872,68 @@ 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 + { + return m_Dict.Keys; + } + } + public ICollection Keys + { + get + { + return m_Dict.Keys; + } + } - public override JSONNode this[string aKey] + ICollection IDictionary.Values { get { - if (m_Dict.ContainsKey(aKey)) - return m_Dict[aKey]; - else - return new JSONLazyCreator(this, aKey); + return m_Dict.Values; } - set + } + + public ICollection Values + { + get { - if (value == null) - value = JSONNull.CreateOrGet(); - if (m_Dict.ContainsKey(aKey)) - m_Dict[aKey] = value; - else - m_Dict.Add(aKey, value); + return m_Dict.Values; } } - public override JSONNode this[int aIndex] + public bool IsReadOnly => false; + + public override JSONNode this[string aKey] { get { - if (aIndex < 0 || aIndex >= m_Dict.Count) - return null; - return m_Dict.ElementAt(aIndex).Value; + if (m_Dict.TryGetValue(aKey, out var value)) + { + return value; + } + else + { + return new JSONLazyCreator(this, aKey); + } } 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; + } + m_Dict[aKey] = value; } } + public override JSONObject AsObject + { + get => this; + } + public override int Count { get { return m_Dict.Count; } @@ -863,50 +941,47 @@ public override int Count public override void Add(string aKey, JSONNode aItem) { - if (aItem == null) - aItem = JSONNull.CreateOrGet(); + if (aKey == null) + { + throw new NotImplementedException(); + } - if (aKey != null) + if (aItem == null) { - if (m_Dict.ContainsKey(aKey)) - m_Dict[aKey] = aItem; - else - m_Dict.Add(aKey, aItem); + aItem = JSONNull.CreateOrGet(); } - else - m_Dict.Add(Guid.NewGuid().ToString(), aItem); + + m_Dict[aKey] = 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; } 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; + throw new NotImplementedException(); } public override JSONNode Remove(JSONNode aNode) { - try + foreach (var kvp in m_Dict) { - var item = m_Dict.Where(k => k.Value == aNode).First(); - m_Dict.Remove(item.Key); - return aNode; - } - catch - { - return null; + if (kvp.Value == aNode) + { + m_Dict.Remove(kvp.Key); + return kvp.Value; + } } + + return null; } public override JSONNode Clone() @@ -926,18 +1001,44 @@ public override bool HasKey(string aKey) public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) { - JSONNode res; - if (m_Dict.TryGetValue(aKey, out res)) - return res; + return GetValueOrDefault(aKey, aDefault); + } + + public override T GetValueOrDefault(string aKey, T aDefault) + { + if (TryGetValue(aKey, out T value)) + { + 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 { - foreach (KeyValuePair N in m_Dict) - yield return N.Value; + foreach (var node in m_Dict.Values) + { + yield return node; + } } } @@ -946,7 +1047,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) @@ -964,10 +1067,56 @@ 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('}'); } + public bool ContainsKey(string key) + { + return m_Dict.ContainsKey(key); + } + + bool IDictionary.Remove(string key) + { + return m_Dict.Remove(key); + } + + public void Add(KeyValuePair item) + { + ((IDictionary)m_Dict).Add(item); + } + + public override 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 @@ -977,23 +1126,32 @@ public partial class JSONString : JSONNode public override JSONNodeType Tag { get { return JSONNodeType.String; } } public override bool IsString { get { return true; } } - - public override Enumerator GetEnumerator() { return new Enumerator(); } - + public override bool IsNull { get { return m_Data == null; } } public override string Value { get { return m_Data; } - set + protected set { m_Data = value; } } + public JSONString() : this(null) + { + + } + public JSONString(string aData) { m_Data = aData; } + + public override void Clear() + { + Value = null; + } + public override JSONNode Clone() { return new JSONString(m_Data); @@ -1001,20 +1159,30 @@ public override JSONNode Clone() 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) { - 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; + 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(); @@ -1024,20 +1192,23 @@ 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; } } 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 + 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; + } } } @@ -1046,10 +1217,33 @@ 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; } + set + { + if (value > MAX_SAFE_INTEGER || value < MIN_SAFE_INTEGER) + { + throw new ArgumentException("JSONNumber cannot store an INT64 this large without losing precision"); + } + + m_Data = value; + } + } + + public override ulong AsULong + { + get { return (ulong)m_Data; } + set + { + if (value > MAX_SAFE_INTEGER) + { + throw new ArgumentException("JSONNumber cannot store an INT64 this large without losing precision"); + } + + m_Data = value; + } } public JSONNumber(double aData) @@ -1071,28 +1265,45 @@ internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int { aSB.Append(Value); } - private 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) { - 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; + 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(); @@ -1106,18 +1317,19 @@ 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 { get { return m_Data.ToString(); } - set + protected set { - bool v; - if (bool.TryParse(value, out v)) + if (bool.TryParse(value, out bool v)) + { m_Data = v; + } } } + public override bool AsBool { get { return m_Data; } @@ -1141,16 +1353,24 @@ public override JSONNode Clone() internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { - aSB.Append((m_Data) ? "true" : "false"); + aSB.Append((m_Data) ? TOKEN_TRUE : TOKEN_FALSE); } + public override bool Equals(object obj) { - if (obj == null) - return false; - if (obj is bool) - return m_Data == (bool)obj; - return false; + 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(); @@ -1160,25 +1380,28 @@ 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() { 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 { } + get { return null; } + protected set { } } + public override bool AsBool { get { return false; } @@ -1193,9 +1416,12 @@ public override JSONNode Clone() public override bool Equals(object obj) { if (object.ReferenceEquals(this, obj)) + { return true; + } return (obj is JSONNull); } + public override int GetHashCode() { return 0; @@ -1203,7 +1429,7 @@ public override int GetHashCode() internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { - aSB.Append("null"); + aSB.Append(TOKEN_NULL); } } // End of JSONNull @@ -1213,7 +1439,7 @@ 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 override bool IsNull { get { return true; } } public JSONLazyCreator(JSONNode aNode) { @@ -1230,9 +1456,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; } @@ -1261,9 +1491,7 @@ public override void Add(string aKey, JSONNode aItem) public static bool operator ==(JSONLazyCreator a, object b) { - if (b == null) - return true; - return System.Object.ReferenceEquals(a, b); + return a.Equals(b); } public static bool operator !=(JSONLazyCreator a, object b) @@ -1274,7 +1502,14 @@ 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); } @@ -1305,18 +1540,25 @@ 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())); + } + } + + public override ulong AsULong + { + get + { + Set(new JSONString("0")); + return 0L; + } + set + { + Set(new JSONString(value.ToString())); } } @@ -1335,18 +1577,36 @@ public override JSONObject AsObject { get { return Set(new JSONObject()); } } + internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { - aSB.Append("null"); + aSB.Append(TOKEN_NULL); } } // End of JSONLazyCreator - public static class JSON + 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); +#endif + aResult = null; + return false; + } + } } } 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/SimpleJSONBinary.cs b/Runtime/SimpleJSONBinary.cs similarity index 93% rename from SimpleJSONBinary.cs rename to Runtime/SimpleJSONBinary.cs index df72f13..dc8639a 100644 --- a/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,11 +37,11 @@ * 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; -namespace SimpleJSON +namespace Utilities.SimpleJSON { #if !SimpleJSON_ExcludeBinary public abstract partial class JSONNode @@ -64,16 +64,17 @@ 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)) { SaveToCompressedStream(F); } } + public string SaveToCompressedBase64() { using (var stream = new System.IO.MemoryStream()) @@ -83,7 +84,7 @@ public string SaveToCompressedBase64() return System.Convert.ToBase64String(stream.ToArray()); } } - + #else public void SaveToCompressedStream(System.IO.Stream aData) { @@ -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/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/SimpleJSONDotNetTypes.cs b/Runtime/SimpleJSONDotNetTypes.cs new file mode 100644 index 0000000..1f83b86 --- /dev/null +++ b/Runtime/SimpleJSONDotNetTypes.cs @@ -0,0 +1,593 @@ +#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 Utilities.SimpleJSON +{ + using System.Globalization; + using System.Collections.Generic; + public partial class JSONNode + { + #region Decimal + public virtual decimal AsDecimal + { + get + { + if (!decimal.TryParse(Value, out decimal 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 + { + if (!System.DateTime.TryParse(Value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out System.DateTime result)) + { + result = new System.DateTime(0); + } + return result; + } + set + { + Value = value.ToString("O"); + } + } + + public static implicit operator JSONNode(System.DateTime aDateTime) + { + return new JSONString(aDateTime.ToString("O")); + } + + public static implicit operator System.DateTime(JSONNode aNode) + { + return aNode.AsDateTime; + } + #endregion DateTime + + #region TimeSpan + public virtual System.TimeSpan AsTimeSpan + { + get + { + if (!System.TimeSpan.TryParse(Value, CultureInfo.InvariantCulture, out System.TimeSpan 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.TryParse(Value, out System.Guid 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 (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]); + } + } + } + + 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 (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]); + } + } + } + + 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 (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]); + } + } + } + + 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 (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]); + } + } + } + + 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/Runtime/SimpleJSONSerializer.cs b/Runtime/SimpleJSONSerializer.cs new file mode 100644 index 0000000..585d16f --- /dev/null +++ b/Runtime/SimpleJSONSerializer.cs @@ -0,0 +1,217 @@ +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using UnityEngine; + +namespace Utilities.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; + case Vector3 v3Value: + return v3Value; + case Vector4 v4Value: + return v4Value; + case Quaternion quatValue: + return quatValue; + case Rect rectValue: + return rectValue; + case RectOffset rectOffsetValue: + return rectOffsetValue; + case Matrix4x4 matrixValue: + return matrixValue; + case Color colorValue: + return colorValue; + case Color32 color32Value: + return color32Value; +#endif + + 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)) + { + 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); + } + + 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) + { + return ToJSONNode((IList)list); + } + + public static JSONNode ToJSONNode(this Dictionary dict) + { + return ToJSONNode((IDictionary)dict); + } + + #endregion + } +} 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/SimpleJSONUnity.cs b/Runtime/SimpleJSONUnity.cs similarity index 64% rename from SimpleJSONUnity.cs rename to Runtime/SimpleJSONUnity.cs index 63b42cb..9f201db 100644 --- a/SimpleJSONUnity.cs +++ b/Runtime/SimpleJSONUnity.cs @@ -1,6 +1,7 @@ +#if UNITY_5_3_OR_NEWER #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 +12,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,101 +35,156 @@ * 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 using UnityEngine; -namespace SimpleJSON +namespace Utilities.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; + public static JSONContainerType RectContainerType = JSONContainerType.Object; + public static JSONContainerType ColorContainerType = JSONContainerType.Object; private static JSONNode GetContainer(JSONContainerType aType) { if (aType == JSONContainerType.Array) + { return new JSONArray(); + } return new JSONObject(); } #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; } + 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(); } + 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(); } + + 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 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; } + public Vector2 ReadVector2(string aXName, string aYName) { if (IsObject) @@ -142,8 +198,11 @@ public Vector2 ReadVector2() { return ReadVector2(Vector2.zero); } + public JSONNode WriteVector2(Vector2 aVec, string aXName = "x", string aYName = "y") { + Clear(); + if (IsObject) { Inline = true; @@ -153,8 +212,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; } @@ -164,23 +226,34 @@ 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; } + public Vector3 ReadVector3() { return ReadVector3(Vector3.zero); } + public JSONNode WriteVector3(Vector3 aVec, string aXName = "x", string aYName = "y", string aZName = "z") { + Clear(); + if (IsObject) { Inline = true; @@ -191,9 +264,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; } @@ -203,17 +277,25 @@ 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; } + public Vector4 ReadVector4() { return ReadVector4(Vector4.zero); } + public JSONNode WriteVector4(Vector4 aVec) { + Clear(); + if (IsObject) { Inline = true; @@ -225,10 +307,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; } @@ -238,17 +321,25 @@ 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; } + public Quaternion ReadQuaternion() { return ReadQuaternion(Quaternion.identity); } + public JSONNode WriteQuaternion(Quaternion aRot) { + Clear(); + if (IsObject) { Inline = true; @@ -260,10 +351,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; } @@ -273,17 +365,25 @@ 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; } + public Rect ReadRect() { return ReadRect(new Rect()); } + public JSONNode WriteRect(Rect aRect) { + Clear(); + if (IsObject) { Inline = true; @@ -295,10 +395,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; } @@ -308,17 +408,25 @@ 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; } + public RectOffset ReadRectOffset() { return ReadRectOffset(new RectOffset()); } + public JSONNode WriteRectOffset(RectOffset aRect) { + Clear(); + if (IsObject) { Inline = true; @@ -330,10 +438,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; } @@ -352,18 +460,166 @@ public Matrix4x4 ReadMatrix() } return result; } + public JSONNode WriteMatrix(Matrix4x4 aMatrix) { + Clear(); + if (IsArray) { Inline = true; for (int i = 0; i < 16; i++) { - this[i].AsFloat = aMatrix[i]; + Add(aMatrix[i]); } } 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) + { + Clear(); + + 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) + { + Clear(); + + 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 + + #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() + { + 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(); + + if (IsObject) + { + this["position"] = aPose.position; + this["rotation"] = aPose.rotation; + } + else if (IsArray) + { + Add(aPose.position); + Add(aPose.rotation); + } + + return this; + } + + #endregion Pose } } +#endif 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 b/Runtime/Unity.SimpleJSON.asmdef new file mode 100644 index 0000000..3d889a1 --- /dev/null +++ b/Runtime/Unity.SimpleJSON.asmdef @@ -0,0 +1,12 @@ +{ + "name": "Unity.SimpleJSON", + "references": [], + "optionalUnityReferences": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +} \ No newline at end of file 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/SimpleJSONDotNetTests.cs b/Tests/Editor/SimpleJSONDotNetTests.cs new file mode 100644 index 0000000..123f4f1 --- /dev/null +++ b/Tests/Editor/SimpleJSONDotNetTests.cs @@ -0,0 +1,464 @@ +using NUnit.Framework; +using Utilities.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: diff --git a/Tests/Editor/SimpleJSONTests.cs b/Tests/Editor/SimpleJSONTests.cs new file mode 100644 index 0000000..293aa3f --- /dev/null +++ b/Tests/Editor/SimpleJSONTests.cs @@ -0,0 +1,489 @@ +using System.Collections.Generic; +using NUnit.Framework; +using Utilities.SimpleJSON; +using UnityEngine; + +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, 5.12345678], " + + "\"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"].AsObject; + + 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).AsArray; + + for (int i = 0; i < jsonArray.Count; i++) + { + Assert.AreEqual(jsonArray[i], parsedJSON["array"][i]); + + 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).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] + 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); + } + + [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")); + } + + [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); + }); + } + + [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/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..64710d9 --- /dev/null +++ b/Tests/Editor/SimpleJSONUnityTests.cs @@ -0,0 +1,229 @@ +using NUnit.Framework; +using Utilities.SimpleJSON; +using UnityEngine; + +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(); + + Assert.AreEqual(vec2, deserializedObject); + Assert.AreEqual(vec2, deserializedArray); + } + + [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(); + + Assert.AreEqual(vec3, deserializedObject); + Assert.AreEqual(vec3, deserializedArray); + } + + [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(); + + Assert.AreEqual(vec4, deserializedObject); + Assert.AreEqual(vec4, deserializedArray); + } + + [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(); + + Assert.AreEqual(quat, deserializedObject); + 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() + { + 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, deserializedObject); + Assert.AreEqual(rect, deserializedArray); + } + + [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); + } + + [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() + { + 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}"); + + var deserializedObject = JSON.Parse(jsonObjectString).ReadColor(); + var deserializedArray = JSON.Parse(jsonArrayString).ReadColor(); + + Assert.AreEqual(color, deserializedObject); + Assert.AreEqual(color, deserializedArray); + Assert.AreEqual(deserializedObject, 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); + Assert.AreEqual(deserializedObject, deserializedArray); + Assert.AreEqual(deserializedString, deserializedArray); + } + } +} 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..3f2234c --- /dev/null +++ b/Tests/Editor/Unity.SimpleJSON.Editor.Tests.asmdef @@ -0,0 +1,23 @@ +{ + "name": "Unity.SimpleJSON.Editor.Tests", + "references": [ + "Unity.SimpleJSON", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [], + "noEngineReferences": false +} \ 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 b/package.json new file mode 100755 index 0000000..6a22646 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "com.github.bunny83.simplejson", + "version": "1.0.2", + "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" +} 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: