1329 lines
No EOL
26 KiB
C#
1329 lines
No EOL
26 KiB
C#
//#define USE_SharpZipLib
|
|
#if !UNITY_WEBPLAYER
|
|
#define USE_FileIO
|
|
#endif
|
|
/* * * * *
|
|
* 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.
|
|
*
|
|
* 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
|
|
*
|
|
* Written by Bunny83
|
|
* 2012-06-09
|
|
*
|
|
* Modified by oPless, 2014-09-21 to round-trip properly
|
|
*
|
|
* Features / attributes:
|
|
* - provides strongly typed node classes and lists / dictionaries
|
|
* - provides easy access to class members / array items / data values
|
|
* - the parser ignores data types. Each value is a string.
|
|
* - only double quotes (") are used for quoting strings.
|
|
* - values and names are not restricted to quoted strings. They simply add up and are trimmed.
|
|
* - There are only 3 types: arrays(JSONArray), objects(JSONClass) and values(JSONData)
|
|
* - provides "casting" properties to easily convert to / from those types:
|
|
* int / float / double / bool
|
|
* - provides a common interface for each node so no explicit casting is required.
|
|
* - the parser try to avoid errors, but if malformed JSON is parsed the result is undefined
|
|
*
|
|
*
|
|
* 2012-12-17 Update:
|
|
* - Added internal JSONLazyCreator class which simplifies the construction of a JSON tree
|
|
* Now you can simple reference any item that doesn't exist yet and it will return a JSONLazyCreator
|
|
* The class determines the required type by it's further use, creates the type and removes itself.
|
|
* - Added binary serialization / deserialization.
|
|
* - Added support for BZip2 zipped binary format. Requires the SharpZipLib ( http://www.icsharpcode.net/opensource/sharpziplib/ )
|
|
* The usage of the SharpZipLib library can be disabled by removing or commenting out the USE_SharpZipLib define at the top
|
|
* - The serializer uses different types when it comes to store the values. Since my data values
|
|
* are all of type string, the serializer will "try" which format fits best. The order is: int, float, double, bool, string.
|
|
* It's not the most efficient way but for a moderate amount of data it should work on all platforms.
|
|
*
|
|
* * * * */
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
|
|
namespace SimpleJSON
|
|
{
|
|
public enum JSONBinaryTag
|
|
{
|
|
Array = 1,
|
|
Class = 2,
|
|
Value = 3,
|
|
IntValue = 4,
|
|
DoubleValue = 5,
|
|
BoolValue = 6,
|
|
FloatValue = 7,
|
|
}
|
|
|
|
public abstract class JSONNode
|
|
{
|
|
#region common interface
|
|
|
|
public virtual void Add(string aKey, JSONNode aItem)
|
|
{
|
|
}
|
|
|
|
public virtual JSONNode this[int aIndex] { get { return null; } set { } }
|
|
|
|
public virtual JSONNode this[string aKey] { get { return null; } set { } }
|
|
|
|
public virtual string Value { get { return ""; } set { } }
|
|
|
|
public virtual int Count { get { return 0; } }
|
|
|
|
public virtual void Add(JSONNode aItem)
|
|
{
|
|
Add("", aItem);
|
|
}
|
|
|
|
public virtual JSONNode Remove(string aKey)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public virtual JSONNode Remove(int aIndex)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public virtual JSONNode Remove(JSONNode aNode)
|
|
{
|
|
return aNode;
|
|
}
|
|
|
|
public virtual IEnumerable<JSONNode> Children
|
|
{
|
|
get
|
|
{
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
public IEnumerable<JSONNode> DeepChildren
|
|
{
|
|
get
|
|
{
|
|
foreach (var C in Children)
|
|
foreach (var D in C.DeepChildren)
|
|
yield return D;
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return "JSONNode";
|
|
}
|
|
|
|
public virtual string ToString(string aPrefix)
|
|
{
|
|
return "JSONNode";
|
|
}
|
|
|
|
public abstract string ToJSON(int prefix);
|
|
|
|
#endregion common interface
|
|
|
|
#region typecasting properties
|
|
|
|
public virtual JSONBinaryTag Tag { get; set; }
|
|
|
|
public virtual int AsInt
|
|
{
|
|
get
|
|
{
|
|
int v = 0;
|
|
if (int.TryParse(Value, out v))
|
|
return v;
|
|
return 0;
|
|
}
|
|
set
|
|
{
|
|
Value = value.ToString();
|
|
Tag = JSONBinaryTag.IntValue;
|
|
}
|
|
}
|
|
|
|
public virtual float AsFloat
|
|
{
|
|
get
|
|
{
|
|
float v = 0.0f;
|
|
if (float.TryParse(Value, out v))
|
|
return v;
|
|
return 0.0f;
|
|
}
|
|
set
|
|
{
|
|
Value = value.ToString();
|
|
Tag = JSONBinaryTag.FloatValue;
|
|
}
|
|
}
|
|
|
|
public virtual double AsDouble
|
|
{
|
|
get
|
|
{
|
|
double v = 0.0;
|
|
if (double.TryParse(Value, out v))
|
|
return v;
|
|
return 0.0;
|
|
}
|
|
set
|
|
{
|
|
Value = value.ToString();
|
|
Tag = JSONBinaryTag.DoubleValue;
|
|
|
|
}
|
|
}
|
|
|
|
public virtual bool AsBool
|
|
{
|
|
get
|
|
{
|
|
bool v = false;
|
|
if (bool.TryParse(Value, out v))
|
|
return v;
|
|
return !string.IsNullOrEmpty(Value);
|
|
}
|
|
set
|
|
{
|
|
Value = (value) ? "true" : "false";
|
|
Tag = JSONBinaryTag.BoolValue;
|
|
|
|
}
|
|
}
|
|
|
|
public virtual JSONArray AsArray
|
|
{
|
|
get
|
|
{
|
|
return this as JSONArray;
|
|
}
|
|
}
|
|
|
|
public virtual JSONClass AsObject
|
|
{
|
|
get
|
|
{
|
|
return this as JSONClass;
|
|
}
|
|
}
|
|
|
|
|
|
#endregion typecasting properties
|
|
|
|
#region operators
|
|
|
|
public static implicit operator JSONNode(string s)
|
|
{
|
|
return new JSONData(s);
|
|
}
|
|
|
|
public static implicit operator string(JSONNode d)
|
|
{
|
|
return (d == null) ? null : d.Value;
|
|
}
|
|
|
|
public static bool operator ==(JSONNode a, object b)
|
|
{
|
|
if (b == null && a is JSONLazyCreator)
|
|
return true;
|
|
return System.Object.ReferenceEquals(a, b);
|
|
}
|
|
|
|
public static bool operator !=(JSONNode a, object b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
return System.Object.ReferenceEquals(this, obj);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return base.GetHashCode();
|
|
}
|
|
|
|
|
|
#endregion operators
|
|
|
|
internal static string Escape(string aText)
|
|
{
|
|
string result = "";
|
|
foreach (char c in aText)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '\\':
|
|
result += "\\\\";
|
|
break;
|
|
case '\"':
|
|
result += "\\\"";
|
|
break;
|
|
case '\n':
|
|
result += "\\n";
|
|
break;
|
|
case '\r':
|
|
result += "\\r";
|
|
break;
|
|
case '\t':
|
|
result += "\\t";
|
|
break;
|
|
case '\b':
|
|
result += "\\b";
|
|
break;
|
|
case '\f':
|
|
result += "\\f";
|
|
break;
|
|
default:
|
|
result += c;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static JSONData Numberize(string token)
|
|
{
|
|
bool flag = false;
|
|
int integer = 0;
|
|
double real = 0;
|
|
|
|
if (int.TryParse(token, out integer))
|
|
{
|
|
return new JSONData(integer);
|
|
}
|
|
|
|
if (double.TryParse(token, out real))
|
|
{
|
|
return new JSONData(real);
|
|
}
|
|
|
|
if (bool.TryParse(token, out flag))
|
|
{
|
|
return new JSONData(flag);
|
|
}
|
|
|
|
throw new NotImplementedException(token);
|
|
}
|
|
|
|
static void AddElement(JSONNode ctx, string token, string tokenName, bool tokenIsString)
|
|
{
|
|
if (tokenIsString)
|
|
{
|
|
if (ctx is JSONArray)
|
|
ctx.Add(token);
|
|
else
|
|
ctx.Add(tokenName, token); // assume dictionary/object
|
|
}
|
|
else
|
|
{
|
|
JSONData number = Numberize(token);
|
|
if (ctx is JSONArray)
|
|
ctx.Add(number);
|
|
else
|
|
ctx.Add(tokenName, number);
|
|
|
|
}
|
|
}
|
|
|
|
public static JSONNode Parse(string aJSON)
|
|
{
|
|
Stack<JSONNode> stack = new Stack<JSONNode>();
|
|
JSONNode ctx = null;
|
|
int i = 0;
|
|
string Token = "";
|
|
string TokenName = "";
|
|
bool QuoteMode = false;
|
|
bool TokenIsString = false;
|
|
while (i < aJSON.Length)
|
|
{
|
|
switch (aJSON[i])
|
|
{
|
|
case '{':
|
|
if (QuoteMode)
|
|
{
|
|
Token += aJSON[i];
|
|
break;
|
|
}
|
|
stack.Push(new JSONClass());
|
|
if (ctx != null)
|
|
{
|
|
TokenName = TokenName.Trim();
|
|
if (ctx is JSONArray)
|
|
ctx.Add(stack.Peek());
|
|
else if (TokenName != "")
|
|
ctx.Add(TokenName, stack.Peek());
|
|
}
|
|
TokenName = "";
|
|
Token = "";
|
|
ctx = stack.Peek();
|
|
break;
|
|
|
|
case '[':
|
|
if (QuoteMode)
|
|
{
|
|
Token += aJSON[i];
|
|
break;
|
|
}
|
|
|
|
stack.Push(new JSONArray());
|
|
if (ctx != null)
|
|
{
|
|
TokenName = TokenName.Trim();
|
|
|
|
if (ctx is JSONArray)
|
|
ctx.Add(stack.Peek());
|
|
else if (TokenName != "")
|
|
ctx.Add(TokenName, stack.Peek());
|
|
}
|
|
TokenName = "";
|
|
Token = "";
|
|
ctx = stack.Peek();
|
|
break;
|
|
|
|
case '}':
|
|
case ']':
|
|
if (QuoteMode)
|
|
{
|
|
Token += aJSON[i];
|
|
break;
|
|
}
|
|
if (stack.Count == 0)
|
|
throw new Exception("JSON Parse: Too many closing brackets");
|
|
|
|
stack.Pop();
|
|
if (Token != "")
|
|
{
|
|
TokenName = TokenName.Trim();
|
|
/*
|
|
if (ctx is JSONArray)
|
|
ctx.Add (Token);
|
|
else if (TokenName != "")
|
|
ctx.Add (TokenName, Token);
|
|
*/
|
|
AddElement(ctx, Token, TokenName, TokenIsString);
|
|
TokenIsString = false;
|
|
}
|
|
TokenName = "";
|
|
Token = "";
|
|
if (stack.Count > 0)
|
|
ctx = stack.Peek();
|
|
break;
|
|
|
|
case ':':
|
|
if (QuoteMode)
|
|
{
|
|
Token += aJSON[i];
|
|
break;
|
|
}
|
|
TokenName = Token;
|
|
Token = "";
|
|
TokenIsString = false;
|
|
break;
|
|
|
|
case '"':
|
|
QuoteMode ^= true;
|
|
TokenIsString = QuoteMode == true ? true : TokenIsString;
|
|
break;
|
|
|
|
case ',':
|
|
if (QuoteMode)
|
|
{
|
|
Token += aJSON[i];
|
|
break;
|
|
}
|
|
if (Token != "")
|
|
{
|
|
/*
|
|
if (ctx is JSONArray) {
|
|
ctx.Add (Token);
|
|
} else if (TokenName != "") {
|
|
ctx.Add (TokenName, Token);
|
|
}
|
|
*/
|
|
AddElement(ctx, Token, TokenName, TokenIsString);
|
|
TokenIsString = false;
|
|
|
|
}
|
|
TokenName = "";
|
|
Token = "";
|
|
TokenIsString = false;
|
|
break;
|
|
|
|
case '\r':
|
|
case '\n':
|
|
break;
|
|
|
|
case ' ':
|
|
case '\t':
|
|
if (QuoteMode)
|
|
Token += aJSON[i];
|
|
break;
|
|
|
|
case '\\':
|
|
++i;
|
|
if (QuoteMode)
|
|
{
|
|
char C = aJSON[i];
|
|
switch (C)
|
|
{
|
|
case 't':
|
|
Token += '\t';
|
|
break;
|
|
case 'r':
|
|
Token += '\r';
|
|
break;
|
|
case 'n':
|
|
Token += '\n';
|
|
break;
|
|
case 'b':
|
|
Token += '\b';
|
|
break;
|
|
case 'f':
|
|
Token += '\f';
|
|
break;
|
|
case 'u':
|
|
{
|
|
string s = aJSON.Substring(i + 1, 4);
|
|
Token += (char)int.Parse(
|
|
s,
|
|
System.Globalization.NumberStyles.AllowHexSpecifier);
|
|
i += 4;
|
|
break;
|
|
}
|
|
default:
|
|
Token += C;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Token += aJSON[i];
|
|
break;
|
|
}
|
|
++i;
|
|
}
|
|
if (QuoteMode)
|
|
{
|
|
throw new Exception("JSON Parse: Quotation marks seems to be messed up.");
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
public virtual void Serialize(System.IO.BinaryWriter aWriter)
|
|
{
|
|
}
|
|
|
|
public void SaveToStream(System.IO.Stream aData)
|
|
{
|
|
var W = new System.IO.BinaryWriter(aData);
|
|
Serialize(W);
|
|
}
|
|
|
|
#if USE_SharpZipLib
|
|
public void SaveToCompressedStream(System.IO.Stream aData)
|
|
{
|
|
using (var gzipOut = new ICSharpCode.SharpZipLib.BZip2.BZip2OutputStream(aData))
|
|
{
|
|
gzipOut.IsStreamOwner = false;
|
|
SaveToStream(gzipOut);
|
|
gzipOut.Close();
|
|
}
|
|
}
|
|
|
|
public void SaveToCompressedFile(string aFileName)
|
|
{
|
|
|
|
#if USE_FileIO
|
|
System.IO.Directory.CreateDirectory((new System.IO.FileInfo(aFileName)).Directory.FullName);
|
|
using(var F = System.IO.File.OpenWrite(aFileName))
|
|
{
|
|
SaveToCompressedStream(F);
|
|
}
|
|
|
|
#else
|
|
throw new Exception("Can't use File IO stuff in webplayer");
|
|
#endif
|
|
}
|
|
public string SaveToCompressedBase64()
|
|
{
|
|
using (var stream = new System.IO.MemoryStream())
|
|
{
|
|
SaveToCompressedStream(stream);
|
|
stream.Position = 0;
|
|
return System.Convert.ToBase64String(stream.ToArray());
|
|
}
|
|
}
|
|
|
|
#else
|
|
public void SaveToCompressedStream(System.IO.Stream aData)
|
|
{
|
|
throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON");
|
|
}
|
|
|
|
public void SaveToCompressedFile(string aFileName)
|
|
{
|
|
throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON");
|
|
}
|
|
|
|
public string SaveToCompressedBase64()
|
|
{
|
|
throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON");
|
|
}
|
|
#endif
|
|
|
|
public void SaveToFile(string aFileName)
|
|
{
|
|
#if USE_FileIO
|
|
System.IO.Directory.CreateDirectory((new System.IO.FileInfo(aFileName)).Directory.FullName);
|
|
using (var F = System.IO.File.OpenWrite(aFileName))
|
|
{
|
|
SaveToStream(F);
|
|
}
|
|
#else
|
|
throw new Exception ("Can't use File IO stuff in webplayer");
|
|
#endif
|
|
}
|
|
|
|
public string SaveToBase64()
|
|
{
|
|
using (var stream = new System.IO.MemoryStream())
|
|
{
|
|
SaveToStream(stream);
|
|
stream.Position = 0;
|
|
return System.Convert.ToBase64String(stream.ToArray());
|
|
}
|
|
}
|
|
|
|
public static JSONNode Deserialize(System.IO.BinaryReader aReader)
|
|
{
|
|
JSONBinaryTag type = (JSONBinaryTag)aReader.ReadByte();
|
|
switch (type)
|
|
{
|
|
case JSONBinaryTag.Array:
|
|
{
|
|
int count = aReader.ReadInt32();
|
|
JSONArray tmp = new JSONArray();
|
|
for (int i = 0; i < count; i++)
|
|
tmp.Add(Deserialize(aReader));
|
|
return tmp;
|
|
}
|
|
case JSONBinaryTag.Class:
|
|
{
|
|
int count = aReader.ReadInt32();
|
|
JSONClass tmp = new JSONClass();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
string key = aReader.ReadString();
|
|
var val = Deserialize(aReader);
|
|
tmp.Add(key, val);
|
|
}
|
|
return tmp;
|
|
}
|
|
case JSONBinaryTag.Value:
|
|
{
|
|
return new JSONData(aReader.ReadString());
|
|
}
|
|
case JSONBinaryTag.IntValue:
|
|
{
|
|
return new JSONData(aReader.ReadInt32());
|
|
}
|
|
case JSONBinaryTag.DoubleValue:
|
|
{
|
|
return new JSONData(aReader.ReadDouble());
|
|
}
|
|
case JSONBinaryTag.BoolValue:
|
|
{
|
|
return new JSONData(aReader.ReadBoolean());
|
|
}
|
|
case JSONBinaryTag.FloatValue:
|
|
{
|
|
return new JSONData(aReader.ReadSingle());
|
|
}
|
|
|
|
default:
|
|
{
|
|
throw new Exception("Error deserializing JSON. Unknown tag: " + type);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if USE_SharpZipLib
|
|
public static JSONNode LoadFromCompressedStream(System.IO.Stream aData)
|
|
{
|
|
var zin = new ICSharpCode.SharpZipLib.BZip2.BZip2InputStream(aData);
|
|
return LoadFromStream(zin);
|
|
}
|
|
public static JSONNode LoadFromCompressedFile(string aFileName)
|
|
{
|
|
#if USE_FileIO
|
|
using(var F = System.IO.File.OpenRead(aFileName))
|
|
{
|
|
return LoadFromCompressedStream(F);
|
|
}
|
|
#else
|
|
throw new Exception("Can't use File IO stuff in webplayer");
|
|
#endif
|
|
}
|
|
public static JSONNode LoadFromCompressedBase64(string aBase64)
|
|
{
|
|
var tmp = System.Convert.FromBase64String(aBase64);
|
|
var stream = new System.IO.MemoryStream(tmp);
|
|
stream.Position = 0;
|
|
return LoadFromCompressedStream(stream);
|
|
}
|
|
#else
|
|
public static JSONNode LoadFromCompressedFile(string aFileName)
|
|
{
|
|
throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON");
|
|
}
|
|
|
|
public static JSONNode LoadFromCompressedStream(System.IO.Stream aData)
|
|
{
|
|
throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON");
|
|
}
|
|
|
|
public static JSONNode LoadFromCompressedBase64(string aBase64)
|
|
{
|
|
throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON");
|
|
}
|
|
#endif
|
|
|
|
public static JSONNode LoadFromStream(System.IO.Stream aData)
|
|
{
|
|
using (var R = new System.IO.BinaryReader(aData))
|
|
{
|
|
return Deserialize(R);
|
|
}
|
|
}
|
|
|
|
public static JSONNode LoadFromFile(string aFileName)
|
|
{
|
|
#if USE_FileIO
|
|
using (var F = System.IO.File.OpenRead(aFileName))
|
|
{
|
|
return LoadFromStream(F);
|
|
}
|
|
#else
|
|
throw new Exception ("Can't use File IO stuff in webplayer");
|
|
#endif
|
|
}
|
|
|
|
public static JSONNode LoadFromBase64(string aBase64)
|
|
{
|
|
var tmp = System.Convert.FromBase64String(aBase64);
|
|
var stream = new System.IO.MemoryStream(tmp);
|
|
stream.Position = 0;
|
|
return LoadFromStream(stream);
|
|
}
|
|
}
|
|
// End of JSONNode
|
|
|
|
public class JSONArray : JSONNode, IEnumerable
|
|
{
|
|
private List<JSONNode> m_List = new List<JSONNode>();
|
|
|
|
public override JSONNode this[int aIndex]
|
|
{
|
|
get
|
|
{
|
|
if (aIndex < 0 || aIndex >= m_List.Count)
|
|
return new JSONLazyCreator(this);
|
|
return m_List[aIndex];
|
|
}
|
|
set
|
|
{
|
|
if (aIndex < 0 || aIndex >= m_List.Count)
|
|
m_List.Add(value);
|
|
else
|
|
m_List[aIndex] = value;
|
|
}
|
|
}
|
|
|
|
public override JSONNode this[string aKey]
|
|
{
|
|
get { return new JSONLazyCreator(this); }
|
|
set { m_List.Add(value); }
|
|
}
|
|
|
|
public override int Count
|
|
{
|
|
get { return m_List.Count; }
|
|
}
|
|
|
|
public override void Add(string aKey, JSONNode aItem)
|
|
{
|
|
m_List.Add(aItem);
|
|
}
|
|
|
|
public override JSONNode Remove(int aIndex)
|
|
{
|
|
if (aIndex < 0 || aIndex >= m_List.Count)
|
|
return null;
|
|
JSONNode tmp = m_List[aIndex];
|
|
m_List.RemoveAt(aIndex);
|
|
return tmp;
|
|
}
|
|
|
|
public override JSONNode Remove(JSONNode aNode)
|
|
{
|
|
m_List.Remove(aNode);
|
|
return aNode;
|
|
}
|
|
|
|
public override IEnumerable<JSONNode> Children
|
|
{
|
|
get
|
|
{
|
|
foreach (JSONNode N in m_List)
|
|
yield return N;
|
|
}
|
|
}
|
|
|
|
public IEnumerator GetEnumerator()
|
|
{
|
|
foreach (JSONNode N in m_List)
|
|
yield return N;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
string result = "[ ";
|
|
foreach (JSONNode N in m_List)
|
|
{
|
|
if (result.Length > 2)
|
|
result += ", ";
|
|
result += N.ToString();
|
|
}
|
|
result += " ]";
|
|
return result;
|
|
}
|
|
|
|
public override string ToString(string aPrefix)
|
|
{
|
|
string result = "[ ";
|
|
foreach (JSONNode N in m_List)
|
|
{
|
|
if (result.Length > 3)
|
|
result += ", ";
|
|
result += "\n" + aPrefix + " ";
|
|
result += N.ToString(aPrefix + " ");
|
|
}
|
|
result += "\n" + aPrefix + "]";
|
|
return result;
|
|
}
|
|
|
|
public override string ToJSON(int prefix)
|
|
{
|
|
string s = new string(' ', (prefix + 1) * 2);
|
|
string ret = "[ ";
|
|
foreach (JSONNode n in m_List)
|
|
{
|
|
if (ret.Length > 3)
|
|
ret += ", ";
|
|
ret += "\n" + s;
|
|
ret += n.ToJSON(prefix + 1);
|
|
|
|
}
|
|
ret += "\n" + s + "]";
|
|
return ret;
|
|
}
|
|
|
|
public override void Serialize(System.IO.BinaryWriter aWriter)
|
|
{
|
|
aWriter.Write((byte)JSONBinaryTag.Array);
|
|
aWriter.Write(m_List.Count);
|
|
for (int i = 0; i < m_List.Count; i++)
|
|
{
|
|
m_List[i].Serialize(aWriter);
|
|
}
|
|
}
|
|
}
|
|
// End of JSONArray
|
|
|
|
public class JSONClass : JSONNode, IEnumerable
|
|
{
|
|
private Dictionary<string, JSONNode> m_Dict = new Dictionary<string, JSONNode>();
|
|
|
|
public Dictionary<string, JSONNode> Dict
|
|
{
|
|
get
|
|
{
|
|
return m_Dict;
|
|
}
|
|
}
|
|
|
|
public override JSONNode this[string aKey]
|
|
{
|
|
get
|
|
{
|
|
if (m_Dict.ContainsKey(aKey))
|
|
return m_Dict[aKey];
|
|
else
|
|
return new JSONLazyCreator(this, aKey);
|
|
}
|
|
set
|
|
{
|
|
if (m_Dict.ContainsKey(aKey))
|
|
m_Dict[aKey] = value;
|
|
else
|
|
m_Dict.Add(aKey, value);
|
|
}
|
|
}
|
|
|
|
public override JSONNode this[int aIndex]
|
|
{
|
|
get
|
|
{
|
|
if (aIndex < 0 || aIndex >= m_Dict.Count)
|
|
return null;
|
|
return m_Dict.ElementAt(aIndex).Value;
|
|
}
|
|
set
|
|
{
|
|
if (aIndex < 0 || aIndex >= m_Dict.Count)
|
|
return;
|
|
string key = m_Dict.ElementAt(aIndex).Key;
|
|
m_Dict[key] = value;
|
|
}
|
|
}
|
|
|
|
public override int Count
|
|
{
|
|
get { return m_Dict.Count; }
|
|
}
|
|
|
|
|
|
public override void Add(string aKey, JSONNode aItem)
|
|
{
|
|
if (!string.IsNullOrEmpty(aKey))
|
|
{
|
|
if (m_Dict.ContainsKey(aKey))
|
|
m_Dict[aKey] = aItem;
|
|
else
|
|
m_Dict.Add(aKey, aItem);
|
|
}
|
|
else
|
|
m_Dict.Add(Guid.NewGuid().ToString(), aItem);
|
|
}
|
|
|
|
public override JSONNode Remove(string aKey)
|
|
{
|
|
if (!m_Dict.ContainsKey(aKey))
|
|
return null;
|
|
JSONNode tmp = m_Dict[aKey];
|
|
m_Dict.Remove(aKey);
|
|
return tmp;
|
|
}
|
|
|
|
public override JSONNode Remove(int aIndex)
|
|
{
|
|
if (aIndex < 0 || aIndex >= m_Dict.Count)
|
|
return null;
|
|
var item = m_Dict.ElementAt(aIndex);
|
|
m_Dict.Remove(item.Key);
|
|
return item.Value;
|
|
}
|
|
|
|
public override JSONNode Remove(JSONNode aNode)
|
|
{
|
|
try
|
|
{
|
|
var item = m_Dict.Where(k => k.Value == aNode).First();
|
|
m_Dict.Remove(item.Key);
|
|
return aNode;
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public override IEnumerable<JSONNode> Children
|
|
{
|
|
get
|
|
{
|
|
foreach (KeyValuePair<string, JSONNode> N in m_Dict)
|
|
yield return N.Value;
|
|
}
|
|
}
|
|
|
|
public IEnumerator GetEnumerator()
|
|
{
|
|
foreach (KeyValuePair<string, JSONNode> N in m_Dict)
|
|
yield return N;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
string result = "{";
|
|
foreach (KeyValuePair<string, JSONNode> N in m_Dict)
|
|
{
|
|
if (result.Length > 2)
|
|
result += ", ";
|
|
result += "\"" + Escape(N.Key) + "\":" + N.Value.ToString();
|
|
}
|
|
result += "}";
|
|
return result;
|
|
}
|
|
|
|
public override string ToString(string aPrefix)
|
|
{
|
|
string result = "{ ";
|
|
foreach (KeyValuePair<string, JSONNode> N in m_Dict)
|
|
{
|
|
if (result.Length > 3)
|
|
result += ", ";
|
|
result += "\n" + aPrefix + " ";
|
|
result += "\"" + Escape(N.Key) + "\" : " + N.Value.ToString(aPrefix + " ");
|
|
}
|
|
result += "\n" + aPrefix + "}";
|
|
return result;
|
|
}
|
|
|
|
public override string ToJSON(int prefix)
|
|
{
|
|
string s = new string(' ', (prefix + 1) * 2);
|
|
string ret = "{ ";
|
|
foreach (KeyValuePair<string, JSONNode> n in m_Dict)
|
|
{
|
|
if (ret.Length > 3)
|
|
ret += ", ";
|
|
ret += "\n" + s;
|
|
ret += string.Format("\"{0}\": {1}", n.Key, n.Value.ToJSON(prefix + 1));
|
|
}
|
|
ret += "\n" + s + "}";
|
|
return ret;
|
|
}
|
|
|
|
public override void Serialize(System.IO.BinaryWriter aWriter)
|
|
{
|
|
aWriter.Write((byte)JSONBinaryTag.Class);
|
|
aWriter.Write(m_Dict.Count);
|
|
foreach (string K in m_Dict.Keys)
|
|
{
|
|
aWriter.Write(K);
|
|
m_Dict[K].Serialize(aWriter);
|
|
}
|
|
}
|
|
}
|
|
// End of JSONClass
|
|
|
|
public class JSONData : JSONNode
|
|
{
|
|
private string m_Data;
|
|
|
|
|
|
public override string Value
|
|
{
|
|
get { return m_Data; }
|
|
set
|
|
{
|
|
m_Data = value;
|
|
Tag = JSONBinaryTag.Value;
|
|
}
|
|
}
|
|
|
|
public JSONData(string aData)
|
|
{
|
|
m_Data = aData;
|
|
Tag = JSONBinaryTag.Value;
|
|
}
|
|
|
|
public JSONData(float aData)
|
|
{
|
|
AsFloat = aData;
|
|
}
|
|
|
|
public JSONData(double aData)
|
|
{
|
|
AsDouble = aData;
|
|
}
|
|
|
|
public JSONData(bool aData)
|
|
{
|
|
AsBool = aData;
|
|
}
|
|
|
|
public JSONData(int aData)
|
|
{
|
|
AsInt = aData;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return "\"" + Escape(m_Data) + "\"";
|
|
}
|
|
|
|
public override string ToString(string aPrefix)
|
|
{
|
|
return "\"" + Escape(m_Data) + "\"";
|
|
}
|
|
|
|
public override string ToJSON(int prefix)
|
|
{
|
|
switch (Tag)
|
|
{
|
|
case JSONBinaryTag.DoubleValue:
|
|
case JSONBinaryTag.FloatValue:
|
|
case JSONBinaryTag.IntValue:
|
|
return m_Data;
|
|
case JSONBinaryTag.Value:
|
|
return string.Format("\"{0}\"", Escape(m_Data));
|
|
default:
|
|
throw new NotSupportedException("This shouldn't be here: " + Tag.ToString());
|
|
}
|
|
}
|
|
|
|
public override void Serialize(System.IO.BinaryWriter aWriter)
|
|
{
|
|
var tmp = new JSONData("");
|
|
|
|
tmp.AsInt = AsInt;
|
|
if (tmp.m_Data == this.m_Data)
|
|
{
|
|
aWriter.Write((byte)JSONBinaryTag.IntValue);
|
|
aWriter.Write(AsInt);
|
|
return;
|
|
}
|
|
tmp.AsFloat = AsFloat;
|
|
if (tmp.m_Data == this.m_Data)
|
|
{
|
|
aWriter.Write((byte)JSONBinaryTag.FloatValue);
|
|
aWriter.Write(AsFloat);
|
|
return;
|
|
}
|
|
tmp.AsDouble = AsDouble;
|
|
if (tmp.m_Data == this.m_Data)
|
|
{
|
|
aWriter.Write((byte)JSONBinaryTag.DoubleValue);
|
|
aWriter.Write(AsDouble);
|
|
return;
|
|
}
|
|
|
|
tmp.AsBool = AsBool;
|
|
if (tmp.m_Data == this.m_Data)
|
|
{
|
|
aWriter.Write((byte)JSONBinaryTag.BoolValue);
|
|
aWriter.Write(AsBool);
|
|
return;
|
|
}
|
|
aWriter.Write((byte)JSONBinaryTag.Value);
|
|
aWriter.Write(m_Data);
|
|
}
|
|
}
|
|
// End of JSONData
|
|
|
|
internal class JSONLazyCreator : JSONNode
|
|
{
|
|
private JSONNode m_Node = null;
|
|
private string m_Key = null;
|
|
|
|
public JSONLazyCreator(JSONNode aNode)
|
|
{
|
|
m_Node = aNode;
|
|
m_Key = null;
|
|
}
|
|
|
|
public JSONLazyCreator(JSONNode aNode, string aKey)
|
|
{
|
|
m_Node = aNode;
|
|
m_Key = aKey;
|
|
}
|
|
|
|
private void Set(JSONNode aVal)
|
|
{
|
|
if (m_Key == null)
|
|
{
|
|
m_Node.Add(aVal);
|
|
}
|
|
else
|
|
{
|
|
m_Node.Add(m_Key, aVal);
|
|
}
|
|
m_Node = null; // Be GC friendly.
|
|
}
|
|
|
|
public override JSONNode this[int aIndex]
|
|
{
|
|
get
|
|
{
|
|
return new JSONLazyCreator(this);
|
|
}
|
|
set
|
|
{
|
|
var tmp = new JSONArray();
|
|
tmp.Add(value);
|
|
Set(tmp);
|
|
}
|
|
}
|
|
|
|
public override JSONNode this[string aKey]
|
|
{
|
|
get
|
|
{
|
|
return new JSONLazyCreator(this, aKey);
|
|
}
|
|
set
|
|
{
|
|
var tmp = new JSONClass();
|
|
tmp.Add(aKey, value);
|
|
Set(tmp);
|
|
}
|
|
}
|
|
|
|
public override void Add(JSONNode aItem)
|
|
{
|
|
var tmp = new JSONArray();
|
|
tmp.Add(aItem);
|
|
Set(tmp);
|
|
}
|
|
|
|
public override void Add(string aKey, JSONNode aItem)
|
|
{
|
|
var tmp = new JSONClass();
|
|
tmp.Add(aKey, aItem);
|
|
Set(tmp);
|
|
}
|
|
|
|
public static bool operator ==(JSONLazyCreator a, object b)
|
|
{
|
|
if (b == null)
|
|
return true;
|
|
return System.Object.ReferenceEquals(a, b);
|
|
}
|
|
|
|
public static bool operator !=(JSONLazyCreator a, object b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (obj == null)
|
|
return true;
|
|
return System.Object.ReferenceEquals(this, obj);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return base.GetHashCode();
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return "";
|
|
}
|
|
|
|
public override string ToString(string aPrefix)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
public override string ToJSON(int prefix)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
public override int AsInt
|
|
{
|
|
get
|
|
{
|
|
JSONData tmp = new JSONData(0);
|
|
Set(tmp);
|
|
return 0;
|
|
}
|
|
set
|
|
{
|
|
JSONData tmp = new JSONData(value);
|
|
Set(tmp);
|
|
}
|
|
}
|
|
|
|
public override float AsFloat
|
|
{
|
|
get
|
|
{
|
|
JSONData tmp = new JSONData(0.0f);
|
|
Set(tmp);
|
|
return 0.0f;
|
|
}
|
|
set
|
|
{
|
|
JSONData tmp = new JSONData(value);
|
|
Set(tmp);
|
|
}
|
|
}
|
|
|
|
public override double AsDouble
|
|
{
|
|
get
|
|
{
|
|
JSONData tmp = new JSONData(0.0);
|
|
Set(tmp);
|
|
return 0.0;
|
|
}
|
|
set
|
|
{
|
|
JSONData tmp = new JSONData(value);
|
|
Set(tmp);
|
|
}
|
|
}
|
|
|
|
public override bool AsBool
|
|
{
|
|
get
|
|
{
|
|
JSONData tmp = new JSONData(false);
|
|
Set(tmp);
|
|
return false;
|
|
}
|
|
set
|
|
{
|
|
JSONData tmp = new JSONData(value);
|
|
Set(tmp);
|
|
}
|
|
}
|
|
|
|
public override JSONArray AsArray
|
|
{
|
|
get
|
|
{
|
|
JSONArray tmp = new JSONArray();
|
|
Set(tmp);
|
|
return tmp;
|
|
}
|
|
}
|
|
|
|
public override JSONClass AsObject
|
|
{
|
|
get
|
|
{
|
|
JSONClass tmp = new JSONClass();
|
|
Set(tmp);
|
|
return tmp;
|
|
}
|
|
}
|
|
}
|
|
// End of JSONLazyCreator
|
|
|
|
public static class JSON
|
|
{
|
|
public static JSONNode Parse(string aJSON)
|
|
{
|
|
return JSONNode.Parse(aJSON);
|
|
}
|
|
}
|
|
} |