using System.Globalization;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace XFEWorkflow.Abstractions;
public static partial class WorkflowJson
{
// Display/file JSON only. Keep the default serializer for protocols and script injection.
private static readonly Lazy<JsonSerializerOptions> displayCompact = new(() => CreateDisplayOptions(false));
private static readonly Lazy<JsonSerializerOptions> displayIndented = new(() => CreateDisplayOptions(true));
private static JsonSerializerOptions CreateDisplayOptions(bool indented) => new(Options)
{ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, WriteIndented = indented };
public static string Display(this JsonNode? value, bool indented = false) =>
ReadableUnicode(value?.ToJsonString(indented ? displayIndented.Value : displayCompact.Value) ?? "null");
public static string SerializeDisplay<T>(T value, bool indented = true) =>
ReadableUnicode(JsonSerializer.Serialize(value, indented ? displayIndented.Value : displayCompact.Value));
private static string ReadableUnicode(string json)
{
// System.Text.Json still escapes supplementary characters (including emoji).
// Scan JSON escapes once: never decode a user-authored literal \\uXXXX or
// turn escaped quotes, backslashes or controls into invalid JSON.
var result = new StringBuilder(json.Length);
for (var i = 0; i < json.Length;)
{
if (json[i] != '\\' || i + 1 >= json.Length) { result.Append(json[i++]); continue; }
if (TryUnicode(json, i, out var character))
{
if (char.IsHighSurrogate(character) && TryUnicode(json, i + 6, out var low) && char.IsLowSurrogate(low))
{ result.Append(character).Append(low); i += 12; continue; }
if (!char.IsSurrogate(character) && !char.IsControl(character) && character is not '"' and not '\\')
{ result.Append(character); i += 6; continue; }
result.Append(json, i, 6); i += 6; continue;
}
result.Append(json, i, 2); i += 2;
}
return result.ToString();
}
private static bool TryUnicode(string json, int index, out char character)
{
character = default;
if (index + 6 > json.Length || json[index] != '\\' || json[index + 1] != 'u' ||
!ushort.TryParse(json.AsSpan(index + 2, 4), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out var code)) return false;
character = (char)code;
return true;
}
}
using System.Globalization;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace XFEWorkflow.Abstractions;
public static partial class WorkflowJson
{
// Display/file JSON only. Keep the default serializer for protocols and script injection.
private static readonly Lazy<JsonSerializerOptions> displayCompact = new(() => CreateDisplayOptions(false));
private static readonly Lazy<JsonSerializerOptions> displayIndented = new(() => CreateDisplayOptions(true));
private static JsonSerializerOptions CreateDisplayOptions(bool indented) => new(Options)
{ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, WriteIndented = indented };
public static string Display(this JsonNode? value, bool indented = false) =>
ReadableUnicode(value?.ToJsonString(indented ? displayIndented.Value : displayCompact.Value) ?? "null");
public static string SerializeDisplay<T>(T value, bool indented = true) =>
ReadableUnicode(JsonSerializer.Serialize(value, indented ? displayIndented.Value : displayCompact.Value));
private static string ReadableUnicode(string json)
{
// System.Text.Json still escapes supplementary characters (including emoji).
// Scan JSON escapes once: never decode a user-authored literal \\uXXXX or
// turn escaped quotes, backslashes or controls into invalid JSON.
var result = new StringBuilder(json.Length);
for (var i = 0; i < json.Length;)
{
if (json[i] != '\\' || i + 1 >= json.Length) { result.Append(json[i++]); continue; }
if (TryUnicode(json, i, out var character))
{
if (char.IsHighSurrogate(character) && TryUnicode(json, i + 6, out var low) && char.IsLowSurrogate(low))
{ result.Append(character).Append(low); i += 12; continue; }
if (!char.IsSurrogate(character) && !char.IsControl(character) && character is not '"' and not '\\')
{ result.Append(character); i += 6; continue; }
result.Append(json, i, 6); i += 6; continue;
}
result.Append(json, i, 2); i += 2;
}
return result.ToString();
}
private static bool TryUnicode(string json, int index, out char character)
{
character = default;
if (index + 6 > json.Length || json[index] != '\\' || json[index + 1] != 'u' ||
!ushort.TryParse(json.AsSpan(index + 2, 4), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out var code)) return false;
character = (char)code;
return true;
}
}