using System.Globalization; using System.Text; using System.Text.Json; using Nbt.Tag; namespace Nbt.Serialization; public static class JsonNbt { private static readonly JsonEncodedText TypeProperty = JsonEncodedText.Encode("type"); private static readonly JsonEncodedText ValueProperty = JsonEncodedText.Encode("value"); #region Serialize #region Lossless public enum SerializationStyle { Compact, Spaced, Indented } public class SerializerOptions { public static readonly SerializerOptions Default = new(); public SerializationStyle Style { get; init; } = SerializationStyle.Compact; public string Indent { get; init; } = " "; } public static string Serialize(INbtTag tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtList tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtCompound tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtArray tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtValue tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtArray tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtArray tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtArray tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtValue tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtValue tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtValue tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtValue tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtValue tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtValue tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static string Serialize(INbtValue tag, SerializerOptions? options = null) => Serialize(options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtTag tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtList tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtCompound tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtArray tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtArray tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtArray tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtArray tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => Serialize(writer, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtTag tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtList tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtCompound tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtArray tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtArray tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtArray tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtArray tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); public static void Serialize(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => Serialize(stream, encoding, options, serializer => serializer.Serialize(tag)); private static string Serialize(SerializerOptions? options, Action serialize) { using var writer = new StringWriter(); Serialize(writer, options, serialize); return writer.ToString(); } private static void Serialize(Stream stream, Encoding? encoding, SerializerOptions? options, Action serialize) { using var writer = new StreamWriter(stream, encoding ?? Encoding.Default); Serialize(writer, options, serialize); } private static void Serialize(TextWriter writer, SerializerOptions? options, Action serialize) { using var serializer = new LosslessSerializer(writer, options); serialize(serializer); } private sealed class LosslessSerializer(TextWriter writer, SerializerOptions? options, bool leaveOpen = false) : IDisposable { private readonly JsonWriter writer = new(writer, options, leaveOpen); public void Serialize(INbtTag tag) { switch (tag) { case INbtArray arrayTag: Serialize(arrayTag); break; case INbtValue valueTag: Serialize(valueTag); break; case INbtList listTag: Serialize(listTag); break; case INbtCompound compoundTag: Serialize(compoundTag); break; } } public void Serialize(INbtList tag) { using (new NbtObjectMeta(writer, tag)) { writer.WriteStartArray(); foreach (var entry in tag) { writer.WriteElement(entry, Serialize); } writer.WriteEndArray(); } } public void Serialize(INbtCompound tag) { using (new NbtObjectMeta(writer, tag)) { writer. WriteStartObject(); foreach (var entry in tag as IEnumerable) { writer.WriteProperty(entry.Name, entry.Tag, Serialize); } writer.WriteEndObject(); } } public void Serialize(INbtArray tag) { switch (tag) { case INbtArray byteArrayTag: Serialize(byteArrayTag); break; case INbtArray intArrayTag: Serialize(intArrayTag); break; case INbtArray longArrayTag: Serialize(longArrayTag); break; } } public void Serialize(INbtValue valueTag) { switch (valueTag) { case INbtValue tag: Serialize(tag); break; case INbtValue tag: Serialize(tag); break; case INbtValue tag: Serialize(tag); break; case INbtValue tag: Serialize(tag); break; case INbtValue tag: Serialize(tag); break; case INbtValue tag: Serialize(tag); break; case INbtValue tag: Serialize(tag); break; } } public void Serialize(INbtArray tag) => Serialize(tag, Serialize); public void Serialize(INbtArray tag) => Serialize(tag, Serialize); public void Serialize(INbtArray tag) => Serialize(tag, Serialize); public void Serialize(INbtValue tag) => Serialize(tag, Serialize); public void Serialize(INbtValue tag) => Serialize(tag, Serialize); public void Serialize(INbtValue tag) => Serialize(tag, Serialize); public void Serialize(INbtValue tag) => Serialize(tag, Serialize); public void Serialize(INbtValue tag) => Serialize(tag, Serialize); public void Serialize(INbtValue tag) => Serialize(tag, Serialize); public void Serialize(INbtValue tag) => Serialize(tag, Serialize); private void Serialize(INbtArray tag, Action writeValue) where T : notnull { using (new NbtObjectMeta(writer, tag)) { writer.WriteStartArray(); foreach (var element in tag) { writer.WriteElement(element, writeValue); } writer.WriteEndArray(); } } private void Serialize(INbtValue tag, Action writeValue) where T : notnull { using (new NbtObjectMeta(writer, tag)) { writeValue(tag.Value); } } private void Serialize(sbyte value) => writer.WriteValue(value); private void Serialize(short value) => writer.WriteValue(value); private void Serialize(int value) => writer.WriteValue(value); private void Serialize(long value) => writer.WriteValue(value); private void Serialize(float value) => writer.WriteValue(value); private void Serialize(double value) => writer.WriteValue(value); private void Serialize(string value) => writer.WriteValue(value); public void Dispose() { writer.Dispose(); GC.SuppressFinalize(this); } } #endregion #region Lossy public static string ToJson(INbtTag tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtList tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtCompound tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtArray tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtValue tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtArray tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtArray tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtArray tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtValue tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtValue tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtValue tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtValue tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtValue tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtValue tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static string ToJson(INbtValue tag, SerializerOptions? options = null) => ToJson(options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtTag tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtList tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtCompound tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtArray tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtArray tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtArray tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtArray tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(TextWriter writer, INbtValue tag, SerializerOptions? options = null) => ToJson(writer, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtTag tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtList tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtCompound tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtArray tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtArray tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtArray tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtArray tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); public static void ToJson(Stream stream, INbtValue tag, Encoding? encoding = null, SerializerOptions? options = null) => ToJson(stream, encoding, options, serializer => serializer.ToJson(tag)); private static string ToJson(SerializerOptions? options, Action serialize) { using var writer = new StringWriter(); ToJson(writer, options, serialize); return writer.ToString(); } private static void ToJson(Stream stream, Encoding? encoding, SerializerOptions? options, Action serialize) { using var writer = new StreamWriter(stream, encoding ?? Encoding.Default); ToJson(writer, options, serialize); } private static void ToJson(TextWriter writer, SerializerOptions? options, Action serialize) { using var serializer = new LossySerializer(writer, options); serialize(serializer); } private sealed class LossySerializer(TextWriter writer, SerializerOptions? options, bool leaveOpen = false) : IDisposable { private readonly JsonWriter writer = new(writer, options, leaveOpen); public void ToJson(INbtTag tag) { switch (tag) { case INbtArray arrayTag: ToJson(arrayTag); break; case INbtValue valueTag: ToJson(valueTag); break; case INbtList listTag: ToJson(listTag); break; case INbtCompound compoundTag: ToJson(compoundTag); break; } } public void ToJson(INbtList tag) { writer.WriteStartArray(); foreach (var entry in tag) { writer.WriteElement(entry, ToJson); } writer.WriteEndArray(); } public void ToJson(INbtCompound tag) { writer.WriteStartObject(); foreach (var entry in tag as IEnumerable) { writer.WriteProperty(entry.Name, entry.Tag, ToJson); } writer.WriteEndObject(); } public void ToJson(INbtArray tag) { switch (tag) { case INbtArray byteArrayTag: ToJson(byteArrayTag); break; case INbtArray intArrayTag: ToJson(intArrayTag); break; case INbtArray longArrayTag: ToJson(longArrayTag); break; } } public void ToJson(INbtValue valueTag) { switch (valueTag) { case INbtValue tag: ToJson(tag); break; case INbtValue tag: ToJson(tag); break; case INbtValue tag: ToJson(tag); break; case INbtValue tag: ToJson(tag); break; case INbtValue tag: ToJson(tag); break; case INbtValue tag: ToJson(tag); break; case INbtValue tag: ToJson(tag); break; } } public void ToJson(INbtArray tag) => ToJson(tag, ToJson); public void ToJson(INbtArray tag) => ToJson(tag, ToJson); public void ToJson(INbtArray tag) => ToJson(tag, ToJson); public void ToJson(INbtValue tag) => ToJson(tag, ToJson); public void ToJson(INbtValue tag) => ToJson(tag, ToJson); public void ToJson(INbtValue tag) => ToJson(tag, ToJson); public void ToJson(INbtValue tag) => ToJson(tag, ToJson); public void ToJson(INbtValue tag) => ToJson(tag, ToJson); public void ToJson(INbtValue tag) => ToJson(tag, ToJson); public void ToJson(INbtValue tag) => ToJson(tag, ToJson); private void ToJson(INbtArray tag, Action writeValue) where T : notnull { writer.WriteStartArray(); foreach (var e in tag) { writer.WriteElement(e, writeValue); } writer.WriteEndArray(); } private static void ToJson(INbtValue tag, Action writeValue) where T : notnull => writeValue(tag.Value); private void ToJson(sbyte value) => writer.WriteValue(value); private void ToJson(short value) => writer.WriteValue(value); private void ToJson(int value) => writer.WriteValue(value); private void ToJson(long value) => writer.WriteValue(value); private void ToJson(float value) => writer.WriteValue(value); private void ToJson(double value) => writer.WriteValue(value); private void ToJson(string value) => writer.WriteValue(value); public void Dispose() { writer.Dispose(); GC.SuppressFinalize(this); } } #endregion private sealed class JsonWriter(TextWriter writer, SerializerOptions? options, bool leaveOpen = false) : IDisposable { private readonly TextWriter writer = writer; private readonly SerializerOptions options = options ?? SerializerOptions.Default; private readonly bool leaveOpen = leaveOpen; private int depth = 0; private Context context = new(null, 0); public void WriteStartArray() { writer.Write('['); depth++; context = new(context, 1); } public void WriteEndArray() { depth--; if (context.length > 0 && options.Style is SerializationStyle.Indented) { writer.WriteLine(); WriteIndent(); } context = context.parent!; writer.Write(']'); } public void WriteStartObject() { writer.Write('{'); depth++; context = new(context, 2); } public void WriteEndObject() { depth--; if (context.length > 0 && options.Style is SerializationStyle.Indented) { writer.WriteLine(); WriteIndent(); } context = context.parent!; writer.Write('}'); } public void WriteElement(byte value) => WriteElement(value, WriteValue); public void WriteElement(sbyte value) => WriteElement(value, WriteValue); public void WriteElement(short value) => WriteElement(value, WriteValue); public void WriteElement(ushort value) => WriteElement(value, WriteValue); public void WriteElement(int value) => WriteElement(value, WriteValue); public void WriteElement(uint value) => WriteElement(value, WriteValue); public void WriteElement(long value) => WriteElement(value, WriteValue); public void WriteElement(ulong value) => WriteElement(value, WriteValue); public void WriteElement(float value) => WriteElement(value, WriteValue); public void WriteElement(double value) => WriteElement(value, WriteValue); public void WriteElement(string value) => WriteElement(value, WriteValue); public void WriteElement(T value, Action elementWriter) { WriteSeparator(); elementWriter(value); context.length++; } public void WriteProperty(string name, byte value) => WriteProperty(name, value, WriteValue); public void WriteProperty(string name, sbyte value) => WriteProperty(name, value, WriteValue); public void WriteProperty(string name, short value) => WriteProperty(name, value, WriteValue); public void WriteProperty(string name, ushort value) => WriteProperty(name, value, WriteValue); public void WriteProperty(string name, int value) => WriteProperty(name, value, WriteValue); public void WriteProperty(string name, uint value) => WriteProperty(name, value, WriteValue); public void WriteProperty(string name, long value) => WriteProperty(name, value, WriteValue); public void WriteProperty(string name, ulong value) => WriteProperty(name, value, WriteValue); public void WriteProperty(string name, float value) => WriteProperty(name, value, WriteValue); public void WriteProperty(string name, double value) => WriteProperty(name, value, WriteValue); public void WriteProperty(string name, string value) => WriteProperty(name, value, WriteValue); public void WritePropertyName(string name) { WriteSeparator(); WriteValue(name); writer.Write(':'); if (options.Style is not SerializationStyle.Compact) { writer.Write(' '); } } public void WriteProperty(string name, T value, Action valueWriter) { WritePropertyName(name); valueWriter(value); context.length++; } public void WriteValue(byte value) => writer.Write(value.ToString(CultureInfo.InvariantCulture)); public void WriteValue(sbyte value) => writer.Write(value.ToString(CultureInfo.InvariantCulture)); public void WriteValue(short value) => writer.Write(value.ToString(CultureInfo.InvariantCulture)); public void WriteValue(ushort value) => writer.Write(value.ToString(CultureInfo.InvariantCulture)); public void WriteValue(int value) => writer.Write(value.ToString(CultureInfo.InvariantCulture)); public void WriteValue(uint value) => writer.Write(value.ToString(CultureInfo.InvariantCulture)); public void WriteValue(long value) => writer.Write(value.ToString(CultureInfo.InvariantCulture)); public void WriteValue(ulong value) => writer.Write(value.ToString(CultureInfo.InvariantCulture)); public void WriteValue(float value) => writer.Write(value.ToString(CultureInfo.InvariantCulture)); public void WriteValue(double value) => writer.Write(value.ToString(CultureInfo.InvariantCulture)); public void WriteValue(string value) => Utils.Quote(writer, value, '"', '\\'); private void WriteSeparator() { if (context.length > 0) { writer.Write(','); if (options.Style is SerializationStyle.Spaced) { writer.Write(' '); } } if (options.Style is SerializationStyle.Indented) { writer.WriteLine(); WriteIndent(); } } private void WriteIndent() { for (var i = 0; i < depth; i++) { writer.Write(options.Indent); } } public void Dispose() { if (!leaveOpen) { writer.Dispose(); } GC.SuppressFinalize(this); } private class Context(Context? parent, byte type) { public readonly Context? parent = parent; public readonly byte type = type; public int length = 0; } } private readonly struct NbtObjectMeta : IDisposable { private readonly JsonWriter writer; public NbtObjectMeta(JsonWriter writer, INbtTag tag) { this.writer = writer; writer.WriteStartObject(); writer.WriteProperty(TypeProperty.Value, (int)tag.Type); writer.WritePropertyName(ValueProperty.Value); } public void Dispose() { writer.WriteEndObject(); } } #endregion #region Deserialize public static INbtTag Deserialize(string s) { using var reader = new StringReader(s); return Deserialize(reader); } public static INbtTag Deserialize(Stream stream) { var doc = JsonDocument.Parse(stream); return Deserialize(doc.RootElement); } public static INbtTag Deserialize(TextReader reader) { using var stream = new MemoryStream(); using (var writer = new StreamWriter(stream, encoding: Encoding.UTF8, leaveOpen: true)) { while (true) { var i = reader.Read(); if (i < 0) { break; } writer.Write((char)i); } } stream.Position = 0L; return Deserialize(stream); } private static INbtTag Deserialize(JsonElement serialized) { var tagType = (NbtTagType)serialized.GetProperty(TypeProperty.Value).GetByte(); var value = serialized.GetProperty(ValueProperty.Value); switch (tagType) { case NbtTagType.End: throw new NbtException($"Invalid tag type: {Enum.GetName(tagType)}"); case NbtTagType.Byte: return new NbtByte(value.GetSByte()); case NbtTagType.Short: return new NbtShort(value.GetInt16()); case NbtTagType.Int: return new NbtInt(value.GetInt32()); case NbtTagType.Long: return new NbtLong(value.GetInt64()); case NbtTagType.Float: return new NbtFloat(value.GetSingle()); case NbtTagType.Double: return new NbtDouble(value.GetDouble()); case NbtTagType.String: return new NbtString(value.GetString() ?? throw new NbtException("Null string value")); case NbtTagType.ByteArray: { var bytes = new sbyte[value.GetArrayLength()]; var i = 0; foreach (var element in value.EnumerateArray()) { bytes[i++] = element.GetSByte(); } return new NbtByteArray(bytes); } case NbtTagType.IntArray: { var ints = new int[value.GetArrayLength()]; var i = 0; foreach (var element in value.EnumerateArray()) { ints[i++] = element.GetInt32(); } return new NbtIntArray(ints); } case NbtTagType.LongArray: { var longs = new long[value.GetArrayLength()]; var i = 0; foreach (var element in value.EnumerateArray()) { longs[i++] = element.GetInt64(); } return new NbtLongArray(longs); } case NbtTagType.List: { var list = new NbtList(); foreach (var element in value.EnumerateArray()) { list.Add(Deserialize(element)); } return list; } case NbtTagType.Compound: { var compound = new NbtCompound(); foreach (var property in value.EnumerateObject()) { compound.Add(property.Name, Deserialize(property.Value)); } return compound; } default: throw new UnknownTagTypeException(tagType); } } #endregion }