备份CatanBuilding瘦身独立工程
This commit is contained in:
@@ -0,0 +1,640 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace com.fpnn.common
|
||||
{
|
||||
public static class Json
|
||||
{
|
||||
public static object Parse(string json)
|
||||
{
|
||||
JsonParser parser = JsonParser.Create(json);
|
||||
return parser.Parse();
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> ParseObject(string json)
|
||||
{
|
||||
JsonParser parser = JsonParser.Create(json);
|
||||
return parser.ParseObject();
|
||||
}
|
||||
|
||||
public static string ToString(object obj)
|
||||
{
|
||||
JsonStringify js = new JsonStringify();
|
||||
return js.Stringify(obj);
|
||||
}
|
||||
}
|
||||
|
||||
//============================[ Exception ]============================//
|
||||
public class JsonException : Exception
|
||||
{
|
||||
public JsonException(string message) : base(message) { }
|
||||
public JsonException(String message, Exception ex) : base(message, ex) { }
|
||||
}
|
||||
|
||||
public class InvalidJsonException : JsonException
|
||||
{
|
||||
public InvalidJsonException(string message) : base(message) { }
|
||||
public InvalidJsonException(string message, Exception ex) : base(message, ex) { }
|
||||
}
|
||||
|
||||
public class JsonTypeException : JsonException
|
||||
{
|
||||
public JsonTypeException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
//============================[ Json Parser ]============================//
|
||||
|
||||
internal enum JsonElementType
|
||||
{
|
||||
Empty,
|
||||
Null,
|
||||
Boolean,
|
||||
String,
|
||||
Int64,
|
||||
UInt64,
|
||||
Double,
|
||||
Array,
|
||||
Dictionary,
|
||||
}
|
||||
|
||||
internal delegate void JsonParseSignDelegate(JsonParser parser);
|
||||
|
||||
internal class JsonParser
|
||||
{
|
||||
class JsonElement
|
||||
{
|
||||
public object element;
|
||||
public JsonElementType type;
|
||||
}
|
||||
|
||||
static private readonly Dictionary<char, JsonParseSignDelegate> signDelegateMap;
|
||||
static private readonly Dictionary<char, ushort> hexTable;
|
||||
static private readonly HashSet<char> numericalChars;
|
||||
|
||||
private readonly string json;
|
||||
private int idx;
|
||||
private string key;
|
||||
private bool wantKey;
|
||||
private bool wantValue;
|
||||
private bool wantComma;
|
||||
private bool wantSemicolon;
|
||||
|
||||
private readonly Stack<JsonElement> elementStack;
|
||||
|
||||
private object parseResult;
|
||||
private JsonElementType resultType;
|
||||
|
||||
static JsonParser()
|
||||
{
|
||||
signDelegateMap = new Dictionary<char, JsonParseSignDelegate>
|
||||
{
|
||||
{ '{', EnterObject },
|
||||
{ '}', ExitObject },
|
||||
{ '[', EnterArray },
|
||||
{ ']', ExitArray },
|
||||
{ ',', ProcessComma },
|
||||
{ ':', ProcessSemicolon },
|
||||
{ '"', ProcessString },
|
||||
};
|
||||
|
||||
hexTable = new Dictionary<char, ushort>
|
||||
{
|
||||
{ '0', 0 },
|
||||
{ '1', 1 },
|
||||
{ '2', 2 },
|
||||
{ '3', 3 },
|
||||
{ '4', 4 },
|
||||
{ '5', 5 },
|
||||
{ '6', 6 },
|
||||
{ '7', 7 },
|
||||
{ '8', 8 },
|
||||
{ '9', 9 },
|
||||
|
||||
{ 'a', 10 },
|
||||
{ 'b', 11 },
|
||||
{ 'c', 12 },
|
||||
{ 'd', 13 },
|
||||
{ 'e', 14 },
|
||||
{ 'f', 15 },
|
||||
|
||||
{ 'A', 10 },
|
||||
{ 'B', 11 },
|
||||
{ 'C', 12 },
|
||||
{ 'D', 13 },
|
||||
{ 'E', 14 },
|
||||
{ 'F', 15 },
|
||||
};
|
||||
|
||||
numericalChars = new HashSet<char>
|
||||
{
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'+', '-', '.', 'E', 'e'
|
||||
};
|
||||
}
|
||||
|
||||
private static void EnterObject(JsonParser parser)
|
||||
{
|
||||
parser.EnterObject();
|
||||
}
|
||||
private static void ExitObject(JsonParser parser)
|
||||
{
|
||||
parser.ExitObject();
|
||||
}
|
||||
private static void EnterArray(JsonParser parser)
|
||||
{
|
||||
parser.EnterArray();
|
||||
}
|
||||
private static void ExitArray(JsonParser parser)
|
||||
{
|
||||
parser.ExitArray();
|
||||
}
|
||||
private static void ProcessComma(JsonParser parser)
|
||||
{
|
||||
parser.ProcessComma();
|
||||
}
|
||||
private static void ProcessSemicolon(JsonParser parser)
|
||||
{
|
||||
parser.ProcessSemicolon();
|
||||
}
|
||||
private static void ProcessString(JsonParser parser)
|
||||
{
|
||||
parser.ProcessString();
|
||||
}
|
||||
|
||||
public static JsonParser Create(string json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
throw new InvalidJsonException("Json Parser: content error. Null or empty content.");
|
||||
|
||||
JsonParser parser;
|
||||
|
||||
try
|
||||
{
|
||||
string normalizedString = json;
|
||||
if (!json.IsNormalized())
|
||||
normalizedString = json.Normalize();
|
||||
|
||||
parser = new JsonParser(normalizedString);
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
throw new InvalidJsonException("Json Parser: content error. Include invalid Unicode.", e);
|
||||
}
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
||||
//---------------------[ Instance Parts ]--------------------------//
|
||||
private JsonParser(string jsonString)
|
||||
{
|
||||
json = jsonString;
|
||||
idx = 0;
|
||||
|
||||
elementStack = new Stack<JsonElement>();
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
key = string.Empty;
|
||||
|
||||
wantKey = false;
|
||||
wantValue = false;
|
||||
wantComma = false;
|
||||
wantSemicolon = false;
|
||||
|
||||
elementStack.Clear();
|
||||
|
||||
parseResult = null;
|
||||
resultType = JsonElementType.Empty;
|
||||
}
|
||||
|
||||
private void ParseCore()
|
||||
{
|
||||
Reset();
|
||||
|
||||
while (idx < json.Length && parseResult == null)
|
||||
{
|
||||
if (Char.IsWhiteSpace(json[idx]))
|
||||
{
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (signDelegateMap.TryGetValue(json[idx], out JsonParseSignDelegate method))
|
||||
method(this);
|
||||
else
|
||||
GeneralProcess();
|
||||
}
|
||||
}
|
||||
|
||||
public object Parse()
|
||||
{
|
||||
ParseCore();
|
||||
return parseResult;
|
||||
}
|
||||
|
||||
|
||||
public Dictionary<string, object> ParseObject()
|
||||
{
|
||||
ParseCore();
|
||||
if (resultType == JsonElementType.Dictionary)
|
||||
return (Dictionary<string, object>)parseResult;
|
||||
|
||||
throw new JsonTypeException("Json Parser: want the Dictionary type, but the parsed type is " + resultType.ToString("G") + ".");
|
||||
}
|
||||
|
||||
private void FillKey(string value)
|
||||
{
|
||||
key = value;
|
||||
|
||||
wantKey = false;
|
||||
wantValue = false;
|
||||
wantComma = false;
|
||||
wantSemicolon = true;
|
||||
}
|
||||
|
||||
private void FillValue(object obj, JsonElementType type)
|
||||
{
|
||||
Dictionary<string, object> dict = (Dictionary<string, object>)elementStack.Peek().element;
|
||||
try
|
||||
{
|
||||
dict.Add(key, obj);
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
throw new InvalidJsonException("Json Parser: reduplicated key: \"" + key + "\", offset " + idx, e);
|
||||
}
|
||||
|
||||
wantKey = false;
|
||||
wantValue = false;
|
||||
wantComma = true;
|
||||
wantSemicolon = false;
|
||||
}
|
||||
|
||||
private void InsertNode(object obj, JsonElementType type)
|
||||
{
|
||||
int stackCount = elementStack.Count;
|
||||
if (stackCount > 0)
|
||||
{
|
||||
if (wantValue)
|
||||
FillValue(obj, type);
|
||||
else
|
||||
{
|
||||
List<object> list = (List<object>)elementStack.Peek().element;
|
||||
list.Add(obj);
|
||||
|
||||
wantComma = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == JsonElementType.Array || type == JsonElementType.Dictionary)
|
||||
{
|
||||
elementStack.Push(new JsonElement()
|
||||
{
|
||||
element = obj,
|
||||
type = type
|
||||
});
|
||||
}
|
||||
else if (stackCount == 0)
|
||||
{
|
||||
parseResult = obj;
|
||||
resultType = type;
|
||||
}
|
||||
}
|
||||
|
||||
private void FinishNode()
|
||||
{
|
||||
JsonElement element = elementStack.Pop();
|
||||
|
||||
if (elementStack.Count == 0)
|
||||
{
|
||||
parseResult = element.element;
|
||||
resultType = element.type;
|
||||
}
|
||||
else
|
||||
{
|
||||
wantKey = false;
|
||||
wantValue = false;
|
||||
wantComma = true;
|
||||
wantSemicolon = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessSlash(ref StringBuilder stringBuilder)
|
||||
{
|
||||
idx++;
|
||||
if (idx >= json.Length)
|
||||
throw new InvalidJsonException("Json Parser: content error, json truncated after '\\'.");
|
||||
|
||||
switch (json[idx])
|
||||
{
|
||||
case '"':
|
||||
stringBuilder.Append('"');
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
stringBuilder.Append('\\');
|
||||
break;
|
||||
|
||||
case '/':
|
||||
stringBuilder.Append('/');
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
stringBuilder.Append('\b');
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
stringBuilder.Append('\f');
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
stringBuilder.Append('\n');
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
stringBuilder.Append('\r');
|
||||
break;
|
||||
|
||||
case 't':
|
||||
stringBuilder.Append('\t');
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
if (idx + 5 >= json.Length)
|
||||
throw new InvalidJsonException("Json Parser: content error, json truncated after '\\u'.");
|
||||
|
||||
idx++;
|
||||
ushort value = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (hexTable.TryGetValue(json[idx+i], out ushort v))
|
||||
{
|
||||
value <<= 4;
|
||||
value += v;
|
||||
}
|
||||
else
|
||||
throw new InvalidJsonException("Json Parser: content error, invalid hex number for '\\u'. Offset " + idx);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
char c = Convert.ToChar(value);
|
||||
stringBuilder.Append(c);
|
||||
}
|
||||
catch (OverflowException e)
|
||||
{
|
||||
throw new InvalidJsonException("Json Parser: content error, invalid unicode value for '\\u'. Offset " + idx, e);
|
||||
}
|
||||
|
||||
idx += 4;
|
||||
return;
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
private string FetchString()
|
||||
{
|
||||
idx++;
|
||||
int startIdx = idx;
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
while (idx < json.Length)
|
||||
{
|
||||
if (json[idx] == '\\')
|
||||
{
|
||||
int count = idx - startIdx;
|
||||
if (count > 0)
|
||||
stringBuilder.Append(json, startIdx, count);
|
||||
|
||||
ProcessSlash(ref stringBuilder);
|
||||
startIdx = idx;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (json[idx] == '"')
|
||||
{
|
||||
int count = idx - startIdx;
|
||||
if (count > 0)
|
||||
stringBuilder.Append(json, startIdx, count);
|
||||
|
||||
idx++;
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
throw new InvalidJsonException("Json Parser: content error, json truncated in string value.");
|
||||
}
|
||||
|
||||
private void EnterObject()
|
||||
{
|
||||
if (wantKey || wantSemicolon || wantComma)
|
||||
throw new InvalidJsonException("Json Parser: content error, '{' at improper place. Offset " + idx);
|
||||
|
||||
InsertNode(new Dictionary<string, object>(), JsonElementType.Dictionary);
|
||||
|
||||
wantKey = true;
|
||||
wantValue = false;
|
||||
wantComma = false;
|
||||
wantSemicolon = false;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
private void ExitObject()
|
||||
{
|
||||
if (wantValue || wantSemicolon || elementStack.Count == 0 || elementStack.Peek().type != JsonElementType.Dictionary)
|
||||
throw new InvalidJsonException("Json Parser: content error, '}' at improper place. Offset " + idx);
|
||||
|
||||
idx++;
|
||||
FinishNode();
|
||||
}
|
||||
|
||||
private void EnterArray()
|
||||
{
|
||||
if (wantKey || wantSemicolon || wantComma)
|
||||
throw new InvalidJsonException("Json Parser: content error, '[' at improper place. Offset " + idx);
|
||||
|
||||
InsertNode(new List<object>(), JsonElementType.Array);
|
||||
|
||||
wantKey = false;
|
||||
wantValue = false;
|
||||
wantComma = false;
|
||||
wantSemicolon = false;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
private void ExitArray()
|
||||
{
|
||||
if (wantValue || wantSemicolon || elementStack.Count == 0 || elementStack.Peek().type != JsonElementType.Array)
|
||||
throw new InvalidJsonException("Json Parser: content error, ']' at improper place. Offset " + idx);
|
||||
|
||||
idx++;
|
||||
FinishNode();
|
||||
}
|
||||
|
||||
private void ProcessComma()
|
||||
{
|
||||
if (!wantComma)
|
||||
throw new InvalidJsonException("Json Parser: content error. ',' at error position. Offset " + idx);
|
||||
|
||||
wantComma = false;
|
||||
if (elementStack.Peek().type == JsonElementType.Dictionary)
|
||||
wantKey = true;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
private void ProcessSemicolon()
|
||||
{
|
||||
if (!wantSemicolon)
|
||||
throw new InvalidJsonException("Json Parser: content error. ':' at error position. Offset " + idx);
|
||||
|
||||
wantSemicolon = false;
|
||||
wantValue = true;
|
||||
idx++;
|
||||
}
|
||||
|
||||
private void ProcessString()
|
||||
{
|
||||
if (wantSemicolon || wantComma)
|
||||
throw new InvalidJsonException("Json Parser: content error. Require ':' or ','. Offset " + idx);
|
||||
|
||||
string value = FetchString();
|
||||
|
||||
if (wantKey)
|
||||
FillKey(value);
|
||||
else
|
||||
InsertNode(value, JsonElementType.String);
|
||||
}
|
||||
|
||||
private void GeneralProcess()
|
||||
{
|
||||
if (wantKey || wantSemicolon || wantComma)
|
||||
throw new InvalidJsonException("Json Parser: content error. Require '\"' or ':' or ','. Offset " + idx);
|
||||
|
||||
if (idx + 3 < json.Length)
|
||||
{
|
||||
string tmp = json.Substring(idx, 4);
|
||||
if (string.Equals(tmp, "true", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
idx += 4;
|
||||
InsertNode(true, JsonElementType.Boolean);
|
||||
return;
|
||||
}
|
||||
if (string.Equals(tmp, "null", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
idx += 4;
|
||||
InsertNode(null, JsonElementType.Null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx + 4 < json.Length)
|
||||
{
|
||||
string tmp = json.Substring(idx, 5);
|
||||
if (string.Equals(tmp, "false", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
idx += 5;
|
||||
InsertNode(false, JsonElementType.Boolean);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessNumber();
|
||||
}
|
||||
|
||||
private void ProcessNumber()
|
||||
{
|
||||
int dot = 0;
|
||||
int e = 0;
|
||||
int startIndex = idx;
|
||||
|
||||
while (idx < json.Length && numericalChars.Contains(json[idx]))
|
||||
{
|
||||
if (json[idx] == '.')
|
||||
dot++;
|
||||
else if (json[idx] == 'e' || json[idx] == 'E')
|
||||
e++;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (startIndex == idx)
|
||||
{
|
||||
if (idx + 7 < json.Length)
|
||||
{
|
||||
string tmp = json.Substring(idx, 8);
|
||||
if (string.Equals(tmp, "Infinity", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
idx += 8;
|
||||
InsertNode(Double.PositiveInfinity, JsonElementType.Double);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx + 2 < json.Length)
|
||||
{
|
||||
string tmp = json.Substring(idx, 3);
|
||||
if (string.Equals(tmp, "nan", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
idx += 3;
|
||||
InsertNode(Double.NaN, JsonElementType.Double);
|
||||
return;
|
||||
}
|
||||
if (string.Equals(tmp, "inf", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
idx += 3;
|
||||
InsertNode(Double.PositiveInfinity, JsonElementType.Double);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidJsonException("Json Parser: content error. Unparseable content. Offset " + idx);
|
||||
}
|
||||
|
||||
if (e > 1 || dot > 1)
|
||||
throw new InvalidJsonException("Json Parser: content error. Unparseable content, invalid number. Offset " + idx);
|
||||
|
||||
string numberString = json.Substring(startIndex, idx - startIndex);
|
||||
|
||||
if (e > 0 || dot > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
double value = Convert.ToDouble(numberString);
|
||||
InsertNode(value, JsonElementType.Double);
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidJsonException("Json Parser: content error. Invalid number. Offset " + idx, ex);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
long value = Convert.ToInt64(numberString);
|
||||
InsertNode(value, JsonElementType.Int64);
|
||||
return;
|
||||
}
|
||||
catch (OverflowException)
|
||||
{
|
||||
UInt64 value = Convert.ToUInt64(numberString);
|
||||
InsertNode(value, JsonElementType.UInt64);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidJsonException("Json Parser: content error. Invalid number. Offset " + idx, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user