// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
namespace Microsoft.Agents.AI.Workflows.Checkpointing;
///
/// Provides support for using values as dictionary keys when serializing and deserializing JSON.
/// It chains to the provided for serialization and deserialization when not used as a property
/// name.
///
///
internal abstract class JsonConverterDictionarySupportBase : JsonConverterBase
{
protected abstract string Stringify([DisallowNull] T value);
protected abstract T Parse(string propertyName);
[return: NotNull]
protected static string Escape(string? value, char escapeChar = '|', bool allowNullAndPad = false, [CallerArgumentExpression(nameof(value))] string? componentName = null)
{
if (!allowNullAndPad && value is null)
{
throw new JsonException($"Invalid {componentName} '{value}'. Expecting non-null string.");
}
if (value is null)
{
return string.Empty;
}
string unescaped = escapeChar.ToString();
string escaped = new(escapeChar, 2);
if (allowNullAndPad)
{
return $"@{value.Replace(unescaped, escaped)}";
}
return $"{value.Replace(unescaped, escaped)}";
}
protected static string? Unescape([DisallowNull] string value, char escapeChar = '|', bool allowNullAndPad = false, [CallerArgumentExpression(nameof(value))] string? componentName = null)
{
if (value.Length == 0)
{
if (!allowNullAndPad)
{
throw new JsonException($"Invalid {componentName} '{value}'. Expecting empty string or a value that is prefixed with '@'.");
}
return null;
}
if (allowNullAndPad && value[0] != '@')
{
throw new JsonException($"Invalid {componentName} component '{value}'. Expecting empty string or a value that is prefixed with '@'.");
}
if (allowNullAndPad)
{
value = value.Substring(1);
}
string unescaped = escapeChar.ToString();
string escaped = new(escapeChar, 2);
return value.Replace(escaped, unescaped);
}
public override T ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
SequencePosition position = reader.Position;
string? propertyName = reader.GetString() ??
throw new JsonException($"Got null trying to read property name at position {position}");
return this.Parse(propertyName);
}
public override void WriteAsPropertyName(Utf8JsonWriter writer, [DisallowNull] T value, JsonSerializerOptions options)
{
string propertyName = this.Stringify(value);
writer.WritePropertyName(propertyName);
}
}