Json Serialization
ds5678 opened this issue · comments
Jeremy Pritts commented
#nullable disable
namespace AssetRipper.AssemblyDumper.InjectedTypes;
public static class JsonConstants
{
/// <summary>
/// The key used to specify the style of a map or sequence.
/// </summary>
public const string Style = "@style";
/// <summary>
/// The key used to specify that an array of key-value pairs should be treated as a map.
/// </summary>
public const string Map = "@map";
/// <summary>
/// The key used to specify that an object should be treated as a pair.
/// </summary>
public const string Pair = "@pair";
/// <summary>
/// The key used to specify that an array of values should be treated as a sequence.
/// </summary>
public const string Sequence = "@sequence";
/// <summary>
/// The set of values that can be used for the <see cref="Style"/> key.
/// </summary>
public static class Styles
{
/// <summary>
/// Usable on both maps and sequences.
/// </summary>
public const string Block = "Block";
/// <summary>
/// Usable only on sequences.
/// </summary>
public const string BlockCurve = "BlockCurve";
/// <summary>
/// Usable on both maps and sequences.
/// </summary>
public const string Flow = "Flow";
/// <summary>
/// Usable only on sequences.
/// </summary>
public const string Raw = "Raw";
}
}
#nullable enable
#nullable disable
using System.Runtime.CompilerServices;
using System.Text.Json.Nodes;
namespace AssetRipper.AssemblyDumper.InjectedTypes;
internal static class JsonHelper
{
/// <summary>
/// Maps appear to have default constructors, but they actually have an options parameter that is null by default.
/// </summary>
/// <returns>A new <see cref="JsonObject"/></returns>
public static JsonObject CreateMap() => new();
/// <summary>
/// Sequences appear to have default constructors, but they actually have an options parameter that is null by default.
/// </summary>
/// <returns></returns>
public static JsonArray CreateSequence() => new();
/// <summary>
/// There is a generic version of <see cref="JsonArray.Add(JsonNode)"/>, but it should not be used.
/// </summary>
/// <param name="sequence"></param>
/// <param name="node"></param>
public static void Append(this JsonArray sequence, JsonNode node) => sequence.Add(node);
public static void Append(this JsonObject map, string key, JsonNode node) => map.Add(key, node);
//There is exactly one overload of JsonValue.Create for each primitive type.
//Each one is a nongeneric static method that takes a value and a set of options, in that order.
//The options can and should be null.
public static JsonValue CreateBoolean(bool value) => JsonValue.Create(value);
public static JsonValue CreateChar(char value) => JsonValue.Create(value);
public static JsonValue CreateByte(byte value) => JsonValue.Create(value);
public static JsonValue CreateSByte(sbyte value) => JsonValue.Create(value);
public static JsonValue CreateUShort(ushort value) => JsonValue.Create(value);
public static JsonValue CreateShort(short value) => JsonValue.Create(value);
public static JsonValue CreateUInt(uint value) => JsonValue.Create(value);
public static JsonValue CreateInt(int value) => JsonValue.Create(value);
public static JsonValue CreateULong(ulong value) => JsonValue.Create(value);
public static JsonValue CreateLong(long value) => JsonValue.Create(value);
public static JsonValue CreateFloat(float value) => JsonValue.Create(value);
public static JsonValue CreateDouble(double value) => JsonValue.Create(value);
public static JsonValue CreateString(string value) => JsonValue.Create(value);
public static JsonValue CreateUtf8String(Utf8String value) => JsonValue.Create(value.String);
/// <summary>
/// Create a <see cref="JsonNode"/> for a byte array.
/// </summary>
/// <remarks>
/// This is NOT base 64 encoded, but instead a hex string.
/// AssetRipper 0.3.4.0 and earlier used lower case hex digits,
/// but <see cref="Convert.ToHexString(ReadOnlySpan{byte})"/> uses upper case hex digits and is very efficient.
/// </remarks>
/// <param name="value">The array of bytes to be encoded.</param>
/// <returns>A <see cref="JsonValue"/> containing the hex string.</returns>
public static JsonValue CreateByteArray(byte[] value)
{
//Converting to span eliminates a null check.
return JsonValue.Create(Convert.ToHexString(new ReadOnlySpan<byte>(value)));
}
public static JsonValue Create<T>(T value)
{
if (typeof(T) == typeof(bool))
{
return JsonValue.Create(Unsafe.As<T, bool>(ref value));
}
else if (typeof(T) == typeof(char))
{
return JsonValue.Create(Unsafe.As<T, char>(ref value));
}
else if (typeof(T) == typeof(byte))
{
return JsonValue.Create(Unsafe.As<T, byte>(ref value));
}
else if (typeof(T) == typeof(sbyte))
{
return JsonValue.Create(Unsafe.As<T, sbyte>(ref value));
}
else if (typeof(T) == typeof(ushort))
{
return JsonValue.Create(Unsafe.As<T, ushort>(ref value));
}
else if (typeof(T) == typeof(short))
{
return JsonValue.Create(Unsafe.As<T, short>(ref value));
}
else if (typeof(T) == typeof(uint))
{
return JsonValue.Create(Unsafe.As<T, uint>(ref value));
}
else if (typeof(T) == typeof(int))
{
return JsonValue.Create(Unsafe.As<T, int>(ref value));
}
else if (typeof(T) == typeof(ulong))
{
return JsonValue.Create(Unsafe.As<T, ulong>(ref value));
}
else if (typeof(T) == typeof(long))
{
return JsonValue.Create(Unsafe.As<T, long>(ref value));
}
else if (typeof(T) == typeof(float))
{
return JsonValue.Create(Unsafe.As<T, float>(ref value));
}
else if (typeof(T) == typeof(double))
{
return JsonValue.Create(Unsafe.As<T, double>(ref value));
}
else if (typeof(T) == typeof(string))
{
return JsonValue.Create(Unsafe.As<T, string>(ref value));
}
else if (typeof(T) == typeof(Utf8String))
{
return JsonValue.Create(Unsafe.As<T, Utf8String>(ref value).String);
}
else if (typeof(T) == typeof(byte[]))
{
return CreateByteArray(Unsafe.As<T, byte[]>(ref value));
}
else
{
return ThrowNotSupportException();
}
[DoesNotReturn]
static JsonValue ThrowNotSupportException() => throw new NotSupportedException($"Type {typeof(T)} is not supported.");
}
public static void AppendTypelessData(this JsonObject map, string name, byte[] data)
{
const string TypelessdataName = "_typelessdata";
map.Add(name, data.Length);
map.Add(TypelessdataName, CreateByteArray(data));
}
}
#nullable enable
Jeremy Pritts commented
I think this has been enabled by d67af04