add
This commit is contained in:
548
Assets/CoolapeFrame/Scripts/toolkit/JSON.cs
Normal file
548
Assets/CoolapeFrame/Scripts/toolkit/JSON.cs
Normal file
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
********************************************************************************
|
||||
*Copyright(C),coolae.net
|
||||
*Author: chenbin
|
||||
*Version: 2.0
|
||||
*Date: 2017-01-09
|
||||
*Description: JSON工具
|
||||
*Others:
|
||||
*History:
|
||||
*********************************************************************************
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace Coolape
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This class encodes and decodes JSON strings.
|
||||
/// Spec. details, see http://www.json.org/
|
||||
///
|
||||
/// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable.
|
||||
/// All numbers are parsed to doubles.
|
||||
/// </summary>
|
||||
public class JSON
|
||||
{
|
||||
public const int TOKEN_NONE = 0;
|
||||
public const int TOKEN_CURLY_OPEN = 1;
|
||||
public const int TOKEN_CURLY_CLOSE = 2;
|
||||
public const int TOKEN_SQUARED_OPEN = 3;
|
||||
public const int TOKEN_SQUARED_CLOSE = 4;
|
||||
public const int TOKEN_COLON = 5;
|
||||
public const int TOKEN_COMMA = 6;
|
||||
public const int TOKEN_STRING = 7;
|
||||
public const int TOKEN_NUMBER = 8;
|
||||
public const int TOKEN_TRUE = 9;
|
||||
public const int TOKEN_FALSE = 10;
|
||||
public const int TOKEN_NULL = 11;
|
||||
|
||||
private const int BUILDER_CAPACITY = 2000;
|
||||
|
||||
public static Hashtable DecodeMap (string json)
|
||||
{
|
||||
object o = JsonDecode (json);
|
||||
if (o == null)
|
||||
return null;
|
||||
if (o is Hashtable) {
|
||||
return (Hashtable)o;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ArrayList DecodeList (string json)
|
||||
{
|
||||
object o = JsonDecode (json);
|
||||
if (o == null)
|
||||
return null;
|
||||
if (o is ArrayList) {
|
||||
return (ArrayList)o;
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the string json into a value
|
||||
/// </summary>
|
||||
/// <param name="json">A JSON string.</param>
|
||||
/// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
|
||||
public static object JsonDecode (string json)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
return JsonDecode (json, ref success);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the string json into a value; and fills 'success' with the successfullness of the parse.
|
||||
/// </summary>
|
||||
/// <param name="json">A JSON string.</param>
|
||||
/// <param name="success">Successful parse?</param>
|
||||
/// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
|
||||
public static object JsonDecode (string json, ref bool success)
|
||||
{
|
||||
try {
|
||||
success = true;
|
||||
if (json != null) {
|
||||
char[] charArray = json.ToCharArray ();
|
||||
if (charArray == null)
|
||||
return null;
|
||||
int index = 0;
|
||||
resetIndex4JsonStr (charArray, ref index);
|
||||
object value = ParseValue (charArray, ref index, ref success);
|
||||
return value;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
UnityEngine.Debug.LogError (e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Hashtable / ArrayList object into a JSON string
|
||||
/// </summary>
|
||||
/// <param name="json">A Hashtable / ArrayList</param>
|
||||
/// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
|
||||
public static string JsonEncode (object json)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder (BUILDER_CAPACITY);
|
||||
bool success = SerializeValue (json, builder);
|
||||
return (success ? builder.ToString () : null);
|
||||
}
|
||||
|
||||
protected static Hashtable ParseObject (char[] json, ref int index, ref bool success)
|
||||
{
|
||||
Hashtable table = new Hashtable ();
|
||||
int token;
|
||||
|
||||
// {
|
||||
NextToken (json, ref index);
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
token = LookAhead (json, index);
|
||||
if (token == JSON.TOKEN_NONE) {
|
||||
success = false;
|
||||
return null;
|
||||
} else if (token == JSON.TOKEN_COMMA) {
|
||||
NextToken (json, ref index);
|
||||
} else if (token == JSON.TOKEN_CURLY_CLOSE) {
|
||||
NextToken (json, ref index);
|
||||
return table;
|
||||
} else {
|
||||
|
||||
// name
|
||||
string name = ParseString (json, ref index, ref success);
|
||||
if (!success) {
|
||||
success = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
// :
|
||||
token = NextToken (json, ref index);
|
||||
if (token != JSON.TOKEN_COLON) {
|
||||
success = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
// value
|
||||
object value = ParseValue (json, ref index, ref success);
|
||||
if (!success) {
|
||||
success = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
table [name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
protected static ArrayList ParseArray (char[] json, ref int index, ref bool success)
|
||||
{
|
||||
ArrayList array = new ArrayList ();
|
||||
|
||||
// [
|
||||
NextToken (json, ref index);
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
int token = LookAhead (json, index);
|
||||
if (token == JSON.TOKEN_NONE) {
|
||||
success = false;
|
||||
return null;
|
||||
} else if (token == JSON.TOKEN_COMMA) {
|
||||
NextToken (json, ref index);
|
||||
} else if (token == JSON.TOKEN_SQUARED_CLOSE) {
|
||||
NextToken (json, ref index);
|
||||
break;
|
||||
} else {
|
||||
object value = ParseValue (json, ref index, ref success);
|
||||
if (!success) {
|
||||
return null;
|
||||
}
|
||||
|
||||
array.Add (value);
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
protected static object ParseValue (char[] json, ref int index, ref bool success)
|
||||
{
|
||||
switch (LookAhead (json, index)) {
|
||||
case JSON.TOKEN_STRING:
|
||||
return ParseString (json, ref index, ref success);
|
||||
case JSON.TOKEN_NUMBER:
|
||||
return ParseNumber (json, ref index, ref success);
|
||||
case JSON.TOKEN_CURLY_OPEN:
|
||||
return ParseObject (json, ref index, ref success);
|
||||
case JSON.TOKEN_SQUARED_OPEN:
|
||||
return ParseArray (json, ref index, ref success);
|
||||
case JSON.TOKEN_TRUE:
|
||||
NextToken (json, ref index);
|
||||
return true;
|
||||
case JSON.TOKEN_FALSE:
|
||||
NextToken (json, ref index);
|
||||
return false;
|
||||
case JSON.TOKEN_NULL:
|
||||
NextToken (json, ref index);
|
||||
return null;
|
||||
case JSON.TOKEN_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
success = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static string ParseString (char[] json, ref int index, ref bool success)
|
||||
{
|
||||
StringBuilder s = new StringBuilder (BUILDER_CAPACITY);
|
||||
char c;
|
||||
|
||||
EatWhitespace (json, ref index);
|
||||
|
||||
// "
|
||||
c = json [index++];
|
||||
|
||||
bool complete = false;
|
||||
while (!complete) {
|
||||
|
||||
if (index == json.Length) {
|
||||
break;
|
||||
}
|
||||
|
||||
c = json [index++];
|
||||
if (c == '"') {
|
||||
complete = true;
|
||||
break;
|
||||
} else if (c == '\\') {
|
||||
|
||||
if (index == json.Length) {
|
||||
break;
|
||||
}
|
||||
c = json [index++];
|
||||
if (c == '"') {
|
||||
s.Append ('"');
|
||||
} else if (c == '\\') {
|
||||
s.Append ('\\');
|
||||
} else if (c == '/') {
|
||||
s.Append ('/');
|
||||
} else if (c == 'b') {
|
||||
s.Append ('\b');
|
||||
} else if (c == 'f') {
|
||||
s.Append ('\f');
|
||||
} else if (c == 'n') {
|
||||
s.Append ('\n');
|
||||
} else if (c == 'r') {
|
||||
s.Append ('\r');
|
||||
} else if (c == 't') {
|
||||
s.Append ('\t');
|
||||
} else if (c == 'u') {
|
||||
int remainingLength = json.Length - index;
|
||||
if (remainingLength >= 4) {
|
||||
// parse the 32 bit hex into an integer codepoint
|
||||
uint codePoint;
|
||||
if (!(success = UInt32.TryParse (new string (json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) {
|
||||
return "";
|
||||
}
|
||||
// convert the integer codepoint to a unicode char and add to string
|
||||
s.Append (Char.ConvertFromUtf32 ((int)codePoint));
|
||||
// skip 4 chars
|
||||
index += 4;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
s.Append (c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!complete) {
|
||||
success = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
return s.ToString ();
|
||||
}
|
||||
|
||||
protected static double ParseNumber (char[] json, ref int index, ref bool success)
|
||||
{
|
||||
EatWhitespace (json, ref index);
|
||||
|
||||
int lastIndex = GetLastIndexOfNumber (json, index);
|
||||
int charLength = (lastIndex - index) + 1;
|
||||
|
||||
double number;
|
||||
success = Double.TryParse (new string (json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number);
|
||||
|
||||
index = lastIndex + 1;
|
||||
return number;
|
||||
}
|
||||
|
||||
protected static int GetLastIndexOfNumber (char[] json, int index)
|
||||
{
|
||||
int lastIndex;
|
||||
|
||||
for (lastIndex = index; lastIndex < json.Length; lastIndex++) {
|
||||
if ("0123456789+-.eE".IndexOf (json [lastIndex]) == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lastIndex - 1;
|
||||
}
|
||||
|
||||
protected static void EatWhitespace (char[] json, ref int index)
|
||||
{
|
||||
for (; index < json.Length; index++) {
|
||||
if (" \t\n\r".IndexOf (json [index]) == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void resetIndex4JsonStr (char[] json, ref int index)
|
||||
{
|
||||
if (json == null)
|
||||
return;
|
||||
for (; index < json.Length; index++) {
|
||||
if ("[{".IndexOf (json [index]) != -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static int LookAhead (char[] json, int index)
|
||||
{
|
||||
int saveIndex = index;
|
||||
return NextToken (json, ref saveIndex);
|
||||
}
|
||||
|
||||
protected static int NextToken (char[] json, ref int index)
|
||||
{
|
||||
EatWhitespace (json, ref index);
|
||||
|
||||
if (index == json.Length) {
|
||||
return JSON.TOKEN_NONE;
|
||||
}
|
||||
|
||||
char c = json [index];
|
||||
index++;
|
||||
switch (c) {
|
||||
case '{':
|
||||
return JSON.TOKEN_CURLY_OPEN;
|
||||
case '}':
|
||||
return JSON.TOKEN_CURLY_CLOSE;
|
||||
case '[':
|
||||
return JSON.TOKEN_SQUARED_OPEN;
|
||||
case ']':
|
||||
return JSON.TOKEN_SQUARED_CLOSE;
|
||||
case ',':
|
||||
return JSON.TOKEN_COMMA;
|
||||
case '"':
|
||||
return JSON.TOKEN_STRING;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '-':
|
||||
return JSON.TOKEN_NUMBER;
|
||||
case ':':
|
||||
return JSON.TOKEN_COLON;
|
||||
}
|
||||
index--;
|
||||
|
||||
int remainingLength = json.Length - index;
|
||||
|
||||
// false
|
||||
if (remainingLength >= 5) {
|
||||
if (json [index] == 'f' &&
|
||||
json [index + 1] == 'a' &&
|
||||
json [index + 2] == 'l' &&
|
||||
json [index + 3] == 's' &&
|
||||
json [index + 4] == 'e') {
|
||||
index += 5;
|
||||
return JSON.TOKEN_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// true
|
||||
if (remainingLength >= 4) {
|
||||
if (json [index] == 't' &&
|
||||
json [index + 1] == 'r' &&
|
||||
json [index + 2] == 'u' &&
|
||||
json [index + 3] == 'e') {
|
||||
index += 4;
|
||||
return JSON.TOKEN_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// null
|
||||
if (remainingLength >= 4) {
|
||||
if (json [index] == 'n' &&
|
||||
json [index + 1] == 'u' &&
|
||||
json [index + 2] == 'l' &&
|
||||
json [index + 3] == 'l') {
|
||||
index += 4;
|
||||
return JSON.TOKEN_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.TOKEN_NONE;
|
||||
}
|
||||
|
||||
protected static bool SerializeValue (object value, StringBuilder builder)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if (value is string) {
|
||||
success = SerializeString ((string)value, builder);
|
||||
} else if (value is Hashtable) {
|
||||
success = SerializeObject ((Hashtable)value, builder);
|
||||
} else if (value is ArrayList) {
|
||||
success = SerializeArray ((ArrayList)value, builder);
|
||||
} else if ((value is Boolean) && ((Boolean)value == true)) {
|
||||
builder.Append ("true");
|
||||
} else if ((value is Boolean) && ((Boolean)value == false)) {
|
||||
builder.Append ("false");
|
||||
} else if (value is ValueType) {
|
||||
// thanks to ritchie for pointing out ValueType to me
|
||||
success = SerializeNumber (Convert.ToDouble (value), builder);
|
||||
} else if (value == null) {
|
||||
builder.Append ("null");
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
protected static bool SerializeObject (Hashtable anObject, StringBuilder builder)
|
||||
{
|
||||
builder.Append ("{");
|
||||
|
||||
IDictionaryEnumerator e = anObject.GetEnumerator ();
|
||||
bool first = true;
|
||||
while (e.MoveNext ()) {
|
||||
string key = e.Key.ToString ();
|
||||
object value = e.Value;
|
||||
|
||||
if (!first) {
|
||||
builder.Append (", ");
|
||||
}
|
||||
|
||||
SerializeString (key, builder);
|
||||
builder.Append (":");
|
||||
if (!SerializeValue (value, builder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
builder.Append ("}");
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static bool SerializeArray (ArrayList anArray, StringBuilder builder)
|
||||
{
|
||||
builder.Append ("[");
|
||||
|
||||
bool first = true;
|
||||
for (int i = 0; i < anArray.Count; i++) {
|
||||
object value = anArray [i];
|
||||
|
||||
if (!first) {
|
||||
builder.Append (", ");
|
||||
}
|
||||
|
||||
if (!SerializeValue (value, builder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
builder.Append ("]");
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static bool SerializeString (string aString, StringBuilder builder)
|
||||
{
|
||||
builder.Append ("\"");
|
||||
|
||||
char[] charArray = aString.ToCharArray ();
|
||||
for (int i = 0; i < charArray.Length; i++) {
|
||||
char c = charArray [i];
|
||||
if (c == '"') {
|
||||
builder.Append ("\\\"");
|
||||
} else if (c == '\\') {
|
||||
builder.Append ("\\\\");
|
||||
} else if (c == '\b') {
|
||||
builder.Append ("\\b");
|
||||
} else if (c == '\f') {
|
||||
builder.Append ("\\f");
|
||||
} else if (c == '\n') {
|
||||
builder.Append ("\\n");
|
||||
} else if (c == '\r') {
|
||||
builder.Append ("\\r");
|
||||
} else if (c == '\t') {
|
||||
builder.Append ("\\t");
|
||||
} else {
|
||||
int codepoint = Convert.ToInt32 (c);
|
||||
if ((codepoint >= 32) && (codepoint <= 126)) {
|
||||
builder.Append (c);
|
||||
} else {
|
||||
builder.Append ("\\u" + Convert.ToString (codepoint, 16).PadLeft (4, '0'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.Append ("\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static bool SerializeNumber (double number, StringBuilder builder)
|
||||
{
|
||||
builder.Append (Convert.ToString (number, CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user