// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; using System.Diagnostics.CodeAnalysis; #if !NET9_0_OR_GREATER using System.Reflection; #endif using System.Text.Json; using System.Text.Json.Serialization; #if !NET9_0_OR_GREATER using ModelContextProtocol; #endif // NOTE: // This is a workaround for lack of System.Text.Json's JsonStringEnumConverter // 9.x support for JsonStringEnumMemberNameAttribute. Once all builds use the System.Text.Json 9.x // version, this whole file can be removed. Note that the type is public so that external source // generators can use it, so removing it is a potential breaking change. namespace ModelContextProtocol { /// /// A JSON converter for enums that allows customizing the serialized string value of enum members /// using the . /// /// The enum type to convert. /// /// This is a temporary workaround for lack of System.Text.Json's JsonStringEnumConverter<T> /// 9.x support for custom enum member naming. It will be replaced by the built-in functionality /// once .NET 9 is fully adopted. /// [EditorBrowsable(EditorBrowsableState.Never)] public sealed class CustomizableJsonStringEnumConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum> : JsonStringEnumConverter where TEnum : struct, Enum { #if !NET9_0_OR_GREATER /// /// Initializes a new instance of the class. /// /// /// The converter automatically detects any enum members decorated with /// and uses those values during serialization and deserialization. /// public CustomizableJsonStringEnumConverter() : base(namingPolicy: ResolveNamingPolicy()) { } private static JsonNamingPolicy? ResolveNamingPolicy() { var map = typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static) .Select(f => (f.Name, AttributeName: f.GetCustomAttribute()?.Name)) .Where(pair => pair.AttributeName != null) .ToDictionary(pair => pair.Name, pair => pair.AttributeName); return map.Count > 0 ? new EnumMemberNamingPolicy(map!) : null; } private sealed class EnumMemberNamingPolicy(Dictionary map) : JsonNamingPolicy { public override string ConvertName(string name) => map.TryGetValue(name, out string? newName) ? newName : name; } #endif } /// /// A JSON converter for enums that allows customizing the serialized string value of enum members /// using the . /// /// /// This is a temporary workaround for lack of System.Text.Json's JsonStringEnumConverter<T> /// 9.x support for custom enum member naming. It will be replaced by the built-in functionality /// once .NET 9 is fully adopted. /// [EditorBrowsable(EditorBrowsableState.Never)] [RequiresUnreferencedCode("Requires unreferenced code to instantiate the generic enum converter.")] [RequiresDynamicCode("Requires dynamic code to instantiate the generic enum converter.")] public sealed class CustomizableJsonStringEnumConverter : JsonConverterFactory { /// public override bool CanConvert(Type typeToConvert) => typeToConvert.IsEnum; /// public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) { Type converterType = typeof(CustomizableJsonStringEnumConverter<>).MakeGenericType(typeToConvert)!; var factory = (JsonConverterFactory)Activator.CreateInstance(converterType)!; return factory.CreateConverter(typeToConvert, options); } } } #if !NET9_0_OR_GREATER namespace System.Text.Json.Serialization { /// /// Determines the custom string value that should be used when serializing an enum member using JSON. /// /// /// This attribute is a temporary workaround for lack of System.Text.Json's support for custom enum member naming /// in versions prior to .NET 9. It works together with /// to provide customized string representations of enum values during JSON serialization and deserialization. /// [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] internal sealed class JsonStringEnumMemberNameAttribute : Attribute { /// /// Creates new attribute instance with a specified enum member name. /// /// The name to apply to the current enum member when serialized to JSON. public JsonStringEnumMemberNameAttribute(string name) { Name = name; } /// /// Gets the custom JSON name of the enum member. /// public string Name { get; } } } #endif