You've already forked CC-Framework.BriskGameServer
444 lines
12 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|