Forge
csharp8f5a77c3
1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3
4using System.ComponentModel;
5using System.Diagnostics.CodeAnalysis;
6#if !NET9_0_OR_GREATER
7using System.Reflection;
8#endif
9using System.Text.Json;
10using System.Text.Json.Serialization;
11#if !NET9_0_OR_GREATER
12using AgentClientProtocol;
13#endif
14
15// NOTE:
16// This is a workaround for lack of System.Text.Json's JsonStringEnumConverter<T>
17// 9.x support for JsonStringEnumMemberNameAttribute. Once all builds use the System.Text.Json 9.x
18// version, this whole file can be removed. Note that the type is public so that external source
19// generators can use it, so removing it is a potential breaking change.
20
21namespace AgentClientProtocol
22{
23 /// <summary>
24 /// A JSON converter for enums that allows customizing the serialized string value of enum members
25 /// using the <see cref="JsonStringEnumMemberNameAttribute"/>.
26 /// </summary>
27 /// <typeparam name="TEnum">The enum type to convert.</typeparam>
28 /// <remarks>
29 /// This is a temporary workaround for lack of System.Text.Json's JsonStringEnumConverter&lt;T&gt;
30 /// 9.x support for custom enum member naming. It will be replaced by the built-in functionality
31 /// once .NET 9 is fully adopted.
32 /// </remarks>
33 [EditorBrowsable(EditorBrowsableState.Never)]
34 public sealed class CustomizableJsonStringEnumConverter<
35#if NET8_0_OR_GREATER
36 [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)]
37#endif
38 TEnum> :
39 JsonStringEnumConverter<TEnum> where TEnum : struct, Enum
40 {
41#if !NET9_0_OR_GREATER
42 /// <summary>
43 /// Initializes a new instance of the <see cref="CustomizableJsonStringEnumConverter{TEnum}"/> class.
44 /// </summary>
45 /// <remarks>
46 /// The converter automatically detects any enum members decorated with <see cref="JsonStringEnumMemberNameAttribute"/>
47 /// and uses those values during serialization and deserialization.
48 /// </remarks>
49 public CustomizableJsonStringEnumConverter() :
50 base(namingPolicy: ResolveNamingPolicy())
51 {
52 }
53
54 private static JsonNamingPolicy? ResolveNamingPolicy()
55 {
56 var map = typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static)
57 .Select(f => (f.Name, AttributeName: f.GetCustomAttribute<JsonStringEnumMemberNameAttribute>()?.Name))
58 .Where(pair => pair.AttributeName != null)
59 .ToDictionary(pair => pair.Name, pair => pair.AttributeName);
60
61 return map.Count > 0 ? new EnumMemberNamingPolicy(map!) : null;
62 }
63
64 private sealed class EnumMemberNamingPolicy(Dictionary<string, string> map) : JsonNamingPolicy
65 {
66 public override string ConvertName(string name) =>
67 map.TryGetValue(name, out string? newName) ?
68 newName :
69 name;
70 }
71#endif
72 }
73
74 /// <summary>
75 /// A JSON converter for enums that allows customizing the serialized string value of enum members
76 /// using the <see cref="JsonStringEnumMemberNameAttribute"/>.
77 /// </summary>
78 /// <remarks>
79 /// This is a temporary workaround for lack of System.Text.Json's JsonStringEnumConverter&lt;T&gt;
80 /// 9.x support for custom enum member naming. It will be replaced by the built-in functionality
81 /// once .NET 9 is fully adopted.
82 /// </remarks>
83 [EditorBrowsable(EditorBrowsableState.Never)]
84#if NET8_0_OR_GREATER
85 [RequiresUnreferencedCode("Requires unreferenced code to instantiate the generic enum converter.")]
86 [RequiresDynamicCode("Requires dynamic code to instantiate the generic enum converter.")]
87#endif
88 public sealed class CustomizableJsonStringEnumConverter : JsonConverterFactory
89 {
90 /// <inheritdoc/>
91 public override bool CanConvert(Type typeToConvert) => typeToConvert.IsEnum;
92 /// <inheritdoc/>
93 public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
94 {
95 Type converterType = typeof(CustomizableJsonStringEnumConverter<>).MakeGenericType(typeToConvert)!;
96 var factory = (JsonConverterFactory)Activator.CreateInstance(converterType)!;
97 return factory.CreateConverter(typeToConvert, options);
98 }
99 }
100}
101
102#if !NET9_0_OR_GREATER
103namespace System.Text.Json.Serialization
104{
105 /// <summary>
106 /// Determines the custom string value that should be used when serializing an enum member using JSON.
107 /// </summary>
108 /// <remarks>
109 /// This attribute is a temporary workaround for lack of System.Text.Json's support for custom enum member naming
110 /// in versions prior to .NET 9. It works together with <see cref="CustomizableJsonStringEnumConverter{TEnum}"/>
111 /// to provide customized string representations of enum values during JSON serialization and deserialization.
112 /// </remarks>
113 [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
114 internal sealed class JsonStringEnumMemberNameAttribute : Attribute
115 {
116 /// <summary>
117 /// Creates new attribute instance with a specified enum member name.
118 /// </summary>
119 /// <param name="name">The name to apply to the current enum member when serialized to JSON.</param>
120 public JsonStringEnumMemberNameAttribute(string name)
121 {
122 Name = name;
123 }
124
125 /// <summary>
126 /// Gets the custom JSON name of the enum member.
127 /// </summary>
128 public string Name { get; }
129 }
130}
131#endif
View only · write via MCP/CIDE