From 11f3c4d70cd0ca03959dea6f1ba13b97f9b94916 Mon Sep 17 00:00:00 2001 From: Eric Freed Date: Sun, 27 Aug 2023 15:27:44 -0400 Subject: [PATCH] Removed SerializableAttribute and ISerializable interface from tags. Improved JSON output. --- SharpNBT/Tags/ArrayTag.cs | 20 +--- SharpNBT/Tags/BoolTag.cs | 24 ++-- SharpNBT/Tags/ByteArrayTag.cs | 28 +++-- SharpNBT/Tags/ByteTag.cs | 27 +++-- SharpNBT/Tags/CompoundTag.cs | 57 +++++----- SharpNBT/Tags/DoubleTag.cs | 23 ++-- SharpNBT/Tags/EndTag.cs | 9 +- SharpNBT/Tags/FloatTag.cs | 22 ++-- SharpNBT/Tags/IntArrayTag.cs | 25 +++-- SharpNBT/Tags/IntTag.cs | 22 ++-- SharpNBT/Tags/ListTag.cs | 64 ++++------- SharpNBT/Tags/LongArrayTag.cs | 30 +++-- SharpNBT/Tags/LongTag.cs | 22 ++-- SharpNBT/Tags/NumericTag.cs | 18 +-- SharpNBT/Tags/ShortTag.cs | 22 ++-- SharpNBT/Tags/StringTag.cs | 25 ++--- SharpNBT/Tags/Tag.cs | 202 ++++++++++++++++------------------ 17 files changed, 314 insertions(+), 326 deletions(-) diff --git a/SharpNBT/Tags/ArrayTag.cs b/SharpNBT/Tags/ArrayTag.cs index 71f3ec6..754f73d 100644 --- a/SharpNBT/Tags/ArrayTag.cs +++ b/SharpNBT/Tags/ArrayTag.cs @@ -2,8 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.Serialization; using System.Text; using JetBrains.Annotations; @@ -13,7 +11,7 @@ namespace SharpNBT; /// Base class for NBT tags that contain a fixed-size array of numeric types. /// /// A value type that implements . -[PublicAPI][Serializable] +[PublicAPI] public abstract class ArrayTag : Tag, IReadOnlyList where T : unmanaged, INumber { /// @@ -35,22 +33,6 @@ protected ArrayTag(TagType type, string? name, T[] value) : base(type, name) array = value; } - /// - protected ArrayTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - var _ = info.GetInt32("count"); - var value = info.GetValue("values", typeof(T[])) as T[]; - array = value ?? Array.Empty(); - } - - /// - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); - info.AddValue("count", array.Length); - info.AddValue("values", array); - } - /// public IEnumerator GetEnumerator() { diff --git a/SharpNBT/Tags/BoolTag.cs b/SharpNBT/Tags/BoolTag.cs index 2bf0591..172a796 100644 --- a/SharpNBT/Tags/BoolTag.cs +++ b/SharpNBT/Tags/BoolTag.cs @@ -1,5 +1,5 @@ using System; -using System.Runtime.Serialization; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -11,7 +11,7 @@ namespace SharpNBT; /// This tag type does not exist in the NBT specification, and is included for convenience to differentiate it from the that it is /// actually serialized as. /// -[PublicAPI][Serializable] +[PublicAPI] [Obsolete("Use the IsBool and Bool properties of ByteTag. This class will be removed in a future version.")] public class BoolTag : Tag { @@ -26,16 +26,20 @@ public BoolTag(string? name, bool value) : base(TagType.Byte, name) { Value = value; } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected BoolTag(SerializationInfo info, StreamingContext context) : base(info, context) + + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) { + if (named && Name != null) + { + writer.WriteBoolean(Name, Value); + } + else + { + writer.WriteBooleanValue(Value); + } } - + /// public override string ToString() => $"TAG_Byte({PrettyName}): {(Value ? "true" : "false")}"; diff --git a/SharpNBT/Tags/ByteArrayTag.cs b/SharpNBT/Tags/ByteArrayTag.cs index 0d27a75..80b671c 100644 --- a/SharpNBT/Tags/ByteArrayTag.cs +++ b/SharpNBT/Tags/ByteArrayTag.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.Serialization; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -13,7 +13,7 @@ namespace SharpNBT; /// While this class uses the CLS compliant (0..255), the NBT specification uses a signed value with a range of -128..127, so ensure /// the bits are equivalent for your values. /// -[PublicAPI][Serializable] +[PublicAPI] public class ByteArrayTag : ArrayTag { /// @@ -51,16 +51,24 @@ public ByteArrayTag(string? name, IEnumerable values) : base(TagType.ByteA public ByteArrayTag(string? name, ReadOnlySpan values) : base(TagType.ByteArray, name, values.ToArray()) { } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected ByteArrayTag(SerializationInfo info, StreamingContext context) : base(info, context) + + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) { + if (named && Name != null) + { + writer.WriteStartArray(Name); + } + else + { + writer.WriteStartArray(); + } + + for (var i = 0; i < Count; i++) + writer.WriteNumberValue(this[i]); + writer.WriteEndArray(); } - + /// public override string ToString() { diff --git a/SharpNBT/Tags/ByteTag.cs b/SharpNBT/Tags/ByteTag.cs index 6c1360b..ec702d2 100644 --- a/SharpNBT/Tags/ByteTag.cs +++ b/SharpNBT/Tags/ByteTag.cs @@ -1,5 +1,5 @@ using System; -using System.Runtime.Serialization; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -86,15 +86,6 @@ public ByteTag(string? name, bool value) : base(TagType.Byte, name, value ? (byt public ByteTag(string? name, sbyte value) : base(TagType.Byte, name, unchecked((byte) value)) { } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected ByteTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } /// public override string ToString() @@ -102,6 +93,22 @@ public override string ToString() object obj = IsBool ? Bool : Value; return $"TAG_Byte({PrettyName}): {obj}"; } + + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) + { + if (named && Name != null) + { + if (IsBool) + writer.WriteBoolean(Name, Bool); + else + writer.WriteNumber(Name, Value); + } + else + { + writer.WriteNumberValue(Value); + } + } /// /// Implicit conversion of this tag to a . diff --git a/SharpNBT/Tags/CompoundTag.cs b/SharpNBT/Tags/CompoundTag.cs index fded242..fbc87d5 100644 --- a/SharpNBT/Tags/CompoundTag.cs +++ b/SharpNBT/Tags/CompoundTag.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.Serialization; using System.Text; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -16,7 +16,7 @@ namespace SharpNBT; /// This along with the class define the structure of the NBT format. Children are not order-dependent, nor is order guaranteed. The /// closing does not require to be explicitly added, it will be added automatically during serialization. /// -[PublicAPI][Serializable] +[PublicAPI] public class CompoundTag : Tag, IDictionary, ICollection { private readonly Dictionary dict; @@ -42,25 +42,7 @@ public CompoundTag(string? name, IEnumerable values) : this(name) dict.Add(value.Name!, AssertName(value)); } } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected CompoundTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - var result = info.GetValue("children", typeof(Dictionary)) as Dictionary; - dict = result ?? new Dictionary(); - } - - /// - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); - info.AddValue("children", dict); - } - + /// void ICollection>.Add(KeyValuePair item) => dict.Add(item.Key, item.Value); @@ -157,6 +139,23 @@ public TTag Get(string name) where TTag : Tag return (TTag)dict[name]; } + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) + { + if (named && Name != null) + { + writer.WriteStartObject(Name); + } + else + { + writer.WriteStartObject(); + } + + foreach (var child in dict.Values) + child.WriteJson(writer, true); + writer.WriteEndObject(); + } + /// Returns a string that represents the current object. /// A string that represents the current object. /// @@ -184,18 +183,18 @@ public string PrettyPrinted(string? indent = " ") /// The name of the tag to search for. /// to recursively search children, otherwise to only search direct descendants. /// The first tag found with , otherwise if none was found. - public Tag? Find(string name, bool recursive = false) + public TTag? Find(string name, bool recursive = false) where TTag : Tag { - foreach (var tag in dict.Values) + foreach (var (key, value) in dict) { - if (string.CompareOrdinal(name, tag.Name) == 0) - return tag; + if (string.CompareOrdinal(name, key) == 0 && value is TTag result) + return result; - if (recursive && tag is CompoundTag child) + if (recursive && value is CompoundTag child) { - var result = child.Find(name, true); - if (result != null) - return result; + var nested = child.Find(name, recursive); + if (nested != null) + return nested; } } diff --git a/SharpNBT/Tags/DoubleTag.cs b/SharpNBT/Tags/DoubleTag.cs index 08559f9..660241d 100644 --- a/SharpNBT/Tags/DoubleTag.cs +++ b/SharpNBT/Tags/DoubleTag.cs @@ -1,5 +1,4 @@ -using System; -using System.Runtime.Serialization; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -7,7 +6,7 @@ namespace SharpNBT; /// /// A tag that contains a single IEEE-754 double-precision floating point number. /// -[PublicAPI][Serializable] +[PublicAPI] public class DoubleTag : NumericTag { /// @@ -18,14 +17,18 @@ public class DoubleTag : NumericTag public DoubleTag(string? name, double value) : base(TagType.Double, name, value) { } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected DoubleTag(SerializationInfo info, StreamingContext context) : base(info, context) + + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) { + if (named && Name != null) + { + writer.WriteNumber(Name, Value); + } + else + { + writer.WriteNumberValue(Value); + } } /// diff --git a/SharpNBT/Tags/EndTag.cs b/SharpNBT/Tags/EndTag.cs index 5ab9c82..2bcff26 100644 --- a/SharpNBT/Tags/EndTag.cs +++ b/SharpNBT/Tags/EndTag.cs @@ -1,4 +1,5 @@ using System.Text; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -15,7 +16,13 @@ public sealed class EndTag : Tag public EndTag() : base(TagType.End, null) { } - + + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) + { + // Do nothing + } + /// public override string ToString() => $"TAG_End"; diff --git a/SharpNBT/Tags/FloatTag.cs b/SharpNBT/Tags/FloatTag.cs index 813d8b5..4c0fd99 100644 --- a/SharpNBT/Tags/FloatTag.cs +++ b/SharpNBT/Tags/FloatTag.cs @@ -1,5 +1,4 @@ -using System; -using System.Runtime.Serialization; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -7,7 +6,7 @@ namespace SharpNBT; /// /// A tag that contains a single IEEE-754 single-precision floating point number. /// -[PublicAPI][Serializable] +[PublicAPI] public class FloatTag : NumericTag { /// @@ -18,13 +17,18 @@ public class FloatTag : NumericTag public FloatTag(string? name, float value) : base(TagType.Float, name, value) { } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected FloatTag(SerializationInfo info, StreamingContext context) : base(info, context) + + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) { + if (named && Name != null) + { + writer.WriteNumber(Name, Value); + } + else + { + writer.WriteNumberValue(Value); + } } /// diff --git a/SharpNBT/Tags/IntArrayTag.cs b/SharpNBT/Tags/IntArrayTag.cs index fc9b965..bca5e56 100644 --- a/SharpNBT/Tags/IntArrayTag.cs +++ b/SharpNBT/Tags/IntArrayTag.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -9,7 +10,7 @@ namespace SharpNBT; /// /// A tag that whose value is a contiguous sequence of 32-bit integers. /// -[PublicAPI][Serializable] +[PublicAPI] public class IntArrayTag : ArrayTag { /// @@ -48,14 +49,22 @@ public IntArrayTag(string? name, IEnumerable values) : base(TagType.IntArra public IntArrayTag(string? name, ReadOnlySpan values) : base(TagType.IntArray, name, values.ToArray()) { } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected IntArrayTag(SerializationInfo info, StreamingContext context) : base(info, context) + + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) { + if (named && Name != null) + { + writer.WriteStartArray(Name); + } + else + { + writer.WriteStartArray(); + } + + for (var i = 0; i < Count; i++) + writer.WriteNumberValue(this[i]); + writer.WriteEndArray(); } /// diff --git a/SharpNBT/Tags/IntTag.cs b/SharpNBT/Tags/IntTag.cs index a62a117..56e8e4b 100644 --- a/SharpNBT/Tags/IntTag.cs +++ b/SharpNBT/Tags/IntTag.cs @@ -1,5 +1,5 @@ using System; -using System.Runtime.Serialization; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -7,7 +7,7 @@ namespace SharpNBT; /// /// A tag that contains a single 32-bit integer value. /// -[PublicAPI][Serializable] +[PublicAPI] public class IntTag : NumericTag { /// @@ -37,14 +37,18 @@ public IntTag(string? name, int value) : base(TagType.Int, name, value) public IntTag(Tag? parent, string? name, uint value) : base(TagType.Int, name, unchecked((int) value)) { } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected IntTag(SerializationInfo info, StreamingContext context) : base(info, context) + + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) { + if (named && Name != null) + { + writer.WriteNumber(Name, Value); + } + else + { + writer.WriteNumberValue(Value); + } } /// diff --git a/SharpNBT/Tags/ListTag.cs b/SharpNBT/Tags/ListTag.cs index c152055..c976ae8 100644 --- a/SharpNBT/Tags/ListTag.cs +++ b/SharpNBT/Tags/ListTag.cs @@ -2,8 +2,8 @@ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; -using System.Runtime.Serialization; using System.Text; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -14,7 +14,7 @@ namespace SharpNBT; /// /// All child tags must be have the same value, and their value will be omitted during serialization. /// -[PublicAPI][Serializable] +[PublicAPI] public class ListTag : Tag, IList { /// @@ -56,28 +56,6 @@ public ListTag(string? name, TagType childType, IEnumerable children) : thi foreach (var item in children) list.Add(AssertType(item)); } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected ListTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - ChildType = (TagType)info.GetByte("child_type"); - var count = info.GetInt32("count"); - list = new List(count); - if (info.GetValue("values", typeof(Tag[])) is Tag[] ary) - AddRange(ary); - } - - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); - info.AddValue("child_type", (byte) ChildType); - info.AddValue("count", list.Count); - info.AddValue("values", list.ToArray()); - } /// public IEnumerator GetEnumerator() => list.GetEnumerator(); @@ -128,24 +106,22 @@ public Tag this[int index] set => list[index] = AssertType(value); } - - - - - - - - - - - - - - - - - - + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) + { + if (named && Name != null) + { + writer.WriteStartArray(Name); + } + else + { + writer.WriteStartArray(); + } + + for (var i = 0; i < Count; i++) + list[i].WriteJson(writer, false); + writer.WriteEndArray(); + } /// public override string ToString() @@ -153,9 +129,7 @@ public override string ToString() var word = Count == 1 ? Strings.WordEntry : Strings.WordEntries; return $"TAG_List({PrettyName}): [{Count} {word}]"; } - - - + /// protected internal override void PrettyPrinted(StringBuilder buffer, int level, string indent) { diff --git a/SharpNBT/Tags/LongArrayTag.cs b/SharpNBT/Tags/LongArrayTag.cs index 609e4da..87171a4 100644 --- a/SharpNBT/Tags/LongArrayTag.cs +++ b/SharpNBT/Tags/LongArrayTag.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.Serialization; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -9,7 +9,7 @@ namespace SharpNBT; /// /// A tag that whose value is a contiguous sequence of 64-bit integers. /// -[PublicAPI][Serializable] +[PublicAPI] public class LongArrayTag : ArrayTag { /// @@ -29,15 +29,6 @@ public LongArrayTag(string? name, long[] values) : base(TagType.LongArray, name, { } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected LongArrayTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - /// /// Initializes a new instance of the with the specified . /// @@ -55,6 +46,23 @@ public LongArrayTag(string? name, IEnumerable values) : base(TagType.LongA public LongArrayTag(string? name, ReadOnlySpan values) : base(TagType.LongArray, name, values.ToArray()) { } + + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) + { + if (named && Name != null) + { + writer.WriteStartArray(Name); + } + else + { + writer.WriteStartArray(); + } + + for (var i = 0; i < Count; i++) + writer.WriteNumberValue(this[i]); + writer.WriteEndArray(); + } /// public override string ToString() diff --git a/SharpNBT/Tags/LongTag.cs b/SharpNBT/Tags/LongTag.cs index c118130..e14e60b 100644 --- a/SharpNBT/Tags/LongTag.cs +++ b/SharpNBT/Tags/LongTag.cs @@ -1,5 +1,5 @@ using System; -using System.Runtime.Serialization; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -7,7 +7,7 @@ namespace SharpNBT; /// /// A tag that contains a single 64-bit integer value. /// -[PublicAPI][Serializable] +[PublicAPI] public class LongTag : NumericTag { /// @@ -37,14 +37,18 @@ public LongTag(string? name, long value) : base(TagType.Long, name, value) public LongTag(string? name, ulong value) : base(TagType.Long, name, unchecked((long) value)) { } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected LongTag(SerializationInfo info, StreamingContext context) : base(info, context) + + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) { + if (named && Name != null) + { + writer.WriteNumber(Name, Value); + } + else + { + writer.WriteNumberValue(Value); + } } /// diff --git a/SharpNBT/Tags/NumericTag.cs b/SharpNBT/Tags/NumericTag.cs index 22d023a..944deef 100644 --- a/SharpNBT/Tags/NumericTag.cs +++ b/SharpNBT/Tags/NumericTag.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Numerics; -using System.Runtime.Serialization; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -10,7 +10,7 @@ namespace SharpNBT; /// Abstract base class for types that contain a single numeric value. /// /// A value type that implements . -[PublicAPI][Serializable] +[PublicAPI] public abstract class NumericTag : Tag, IEquatable>, IComparable>, IComparable where T : unmanaged, INumber { /// @@ -23,21 +23,7 @@ protected NumericTag(TagType type, string? name, T value) : base(type, name) { Value = value; } - - /// - protected NumericTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - var value = info.GetValue("value", typeof(T)); - Value = value is null ? default : (T)value; - } - /// - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); - info.AddValue("value", Value, typeof(T)); - } - /// public bool Equals(NumericTag? other) { diff --git a/SharpNBT/Tags/ShortTag.cs b/SharpNBT/Tags/ShortTag.cs index 7a6a1ae..cb71678 100644 --- a/SharpNBT/Tags/ShortTag.cs +++ b/SharpNBT/Tags/ShortTag.cs @@ -1,5 +1,5 @@ using System; -using System.Runtime.Serialization; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -7,7 +7,7 @@ namespace SharpNBT; /// /// A tag that contains a single 16-bit integer value. /// -[PublicAPI][Serializable] +[PublicAPI] public class ShortTag : NumericTag { /// @@ -43,14 +43,18 @@ public ShortTag(string? name, int value) : base(TagType.Short, name, unchecked(( public ShortTag(string? name, ushort value) : base(TagType.Short, name, unchecked((short) value)) { } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected ShortTag(SerializationInfo info, StreamingContext context) : base(info, context) + + /// + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) { + if (named && Name != null) + { + writer.WriteNumber(Name, Value); + } + else + { + writer.WriteNumberValue(Value); + } } /// diff --git a/SharpNBT/Tags/StringTag.cs b/SharpNBT/Tags/StringTag.cs index 7e5c05c..ed57705 100644 --- a/SharpNBT/Tags/StringTag.cs +++ b/SharpNBT/Tags/StringTag.cs @@ -1,6 +1,5 @@ using System; -using System.Numerics; -using System.Runtime.Serialization; +using System.Text.Json; using JetBrains.Annotations; namespace SharpNBT; @@ -8,7 +7,7 @@ namespace SharpNBT; /// /// A tag the contains a UTF-8 string. /// -[PublicAPI][Serializable] +[PublicAPI] public class StringTag : Tag, IEquatable { /// @@ -25,18 +24,18 @@ public StringTag(string? name, string? value) : base(TagType.String, name) { Value = value ?? string.Empty; } - - /// - protected StringTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - Value = info.GetString("value") ?? string.Empty; - } - + /// - public override void GetObjectData(SerializationInfo info, StreamingContext context) + protected internal override void WriteJson(Utf8JsonWriter writer, bool named = true) { - base.GetObjectData(info, context); - info.AddValue("value", Value); + if (named && Name != null) + { + writer.WriteString(Name, Value); + } + else + { + writer.WriteStringValue(Value); + } } /// diff --git a/SharpNBT/Tags/Tag.cs b/SharpNBT/Tags/Tag.cs index 151069a..4f69f01 100644 --- a/SharpNBT/Tags/Tag.cs +++ b/SharpNBT/Tags/Tag.cs @@ -1,11 +1,10 @@ using System; -using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Json; using System.Text; -using System.Text.RegularExpressions; +using System.Text.Json; +using System.Threading.Tasks; using JetBrains.Annotations; [assembly: CLSCompliant(true)] @@ -16,40 +15,9 @@ namespace SharpNBT; /// /// Abstract base class that all NBT tags inherit from. /// -[PublicAPI][Serializable] -public abstract class Tag : IEquatable, ISerializable, ICloneable +[PublicAPI] +public abstract class Tag : IEquatable, ICloneable { - private static Regex simpleNameMatcher; - - static Tag() - { - simpleNameMatcher = new Regex(@"^[A-Ba-z0-9_-]+$", RegexOptions.Compiled); - } - - - private static IEnumerable GetKnownTypes() - { - return new[] - { - typeof(TagType), - typeof(NumericTag<>), - typeof(ArrayTag<>), - typeof(Tag[]), - typeof(ByteTag), - typeof(ShortTag), - typeof(IntTag), - typeof(LongTag), - typeof(FloatTag), - typeof(DoubleTag), - typeof(StringTag), - typeof(ByteArrayTag), - typeof(IntArrayTag), - typeof(LongArrayTag), - typeof(ListTag), - typeof(CompoundTag) - }; - } - /// /// Text applied in a pretty-print sting when a tag has no defined value. /// @@ -58,17 +26,18 @@ private static IEnumerable GetKnownTypes() /// /// Gets a constant describing the NBT type this object represents. /// - public TagType Type { get; private set; } + public TagType Type { get; } /// /// Gets the parent this object is a child of. /// + [Obsolete("Parent property will be removed in a future version.")] public Tag? Parent { get; internal set; } /// /// Gets the name assigned to this . /// - public string? Name { get; set; } + public string? Name { get; } /// /// Initialized a new instance of the class. @@ -98,77 +67,115 @@ protected internal virtual void PrettyPrinted(StringBuilder buffer, int level, s /// Gets the name of the object as a human-readable quoted string, or a default name to indicate it has no name when applicable. /// protected internal string PrettyName => Name is null ? "None" : $"\"{Name}\""; - + /// - /// Gets a representation of this as a JSON string. + /// Uses the provided to write the NBT tag in JSON format. /// - /// Flag indicating if formatting should be applied to make the string human-readable. - /// When is , indicates the indent characters(s) to use. - /// A JSON string describing this object. - public string ToJsonString(bool pretty = false, string indent = " ") + /// A JSON writer instance. + /// + /// Flag indicating if this object's name should be written as a property name, or when it + /// is a child of , in which case it should be written as a JSON array element. + /// + protected internal abstract void WriteJson(Utf8JsonWriter writer, bool named = true); + + /// + /// Writes the tag to the specified in JSON format. + /// + /// The stream instance to write to. + /// Options that will be passed to the JSON writer. + /// The stream is no opened for writing. + public void WriteJson(Stream stream, JsonWriterOptions? options = null) { - var settings = new DataContractJsonSerializerSettings + using var json = new Utf8JsonWriter(stream, options ?? new JsonWriterOptions()); + + if (string.IsNullOrEmpty(Name)) { - UseSimpleDictionaryFormat = true, - EmitTypeInformation = EmitTypeInformation.Never, - KnownTypes = GetKnownTypes() - }; - var serializer = new DataContractJsonSerializer(typeof(Tag), settings); - using var stream = new MemoryStream(); - if (pretty) + json.WriteStartArray(); + WriteJson(json, false); + json.WriteEndArray(); + } + else + { + json.WriteStartObject(); + WriteJson(json, true); + json.WriteEndObject(); + } + + json.Flush(); + } + + /// + /// Asynchronously writes the tag to the specified in JSON format. + /// + /// The stream instance to write to. + /// Options that will be passed to the JSON writer. + /// The stream is no opened for writing. + public async Task WriteJsonAsync(Stream stream, JsonWriterOptions? options = null) + { + await using var json = new Utf8JsonWriter(stream, options ?? new JsonWriterOptions()); + + if (string.IsNullOrEmpty(Name)) { - using var writer = JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.UTF8, false, true, indent); - serializer.WriteObject(writer, this); - writer.Flush(); + json.WriteStartArray(); + WriteJson(json, false); + json.WriteEndArray(); } else { - serializer.WriteObject(stream, this); + json.WriteStartObject(); + WriteJson(json, true); + json.WriteEndObject(); } + + await json.FlushAsync(); + } + + /// + /// Converts the NBT to an equivalent JSON representation, and returns it as a string. + /// + /// Options that will be passed to the JSON writer. + /// The JSON-encoded string representing describing the tag. + public string ToJson(JsonWriterOptions? options = null) + { + using var stream = new MemoryStream(); + WriteJson(stream, options); stream.Flush(); return Encoding.UTF8.GetString(stream.ToArray()); } + + /// + /// Gets a representation of this as a JSON string. + /// + /// Flag indicating if formatting should be applied to make the string human-readable. + /// Ignored + /// A JSON string describing this object. + [Obsolete("Use WriteJson and ToJson instead.")] + public string ToJsonString(bool pretty = false, string indent = " ") + { + var options = new JsonWriterOptions { Indented = pretty }; + return ToJson(options); + } - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// - /// if the current object is equal to the parameter; otherwise, . - /// + /// public bool Equals(Tag? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return Type == other.Type && Name == other.Name; + return Type == other.Type && string.CompareOrdinal(Name, other.Name) == 0; } - /// Determines whether the specified object is equal to the current object. - /// The object to compare with the current object. - /// - /// if the specified object is equal to the current object; otherwise, . - /// + /// public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((Tag)obj); - } - - /// Serves as the default hash function. - /// A hash code for the current object. - /// - public override int GetHashCode() - { - unchecked - { - // ReSharper disable NonReadonlyMemberInGetHashCode - return ((int)Type * 373) ^ (Name != null ? Name.GetHashCode() : 0); - // ReSharper restore NonReadonlyMemberInGetHashCode - } + return obj.GetType() == GetType() && Equals((Tag)obj); } - /// Creates a new object that is a copy of the current instance. - /// A new object that is a copy of this instance. + /// + public override int GetHashCode() => HashCode.Combine((int)Type, Name); + + /// public object Clone() { // Serialize then deserialize to make a deep-copy @@ -182,28 +189,7 @@ public object Clone() writer.WriteTag(this); stream.Seek(0, SeekOrigin.Begin); - return reader.ReadTag(!(Parent is ListTag)); - } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected Tag(SerializationInfo info, StreamingContext context) - { - Type = (TagType) info.GetByte("type"); - Name = info.GetString("name"); - } - - /// Populates a with the data needed to serialize the target object. - /// The to populate with data. - /// The destination (see ) for this serialization. - /// The caller does not have the required permission. - public virtual void GetObjectData(SerializationInfo info, StreamingContext context) - { - info.AddValue("type", (byte) Type); - info.AddValue("name", Name); + return reader.ReadTag(!string.IsNullOrWhiteSpace(Name)); } /// @@ -238,7 +224,7 @@ protected internal string StringifyName { if (string.IsNullOrEmpty(Name)) return string.Empty; - return simpleNameMatcher.IsMatch(Name) ? Name : $"\"{Name}\""; + return Name.All(c => c.IsValidUnquoted()) ? Name : $"\"{Name}\""; } } } \ No newline at end of file