123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794 |
- /* INFINITY CODE 2013-2019 */
- /* http://www.infinity-code.com */
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Reflection;
- using System.Text;
- using InfinityCode.RealWorldTerrain.Utils;
- using UnityEngine;
- namespace InfinityCode.RealWorldTerrain.JSON
- {
- /// <summary>
- /// Class for working with JSON. It is used for parsing of string, serialization and deserialization of object.
- /// </summary>
- public class RealWorldTerrainJson
- {
- private string json;
- private int index = 0;
- private Token lookAheadToken = Token.None;
- private StringBuilder s;
- private int length;
- protected RealWorldTerrainJson(string json)
- {
- s = new StringBuilder();
- this.json = json;
- length = json.Length;
- }
- /// <summary>
- /// Deserialize string into object.
- /// </summary>
- /// <typeparam name="T">Type</typeparam>
- /// <param name="json">JSON string</param>
- /// <returns>Deserialized object</returns>
- public static T Deserialize<T>(string json)
- {
- object obj = ParseDirect(json);
- if (obj is IDictionary) return (T) DeserializeObject(typeof(T), obj as Dictionary<string, object>);
- if (obj is IList) return (T) DeserializeArray(typeof(T), obj as List<object>);
- return (T) DeserializeValue(typeof(T), obj);
- }
- private static object DeserializeValue(Type type, object obj)
- {
- if (obj == null) return null;
- try
- {
- return Convert.ChangeType(obj, type);
- }
- catch (Exception exception)
- {
- Debug.Log(exception.Message + "\n" + exception.StackTrace);
- }
- return null;
- }
- private static object DeserializeArray(Type type, List<object> list)
- {
- if (list == null || list.Count == 0) return null;
- if (type.IsArray)
- {
- Type elementType = type.GetElementType();
- Array v = Array.CreateInstance(elementType, list.Count);
- for (int i = 0; i < list.Count; i++)
- {
- object child = list[i];
- object item;
- if (child is IDictionary) item = DeserializeObject(elementType, child as Dictionary<string, object>);
- else if (child is IList) item = DeserializeArray(elementType, child as List<object>);
- else item = DeserializeValue(elementType, child);
- v.SetValue(item, i);
- }
- return v;
- }
- if (RealWorldTerrainReflectionHelper.IsGenericType(type))
- {
- Type listType = RealWorldTerrainReflectionHelper.GetGenericArguments(type)[0];
- object v = Activator.CreateInstance(type);
- for (int i = 0; i < list.Count; i++)
- {
- object child = list[i];
- object item;
- if (child is IDictionary) item = DeserializeObject(listType, child as Dictionary<string, object>);
- else if (child is IList) item = DeserializeArray(listType, child as List<object>);
- else item = DeserializeValue(listType, child);
- try
- {
- MethodInfo methodInfo = RealWorldTerrainReflectionHelper.GetMethod(type, "Add");
- if (methodInfo != null) methodInfo.Invoke(v, new[] {item});
- }
- catch
- {
- }
- }
- return v;
- }
- return null;
- }
- private static object DeserializeObject(Type type, Dictionary<string, object> table)
- {
- IEnumerable<MemberInfo> members = RealWorldTerrainReflectionHelper.GetMembers(type, BindingFlags.Instance | BindingFlags.Public);
- object v = Activator.CreateInstance(type);
- foreach (MemberInfo member in members)
- {
- #if !NETFX_CORE
- MemberTypes memberType = member.MemberType;
- if (memberType != MemberTypes.Field && memberType != MemberTypes.Property) continue;
- #else
- MemberTypes memberType;
- if (member is PropertyInfo) memberType = MemberTypes.Property;
- else if (member is FieldInfo) memberType = MemberTypes.Field;
- else continue;
- #endif
- if (memberType == MemberTypes.Property && !((PropertyInfo) member).CanWrite) continue;
- object item;
- #if !NETFX_CORE
- object[] attributes = member.GetCustomAttributes(typeof(AliasAttribute), true);
- AliasAttribute alias = attributes.Length > 0 ? attributes[0] as AliasAttribute : null;
- #else
- IEnumerable<Attribute> attributes = member.GetCustomAttributes(typeof(AliasAttribute), true);
- AliasAttribute alias = null;
- foreach (Attribute a in attributes)
- {
- alias = a as AliasAttribute;
- break;
- }
- #endif
- if (alias == null || !alias.ignoreFieldName)
- {
- if (table.TryGetValue(member.Name, out item))
- {
- DeserializeValue(memberType, member, item, v);
- continue;
- }
- }
- if (alias != null)
- {
- for (int j = 0; j < alias.aliases.Length; j++)
- {
- if (table.TryGetValue(alias.aliases[j], out item))
- {
- DeserializeValue(memberType, member, item, v);
- break;
- }
- }
- }
- }
- return v;
- }
- private static void DeserializeValue(MemberTypes memberType, MemberInfo member, object item, object v)
- {
- object cv;
- Type t = memberType == MemberTypes.Field ? ((FieldInfo) member).FieldType : ((PropertyInfo) member).PropertyType;
- if (t == typeof(System.Object)) cv = item;
- else if (item is IDictionary) cv = DeserializeObject(t, item as Dictionary<string, object>);
- else if (item is IList) cv = DeserializeArray(t, item as List<object>);
- else cv = DeserializeValue(t, item);
- if (memberType == MemberTypes.Field) ((FieldInfo) member).SetValue(v, cv);
- else ((PropertyInfo) member).SetValue(v, cv, null);
- }
- private Token LookAhead()
- {
- if (lookAheadToken != Token.None) return lookAheadToken;
- return lookAheadToken = NextTokenCore();
- }
- private Token NextToken()
- {
- Token result = lookAheadToken != Token.None ? lookAheadToken : NextTokenCore();
- lookAheadToken = Token.None;
- return result;
- }
- private Token NextTokenCore()
- {
- char c;
- do
- {
- c = json[index];
- if (c == '/' && json[index + 1] == '/')
- {
- index += 2;
- do
- {
- c = json[index];
- if (c == '\r' || c == '\n') break;
- } while (++index < length);
- }
- if (c > ' ') break;
- if (c != ' ' && c != '\t' && c != '\n' && c != '\r') break;
- } while (++index < length);
- if (index == length) throw new Exception("Reached end of string unexpectedly");
- c = json[index];
- index++;
- switch (c)
- {
- case '{':
- return Token.Curly_Open;
- case '}':
- return Token.Curly_Close;
- case '[':
- return Token.Squared_Open;
- case ']':
- return Token.Squared_Close;
- case ',':
- return Token.Comma;
- case '"':
- return Token.String;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '-':
- case '+':
- case '.':
- return Token.Number;
- case ':':
- return Token.Colon;
- case 'f':
- if (length - index >= 4 &&
- json[index + 0] == 'a' &&
- json[index + 1] == 'l' &&
- json[index + 2] == 's' &&
- json[index + 3] == 'e')
- {
- index += 4;
- return Token.False;
- }
- break;
- case 't':
- if (length - index >= 3 &&
- json[index + 0] == 'r' &&
- json[index + 1] == 'u' &&
- json[index + 2] == 'e')
- {
- index += 3;
- return Token.True;
- }
- break;
- case 'n':
- if (length - index >= 3 &&
- json[index + 0] == 'u' &&
- json[index + 1] == 'l' &&
- json[index + 2] == 'l')
- {
- index += 3;
- return Token.Null;
- }
- break;
- }
- throw new Exception("Could not find token at index " + --index);
- }
- /// <summary>
- /// Parse JSON string into RealWorldTerrainJsonItem
- /// </summary>
- /// <param name="json">JSON string</param>
- /// <returns>Root object</returns>
- public static RealWorldTerrainJsonItem Parse(string json)
- {
- RealWorldTerrainJson instance = new RealWorldTerrainJson(json);
- return instance.ParseValue();
- }
- /// <summary>
- /// Parse JSON string into Dictonary, List and Object
- /// </summary>
- /// <param name="json">JSON string</param>
- /// <returns>Root object</returns>
- public static object ParseDirect(string json)
- {
- RealWorldTerrainJson instance = new RealWorldTerrainJson(json);
- return instance.ParseValueDirect();
- }
- private RealWorldTerrainJsonArray ParseArray()
- {
- RealWorldTerrainJsonArray array = new RealWorldTerrainJsonArray();
- lookAheadToken = Token.None;
- while (true)
- {
- switch (LookAhead())
- {
- case Token.Comma:
- lookAheadToken = Token.None;
- break;
- case Token.Squared_Close:
- lookAheadToken = Token.None;
- return array;
- default:
- array.Add(ParseValue());
- break;
- }
- }
- }
- private List<object> ParseArrayDirect()
- {
- List<object> array = new List<object>();
- lookAheadToken = Token.None;
- while (true)
- {
- switch (LookAhead())
- {
- case Token.Comma:
- lookAheadToken = Token.None;
- break;
- case Token.Squared_Close:
- lookAheadToken = Token.None;
- return array;
- default:
- array.Add(ParseValueDirect());
- break;
- }
- }
- }
- private object ParseNumber()
- {
- lookAheadToken = Token.None;
- index--;
- long n = 0;
- bool neg = false;
- long decimalV = 0;
- long exp = 0;
- bool negExp = false;
- while (index < length)
- {
- char c = json[index];
- if (c >= '0' && c <= '9')
- {
- n = n * 10 + (c - '0');
- decimalV *= 10;
- }
- else if (c == '.')
- {
- decimalV = 1;
- }
- else if (c == '-') neg = true;
- else if (c == '+') neg = false;
- else if (c == 'e' || c == 'E')
- {
- if (decimalV == 0) decimalV = 1;
- index++;
- exp = 0;
- while (index < length)
- {
- c = json[index];
- if (c >= '0' && c <= '9') exp = exp * 10 + (c - '0');
- else if (c == '-') negExp = true;
- else if (c == '+') negExp = false;
- else break;
- index++;
- }
- break;
- }
- else break;
- index++;
- }
- if (neg) n = -n;
- if (decimalV != 0)
- {
- double v = n / (double) decimalV;
- if (exp > 0)
- {
- if (negExp) v /= Math.Pow(10, exp);
- else v *= Math.Pow(10, exp);
- }
- return v;
- }
- return n;
- }
- private RealWorldTerrainJsonObject ParseObject()
- {
- RealWorldTerrainJsonObject obj = new RealWorldTerrainJsonObject();
- lookAheadToken = Token.None;
- while (true)
- {
- switch (LookAhead())
- {
- case Token.Comma:
- lookAheadToken = Token.None;
- break;
- case Token.Curly_Close:
- lookAheadToken = Token.None;
- return obj;
- default:
- {
- string name = ParseString();
- if (NextToken() != Token.Colon) throw new Exception("Expected colon at index " + index);
- obj.Add(name, ParseValue());
- }
- break;
- }
- }
- }
- private Dictionary<string, object> ParseObjectDirect()
- {
- Dictionary<string, object> obj = new Dictionary<string, object>();
- lookAheadToken = Token.None;
- while (true)
- {
- switch (LookAhead())
- {
- case Token.Comma:
- lookAheadToken = Token.None;
- break;
- case Token.Curly_Close:
- lookAheadToken = Token.None;
- return obj;
- default:
- {
- string name = ParseString();
- if (NextToken() != Token.Colon) throw new Exception("Expected colon at index " + index);
- obj.Add(name, ParseValueDirect());
- }
- break;
- }
- }
- }
- private uint ParseSingleChar(char c1, uint multipliyer)
- {
- uint p1 = 0;
- if (c1 >= '0' && c1 <= '9') p1 = (uint) (c1 - '0') * multipliyer;
- else if (c1 >= 'A' && c1 <= 'F') p1 = (uint) (c1 - 'A' + 10) * multipliyer;
- else if (c1 >= 'a' && c1 <= 'f') p1 = (uint) (c1 - 'a' + 10) * multipliyer;
- return p1;
- }
- private string ParseString()
- {
- lookAheadToken = Token.None;
- s.Length = 0;
- int runIndex = -1;
- int l = length;
- string p = json;
- {
- while (index < l)
- {
- char c = p[index++];
- if (c == '"')
- {
- if (runIndex != -1)
- {
- if (s.Length == 0) return p.Substring(runIndex, index - runIndex - 1);
- s.Append(p, runIndex, index - runIndex - 1);
- }
- return s.ToString();
- }
- if (c != '\\')
- {
- if (runIndex == -1) runIndex = index - 1;
- continue;
- }
- if (index == l) break;
- if (runIndex != -1)
- {
- s.Append(p, runIndex, index - runIndex - 1);
- runIndex = -1;
- }
- switch (p[index++])
- {
- case '"':
- s.Append('"');
- break;
- case '\\':
- s.Append('\\');
- break;
- case '/':
- s.Append('/');
- break;
- case 'b':
- s.Append('\b');
- break;
- case 'f':
- s.Append('\f');
- break;
- case 'n':
- s.Append('\n');
- break;
- case 'r':
- s.Append('\r');
- break;
- case 't':
- s.Append('\t');
- break;
- case 'u':
- {
- int remainingLength = l - index;
- if (remainingLength < 4) break;
- uint codePoint = ParseUnicode(p[index], p[index + 1], p[index + 2], p[index + 3]);
- s.Append((char) codePoint);
- index += 4;
- }
- break;
- }
- }
- }
- throw new Exception("Unexpectedly reached end of string");
- }
- private uint ParseUnicode(char c1, char c2, char c3, char c4)
- {
- uint p1 = ParseSingleChar(c1, 0x1000);
- uint p2 = ParseSingleChar(c2, 0x100);
- uint p3 = ParseSingleChar(c3, 0x10);
- uint p4 = ParseSingleChar(c4, 1);
- return p1 + p2 + p3 + p4;
- }
- private RealWorldTerrainJsonItem ParseValue()
- {
- switch (LookAhead())
- {
- case Token.Number:
- object number = ParseNumber();
- return new RealWorldTerrainJsonValue(number, number is double ? RealWorldTerrainJsonValue.ValueType.DOUBLE : RealWorldTerrainJsonValue.ValueType.LONG);
- case Token.String:
- return new RealWorldTerrainJsonValue(ParseString(), RealWorldTerrainJsonValue.ValueType.STRING);
- case Token.Curly_Open:
- return ParseObject();
- case Token.Squared_Open:
- return ParseArray();
- case Token.True:
- lookAheadToken = Token.None;
- return new RealWorldTerrainJsonValue(true, RealWorldTerrainJsonValue.ValueType.BOOLEAN);
- case Token.False:
- lookAheadToken = Token.None;
- return new RealWorldTerrainJsonValue(false, RealWorldTerrainJsonValue.ValueType.BOOLEAN);
- case Token.Null:
- lookAheadToken = Token.None;
- return new RealWorldTerrainJsonValue(null, RealWorldTerrainJsonValue.ValueType.NULL);
- }
- throw new Exception("Unrecognized token at index" + index);
- }
- private object ParseValueDirect()
- {
- switch (LookAhead())
- {
- case Token.Number:
- return ParseNumber();
- case Token.String:
- return ParseString();
- case Token.Curly_Open:
- return ParseObjectDirect();
- case Token.Squared_Open:
- return ParseArrayDirect();
- case Token.True:
- lookAheadToken = Token.None;
- return true;
- case Token.False:
- lookAheadToken = Token.None;
- return false;
- case Token.Null:
- lookAheadToken = Token.None;
- return null;
- }
- throw new Exception("Unrecognized token at index" + index);
- }
- /// <summary>
- /// Serializes an object to JSON.
- /// </summary>
- /// <param name="obj">Object</param>
- /// <param name="bindingFlags">A bitmask comprised of one or more BindingFlags that specify how the search is conducted.</param>
- /// <returns>JSON</returns>
- public static RealWorldTerrainJsonItem Serialize(object obj, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public)
- {
- #if !UNITY_WP_8_1 || UNITY_EDITOR
- if (obj == null || obj is DBNull) return new RealWorldTerrainJsonValue(obj, RealWorldTerrainJsonValue.ValueType.NULL);
- #else
- if (obj == null) return new RealWorldTerrainJsonValue(obj, RealWorldTerrainJsonValue.ValueType.NULL);
- #endif
- if (obj is string || obj is bool || obj is int || obj is long || obj is short || obj is float || obj is double) return new RealWorldTerrainJsonValue(obj);
- if (obj is UnityEngine.Object)
- {
- if (!(obj is Component || obj is ScriptableObject)) return new RealWorldTerrainJsonValue((obj as UnityEngine.Object).GetInstanceID());
- }
- if (obj is IDictionary)
- {
- IDictionary d = obj as IDictionary;
- RealWorldTerrainJsonObject dv = new RealWorldTerrainJsonObject();
- ICollection keys = d.Keys;
- ICollection values = d.Values;
- IEnumerator keysEnum = keys.GetEnumerator();
- IEnumerator valuesEnum = values.GetEnumerator();
- while (keysEnum.MoveNext() && valuesEnum.MoveNext())
- {
- object k = keysEnum.Current;
- object v = valuesEnum.Current;
- dv.Add(k as string, Serialize(v, bindingFlags));
- }
- return dv;
- }
- if (obj is IEnumerable)
- {
- IEnumerable v = (IEnumerable) obj;
- RealWorldTerrainJsonArray array = new RealWorldTerrainJsonArray();
- foreach (var item in v) array.Add(Serialize(item, bindingFlags));
- return array;
- }
- RealWorldTerrainJsonObject o = new RealWorldTerrainJsonObject();
- Type type = obj.GetType();
- if (RealWorldTerrainReflectionHelper.CheckIfAnonymousType(type)) bindingFlags |= BindingFlags.NonPublic;
- IEnumerable<FieldInfo> fields = RealWorldTerrainReflectionHelper.GetFields(type, bindingFlags);
- foreach (FieldInfo field in fields)
- {
- string fieldName = field.Name;
- if (field.Attributes == (FieldAttributes.Private | FieldAttributes.InitOnly))
- {
- int startIndex = fieldName.IndexOf('<') + 1;
- int endIndex = fieldName.IndexOf('>', startIndex);
- if (endIndex != -1 && startIndex != -1) fieldName = fieldName.Substring(startIndex, endIndex - startIndex);
- else fieldName = fieldName.Trim('<', '>');
- }
- o.Add(fieldName, Serialize(field.GetValue(obj)));
- }
- return o;
- }
- private enum Token
- {
- None = -1,
- Curly_Open,
- Curly_Close,
- Squared_Open,
- Squared_Close,
- Colon,
- Comma,
- String,
- Number,
- True,
- False,
- Null
- }
- /// <summary>
- /// Alias of field used during deserialization.
- /// </summary>
- public class AliasAttribute : Attribute
- {
- /// <summary>
- /// Aliases
- /// </summary>
- public readonly string[] aliases;
- /// <summary>
- /// If true, the original field name will be ignored.
- /// </summary>
- public readonly bool ignoreFieldName;
- /// <summary>
- /// Constructor
- /// </summary>
- /// <param name="ignoreFieldName">If true, the original field name will be ignored.</param>
- /// <param name="aliases">Aliases</param>
- public AliasAttribute(bool ignoreFieldName, params string[] aliases)
- {
- if (aliases == null || aliases.Length == 0) throw new Exception("You must use at least one alias.");
- this.ignoreFieldName = ignoreFieldName;
- this.aliases = aliases;
- }
- /// <summary>
- /// Constructor
- /// </summary>
- /// <param name="aliases">Aliases</param>
- public AliasAttribute(params string[] aliases) : this(false, aliases)
- {
- }
- }
- }
- }
|