ProjectDDD/Assets/_Datas/SLShared/SLSystem/SLEntitySpace/Loader/JsonLoader.cs
2025-06-17 20:47:57 +09:00

476 lines
15 KiB (Stored with Git LFS)
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Superlazy.Loader
{
public static class JsonLoader
{
private static void ParseElement(SLEntity context, string token, string tokenName, bool quoted)
{
if (context.HasChild(tokenName))
{
throw new Exception($"{tokenName} Already has child");
}
if (quoted)
{
context[tokenName] = token;
return;
}
{
if (double.TryParse(token, out var val))
{
if (val < 1 || val > int.MaxValue || (token.IsRight(".0") == false && token.Contains('.')))
{
context[tokenName] = val;
}
else
{
context[tokenName] = (int)val;
}
}
else
{
context[tokenName] = token;
}
}
}
public static SLEntity Parse(SLEntity root, string aJSON)
{
var stack = new Stack<SLEntity>();
SLEntity context = null;
var i = 0;
var Token = new StringBuilder();
var TokenName = "";
var QuoteMode = false;
var TokenIsQuoted = false;
while (i < aJSON.Length)
{
switch (aJSON[i])
{
case '{':
if (QuoteMode)
{
Token.Append(aJSON[i]);
break;
}
if (context is null)
{
stack.Push(root);
}
else
{
stack.Push(context[TokenName]);
}
TokenName = "";
Token.Length = 0;
context = stack.Peek();
break;
case '[':
if (QuoteMode)
{
Token.Append(aJSON[i]);
break;
}
stack.Push(SLEntity.Empty);
if (context != null)
{
context[TokenName] = stack.Peek();
}
TokenName = "";
Token.Length = 0;
context = stack.Peek();
break;
case '}':
case ']':
if (QuoteMode)
{
Token.Append(aJSON[i]);
break;
}
if (stack.Count == 0)
throw new Exception("JSON Parse: Too many closing brackets\n" + aJSON);
stack.Peek().EndModified();
stack.Pop();
if (Token.Length > 0 || TokenIsQuoted)
{
ParseElement(context, Token.ToString(), TokenName, TokenIsQuoted);
TokenIsQuoted = false;
}
TokenName = "";
Token.Length = 0;
if (stack.Count > 0)
context = stack.Peek();
break;
case ':':
if (QuoteMode)
{
Token.Append(aJSON[i]);
break;
}
TokenName = Token.ToString();
Token.Length = 0;
TokenIsQuoted = false;
break;
case '"':
QuoteMode ^= true;
TokenIsQuoted |= QuoteMode;
break;
case ',':
if (QuoteMode)
{
Token.Append(aJSON[i]);
break;
}
if (Token.Length > 0 || TokenIsQuoted)
{
ParseElement(context, Token.ToString(), TokenName, TokenIsQuoted);
}
TokenName = "";
Token.Length = 0;
TokenIsQuoted = false;
break;
case '\r':
case '\n':
break;
case ' ':
case '\t':
if (QuoteMode)
Token.Append(aJSON[i]);
break;
case '\\':
++i;
if (QuoteMode)
{
var C = aJSON[i];
switch (C)
{
case 't':
Token.Append('\t');
break;
case 'r':
Token.Append('\r');
break;
case 'n':
Token.Append('\n');
break;
case 'b':
Token.Append('\b');
break;
case 'f':
Token.Append('\f');
break;
case 'u':
{
var s = aJSON.Substring(i + 1, 4);
Token.Append((char)int.Parse(
s,
System.Globalization.NumberStyles.AllowHexSpecifier));
i += 4;
break;
}
default:
Token.Append(C);
break;
}
}
break;
default:
Token.Append(aJSON[i]);
break;
}
++i;
}
if (QuoteMode)
{
throw new Exception("JSON Parse: Quotation marks seems to be messed up.\n" + aJSON);
}
return context;
}
public static SLEntity LoadJson(string v)
{
var obj = Parse(SLEntity.Empty, v);
return obj;
}
public static SLEntity LoadJson(SLEntity root, string v)
{
var obj = Parse(root, v);
return obj;
}
public static string SaveToJson(SLEntity entity, int sortDepth = -1, bool indent = false)
{
if (indent)
{
var collection = SaveObj(null, entity, new StringBuilder(1024 * 1024 * 5), 0, sortDepth);
return collection.ToString();
}
else
{
var collection = SaveObjNoIndent(null, entity, new StringBuilder(1024 * 1024 * 5), 0, sortDepth);
return collection.ToString();
}
}
private static StringBuilder SaveObj(string id, SLEntity entity, StringBuilder builder, int depth, int sortDepth = -1)
{
if (depth > 0) builder.Append(' ', depth);
if (entity.IsValue)
{
if (entity.IsNumeric)
{
builder.Append('\"').Append(id).Append("\": ").Append(RoundAndFormat(entity));
}
else
{
builder.Append('\"').Append(id).Append("\": \"");
foreach (var ch in entity.ToString())
{
if (ch == '"')
{
builder.Append("\\\"");
}
else if (ch == '\n')
{
builder.Append("\\n");
}
else
{
builder.Append(ch);
}
}
builder.Append("\"");
}
}
else
{
if (depth != 0)
{
builder.Append('\"').Append(id).Append("\": {\r\n");
}
else
{
builder.Append("{\r\n");
}
if (sortDepth != -1 && depth > sortDepth)
{
var count = entity.Count();
foreach (var child in entity.OrderBy(u =>
{
if (int.TryParse(u.ID, out var numberID))
{
return string.Format("{0}{1:0000}", u.IsValue ? 0 : 1, numberID);
}
return string.Format("{0}{1}", u.IsValue ? 0 : 1, u.ID);
}))
{
SaveObj(child.ID, child, builder, depth + 2, sortDepth);
count -= 1;
if (count > 0)
{
builder.Append(",\r\n");
}
else
{
builder.Append("\r\n");
}
}
}
else
{
var count = entity.Count();
foreach (var child in entity)
{
SaveObj(child.ID, child, builder, depth + 2, sortDepth);
count -= 1;
if (count > 0)
{
builder.Append(",\r\n");
}
else
{
builder.Append("\r\n");
}
}
}
if (depth > 0) builder.Append(' ', depth);
builder.Append('}');
}
return builder;
}
private static StringBuilder SaveObjNoIndent(string id, SLEntity entity, StringBuilder builder, int depth, int sortDepth = -1)
{
if (entity.IsValue)
{
if (entity.IsNumeric)
{
//builder.Append('\"').Append(id).Append("\":").Append(RoundAndFormat(entity)); // 인덴트가 없다는건 데이터 전송용이므로 빠르게 진행
builder.Append('\"').Append(id).Append("\":").Append(entity.ToString());
}
else
{
builder.Append('\"').Append(id).Append("\":\"");
foreach (var ch in entity.ToString())
{
if (ch == '"')
{
builder.Append("\\\"");
}
else if (ch == '\n')
{
builder.Append("\\n");
}
else
{
builder.Append(ch);
}
}
builder.Append("\"");
}
}
else
{
if (depth != 0)
{
builder.Append('\"').Append(id).Append("\":{");
}
else
{
builder.Append("{");
}
if (sortDepth != -1 && depth > sortDepth)
{
var count = entity.Count();
foreach (var child in entity.OrderBy(u =>
{
if (int.TryParse(u.ID, out var numberID))
{
return string.Format("{0}{1:0000}", u.IsValue ? 0 : 1, numberID);
}
return string.Format("{0}{1}", u.IsValue ? 0 : 1, u.ID);
}))
{
SaveObjNoIndent(child.ID, child, builder, depth + 2, sortDepth);
count -= 1;
if (count > 0)
{
builder.Append(",");
}
}
}
else
{
var count = entity.Count();
foreach (var child in entity)
{
SaveObjNoIndent(child.ID, child, builder, depth + 2, sortDepth);
count -= 1;
if (count > 0)
{
builder.Append(",");
}
}
}
builder.Append('}');
}
return builder;
}
public static string RoundAndFormat(SLEntity num)
{
var str = num.ToString();
var decimalIndex = str.IndexOf('.');
if (decimalIndex >= 0)
{
// 2자리까지 반올림 대상으로 한다
var roundIndex = str.Length - 2;
if (str[roundIndex] != '9' && str[roundIndex] != '0')
{
roundIndex = str.Length - 3;
}
var nine = false;
if (str[roundIndex] == '9') nine = true;
while (roundIndex > decimalIndex && ((nine && str[roundIndex] == '9') || (nine == false && str[roundIndex] == '0')))
{
roundIndex--;
}
if (str.Length > decimalIndex + 6 && roundIndex <= decimalIndex)
{
// 반올림정수
return ((int)Math.Round((double)num)).ToString();
}
if (str.Length < decimalIndex + 6)
{
return str; // 단순한 소수
}
if (roundIndex < str.Length - 6) // 3~4자리 이상 반복
{
return string.Format("{0:f" + (roundIndex - decimalIndex) + "}", (double)num);
}
else
{
return str; // 복잡한 소수
}
}
else
{
return str; // 정수
}
}
}
}