You've already forked CC-Framework.BriskGameServer
Initial Brisk Unity SDK project
This commit is contained in:
443
Assets/BriskSdk/Runtime/Core/BriskJson.cs
Normal file
443
Assets/BriskSdk/Runtime/Core/BriskJson.cs
Normal file
@@ -0,0 +1,443 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
internal static class BriskJson
|
||||
{
|
||||
public static object Deserialize(string json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Parser.Parse(json);
|
||||
}
|
||||
|
||||
public static string Serialize(object value)
|
||||
{
|
||||
return Serializer.Serialize(value);
|
||||
}
|
||||
|
||||
private sealed class Parser : IDisposable
|
||||
{
|
||||
private readonly StringReader _reader;
|
||||
|
||||
private Parser(string json)
|
||||
{
|
||||
_reader = new StringReader(json);
|
||||
}
|
||||
|
||||
public static object Parse(string json)
|
||||
{
|
||||
using (var parser = new Parser(json))
|
||||
{
|
||||
return parser.ParseValue();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_reader.Dispose();
|
||||
}
|
||||
|
||||
private object ParseValue()
|
||||
{
|
||||
EatWhitespace();
|
||||
if (_reader.Peek() == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (PeekChar)
|
||||
{
|
||||
case '{':
|
||||
return ParseObject();
|
||||
case '[':
|
||||
return ParseArray();
|
||||
case '"':
|
||||
return ParseString();
|
||||
case 't':
|
||||
return ParseTrue();
|
||||
case 'f':
|
||||
return ParseFalse();
|
||||
case 'n':
|
||||
return ParseNull();
|
||||
default:
|
||||
return ParseNumber();
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, object> ParseObject()
|
||||
{
|
||||
var table = new Dictionary<string, object>();
|
||||
ReadChar();
|
||||
while (true)
|
||||
{
|
||||
EatWhitespace();
|
||||
if (PeekChar == '}')
|
||||
{
|
||||
ReadChar();
|
||||
return table;
|
||||
}
|
||||
|
||||
var key = ParseString();
|
||||
EatWhitespace();
|
||||
ReadChar();
|
||||
var value = ParseValue();
|
||||
table[key] = value;
|
||||
EatWhitespace();
|
||||
if (PeekChar == ',')
|
||||
{
|
||||
ReadChar();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PeekChar == '}')
|
||||
{
|
||||
ReadChar();
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<object> ParseArray()
|
||||
{
|
||||
var array = new List<object>();
|
||||
ReadChar();
|
||||
while (true)
|
||||
{
|
||||
EatWhitespace();
|
||||
if (PeekChar == ']')
|
||||
{
|
||||
ReadChar();
|
||||
return array;
|
||||
}
|
||||
|
||||
array.Add(ParseValue());
|
||||
EatWhitespace();
|
||||
if (PeekChar == ',')
|
||||
{
|
||||
ReadChar();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PeekChar == ']')
|
||||
{
|
||||
ReadChar();
|
||||
return array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string ParseString()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
ReadChar();
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (_reader.Peek() == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var ch = ReadChar();
|
||||
if (ch == '"')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch == '\\')
|
||||
{
|
||||
if (_reader.Peek() == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ch = ReadChar();
|
||||
switch (ch)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
builder.Append(ch);
|
||||
break;
|
||||
case 'b':
|
||||
builder.Append('\b');
|
||||
break;
|
||||
case 'f':
|
||||
builder.Append('\f');
|
||||
break;
|
||||
case 'n':
|
||||
builder.Append('\n');
|
||||
break;
|
||||
case 'r':
|
||||
builder.Append('\r');
|
||||
break;
|
||||
case 't':
|
||||
builder.Append('\t');
|
||||
break;
|
||||
case 'u':
|
||||
builder.Append((char)Convert.ToInt32(ReadChars(4), 16));
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.Append(ch);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private object ParseNumber()
|
||||
{
|
||||
var token = NextToken();
|
||||
if (token.IndexOf('.') >= 0 || token.IndexOf('e') >= 0 || token.IndexOf('E') >= 0)
|
||||
{
|
||||
double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out var doubleValue);
|
||||
return doubleValue;
|
||||
}
|
||||
|
||||
long.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue);
|
||||
return longValue;
|
||||
}
|
||||
|
||||
private bool ParseTrue()
|
||||
{
|
||||
ReadChars(4);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ParseFalse()
|
||||
{
|
||||
ReadChars(5);
|
||||
return false;
|
||||
}
|
||||
|
||||
private object ParseNull()
|
||||
{
|
||||
ReadChars(4);
|
||||
return null;
|
||||
}
|
||||
|
||||
private string NextToken()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
while (_reader.Peek() != -1)
|
||||
{
|
||||
var ch = PeekChar;
|
||||
if (char.IsWhiteSpace(ch) || ch == ',' || ch == ']' || ch == '}')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
builder.Append(ReadChar());
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private void EatWhitespace()
|
||||
{
|
||||
while (_reader.Peek() != -1 && char.IsWhiteSpace(PeekChar))
|
||||
{
|
||||
_reader.Read();
|
||||
}
|
||||
}
|
||||
|
||||
private string ReadChars(int count)
|
||||
{
|
||||
var buffer = new char[count];
|
||||
_reader.Read(buffer, 0, count);
|
||||
return new string(buffer);
|
||||
}
|
||||
|
||||
private char PeekChar => Convert.ToChar(_reader.Peek());
|
||||
|
||||
private char ReadChar() => Convert.ToChar(_reader.Read());
|
||||
}
|
||||
|
||||
private static class Serializer
|
||||
{
|
||||
public static string Serialize(object value)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
SerializeValue(value, builder);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static void SerializeValue(object value, StringBuilder builder)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
builder.Append("null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is string str)
|
||||
{
|
||||
SerializeString(str, builder);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is bool boolean)
|
||||
{
|
||||
builder.Append(boolean ? "true" : "false");
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is IDictionary dictionary)
|
||||
{
|
||||
SerializeObject(dictionary, builder);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is IEnumerable enumerable && !(value is string))
|
||||
{
|
||||
SerializeArray(enumerable, builder);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsNumeric(value))
|
||||
{
|
||||
builder.Append(Convert.ToString(value, CultureInfo.InvariantCulture));
|
||||
return;
|
||||
}
|
||||
|
||||
SerializeObject(ToReflectionDictionary(value), builder);
|
||||
}
|
||||
|
||||
private static void SerializeObject(IDictionary dictionary, StringBuilder builder)
|
||||
{
|
||||
var first = true;
|
||||
builder.Append('{');
|
||||
foreach (DictionaryEntry entry in dictionary)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
builder.Append(',');
|
||||
}
|
||||
|
||||
SerializeString(Convert.ToString(entry.Key, CultureInfo.InvariantCulture), builder);
|
||||
builder.Append(':');
|
||||
SerializeValue(entry.Value, builder);
|
||||
first = false;
|
||||
}
|
||||
|
||||
builder.Append('}');
|
||||
}
|
||||
|
||||
private static void SerializeArray(IEnumerable array, StringBuilder builder)
|
||||
{
|
||||
var first = true;
|
||||
builder.Append('[');
|
||||
foreach (var item in array)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
builder.Append(',');
|
||||
}
|
||||
|
||||
SerializeValue(item, builder);
|
||||
first = false;
|
||||
}
|
||||
|
||||
builder.Append(']');
|
||||
}
|
||||
|
||||
private static void SerializeString(string value, StringBuilder builder)
|
||||
{
|
||||
builder.Append('"');
|
||||
foreach (var ch in value)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case '"':
|
||||
builder.Append("\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
builder.Append("\\\\");
|
||||
break;
|
||||
case '\b':
|
||||
builder.Append("\\b");
|
||||
break;
|
||||
case '\f':
|
||||
builder.Append("\\f");
|
||||
break;
|
||||
case '\n':
|
||||
builder.Append("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
builder.Append("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
builder.Append("\\t");
|
||||
break;
|
||||
default:
|
||||
if (ch < 32)
|
||||
{
|
||||
builder.Append("\\u");
|
||||
builder.Append(((int)ch).ToString("x4"));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(ch);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
builder.Append('"');
|
||||
}
|
||||
|
||||
private static bool IsNumeric(object value)
|
||||
{
|
||||
switch (Type.GetTypeCode(value.GetType()))
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.UInt64:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.Decimal:
|
||||
case TypeCode.Double:
|
||||
case TypeCode.Single:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static IDictionary ToReflectionDictionary(object value)
|
||||
{
|
||||
var result = new Dictionary<string, object>();
|
||||
var type = value.GetType();
|
||||
|
||||
foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public))
|
||||
{
|
||||
result[field.Name] = field.GetValue(value);
|
||||
}
|
||||
|
||||
foreach (var property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
|
||||
{
|
||||
if (!property.CanRead || property.GetIndexParameters().Length > 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result[property.Name] = property.GetValue(value, null);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user