using System; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Nodes; namespace MinecraftClient; /// /// JSON utilities backed by System.Text.Json. /// public static class Json { private static readonly JsonSerializerOptions s_escapeOptions = new() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; /// /// Parse a JSON string into a mutable DOM. /// Returns null for null, empty, or whitespace-only input. /// Returns a wrapping the raw string when the input /// is not valid JSON (e.g. a plain-text Minecraft MOTD or chat message). /// public static JsonNode? ParseJson(string? json) { if (string.IsNullOrWhiteSpace(json)) return null; ReadOnlySpan text = json.AsSpan().TrimStart(); if (!LooksLikeJson(text)) return JsonValue.Create(json); try { return JsonNode.Parse(json); } catch (JsonException) { return JsonValue.Create(json); } } private static bool LooksLikeJson(ReadOnlySpan text) { if (text.IsEmpty) return false; return text[0] switch { '{' or '"' => true, '[' => LooksLikeJsonArray(text[1..]), '-' => text.Length > 1 && char.IsAsciiDigit(text[1]), >= '0' and <= '9' => true, 't' or 'f' or 'n' => true, _ => false }; } private static bool LooksLikeJsonArray(ReadOnlySpan text) { text = text.TrimStart(); if (text.IsEmpty) return false; return text[0] switch { ']' or '{' or '[' or '"' => true, '-' => text.Length > 1 && char.IsAsciiDigit(text[1]), >= '0' and <= '9' => true, 't' or 'f' or 'n' => true, _ => false }; } /// /// Escape a string for embedding inside a JSON string literal. /// Uses System.Text.Json serialization and strips the surrounding quotes. /// public static string EscapeString(string src) => JsonSerializer.Serialize(src, s_escapeOptions)[1..^1]; } /// /// Extension helpers for that replicate the access patterns /// of the former JSONData.StringValue property. /// public static class JsonNodeExtensions { /// /// Return the string representation of any JSON value. /// Strings are returned without quotes; numbers, booleans, and null /// are returned as their text representation. /// public static string GetStringValue(this JsonNode? node) => node switch { null => "null", JsonValue val when val.TryGetValue(out var s) => s, _ => node.ToJsonString() }; }