1+ using System . Text . Json ;
2+ using System . Text . Json . Serialization ;
3+ using Microsoft . Extensions . AI ;
4+
5+ namespace Devlooped . Extensions . AI ;
6+
7+ /// <summary>
8+ /// A JSON converter for <see cref="AdditionalPropertiesDictionary"/> that handles serialization and deserialization
9+ /// of additional properties so they are stored and retrieved as primitive types.
10+ /// </summary>
11+ partial class AdditionalPropertiesDictionaryConverter : JsonConverter < AdditionalPropertiesDictionary >
12+ {
13+ public override AdditionalPropertiesDictionary Read ( ref Utf8JsonReader reader , Type typeToConvert , JsonSerializerOptions options )
14+ {
15+ if ( reader . TokenType != JsonTokenType . StartObject )
16+ throw new JsonException ( "Expected start of object." ) ;
17+
18+ var dictionary = new AdditionalPropertiesDictionary ( ) ;
19+
20+ while ( reader . Read ( ) )
21+ {
22+ if ( reader . TokenType == JsonTokenType . EndObject )
23+ return dictionary ;
24+
25+ if ( reader . TokenType != JsonTokenType . PropertyName )
26+ throw new JsonException ( "Expected property name." ) ;
27+
28+ var key = reader . GetString ( ) ! ;
29+ reader . Read ( ) ;
30+
31+ var value = JsonSerializer . Deserialize < object > ( ref reader , options ) ;
32+ if ( value is JsonElement element )
33+ dictionary [ key ] = GetPrimitive ( element ) ;
34+ else
35+ dictionary [ key ] = value ;
36+ }
37+
38+ throw new JsonException ( "Unexpected end of JSON." ) ;
39+ }
40+
41+ public override void Write ( Utf8JsonWriter writer , AdditionalPropertiesDictionary value , JsonSerializerOptions options )
42+ {
43+ writer . WriteStartObject ( ) ;
44+
45+ foreach ( var kvp in value . Where ( x => x . Value is not null ) )
46+ {
47+ writer . WritePropertyName ( kvp . Key ) ;
48+ JsonSerializer . Serialize ( writer , kvp . Value , options ) ;
49+ }
50+
51+ writer . WriteEndObject ( ) ;
52+ }
53+
54+ // Helper to convert JsonElement to closest .NET primitive
55+ static object ? GetPrimitive ( JsonElement element )
56+ {
57+ switch ( element . ValueKind )
58+ {
59+ case JsonValueKind . String : return element . GetString ( ) ;
60+ case JsonValueKind . Number :
61+ if ( element . TryGetInt32 ( out var i ) ) return i ;
62+ if ( element . TryGetInt64 ( out var l ) ) return l ;
63+ if ( element . TryGetDouble ( out var d ) ) return d ;
64+ return element . GetDecimal ( ) ;
65+ case JsonValueKind . True : return true ;
66+ case JsonValueKind . False : return false ;
67+ case JsonValueKind . Null : return null ;
68+ case JsonValueKind . Object : return element ; // You can recurse here if needed
69+ case JsonValueKind . Array : return element ; // Or parse as List<object?>
70+ default : return element ;
71+ }
72+ }
73+ }
0 commit comments