Files
CC-Framework.BriskGameServer/Assets/BriskSdk/Runtime/Core/BriskJson.cs

444 lines
12 KiB
C#

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;
}
}
}