@@ -19,6 +19,28 @@ internal class MagicContractResolver<T> : JsonConverter<T>
1919 if ( reader . TokenType == JsonTokenType . Null )
2020 return default ; // ✅ Return default(T) if null is encountered
2121
22+ // ✅ Handle primitive types before assuming it's complex
23+ if ( PropertyMappingCache . IsSimpleType ( typeToConvert ) )
24+ {
25+ return ( T ? ) ReadSimpleType ( ref reader , typeToConvert ) ;
26+ }
27+
28+ // ✅ Explicitly check if the type is `JsonElement`
29+ if ( typeToConvert == typeof ( JsonElement ) )
30+ {
31+ JsonElement element = JsonSerializer . Deserialize < JsonElement > ( ref reader ) ; // Extract JsonElement
32+
33+ // ✅ Re-run null check for JsonElement
34+ if ( IsSimpleJsonNull ( element ) )
35+ return default ;
36+
37+ // ✅ Re-run primitive check for JsonElement
38+ if ( IsSimpleJsonElement ( element ) )
39+ return ( T ? ) ( object ) element ; // 🚀 Directly cast JsonElement to T
40+
41+ return ( T ? ) ( object ) JsonSerializer . Deserialize < JsonElement > ( ref reader ) ;
42+ }
43+
2244 // ✅ Handle root-level arrays correctly
2345 if ( reader . TokenType == JsonTokenType . StartArray )
2446 {
@@ -28,7 +50,66 @@ internal class MagicContractResolver<T> : JsonConverter<T>
2850 return ( T ? ) ReadIEnumerable ( ref reader , typeToConvert , options ) ;
2951 }
3052
31- return ( T ? ) ReadComplexObject ( ref reader , typeToConvert , options ) ;
53+ // ✅ If it's neither a primitive nor an array, assume it's a complex object
54+ if ( reader . TokenType == JsonTokenType . StartObject )
55+ {
56+ return ( T ? ) ReadComplexObject ( ref reader , typeToConvert , options ) ;
57+ }
58+
59+ throw new JsonException ( $ "Unexpected JSON token: { reader . TokenType } when deserializing { typeToConvert . Name } .") ;
60+ }
61+
62+ private object CreateObjectFromDictionary ( Type type , Dictionary < string , object ? > propertyValues )
63+ {
64+ var constructor = type . GetConstructors ( ) . FirstOrDefault ( ) ;
65+
66+ // 🔥 If there's a constructor AND it has parameters, we use it (for records like QuotaUsage)
67+ if ( constructor != null && constructor . GetParameters ( ) . Length > 0 )
68+ {
69+ var parameters = constructor . GetParameters ( )
70+ . Select ( p =>
71+ {
72+ if ( propertyValues . TryGetValue ( p . Name ! , out var value ) )
73+ return value ;
74+
75+ var matchedKey = propertyValues . Keys
76+ . FirstOrDefault ( k => string . Equals ( k , p . Name , StringComparison . OrdinalIgnoreCase ) ) ;
77+
78+ return matchedKey != null ? propertyValues [ matchedKey ] : GetDefaultValue ( p . ParameterType ) ;
79+ } )
80+ . ToArray ( ) ;
81+
82+ return constructor . Invoke ( parameters ) ;
83+ }
84+
85+ // 🔥 Handle non-constructor classes (e.g., Person class)
86+ var instance = Activator . CreateInstance ( type ) ;
87+ if ( instance == null )
88+ throw new InvalidOperationException ( $ "Failed to create instance of type { type . Name } .") ;
89+
90+ foreach ( var ( propName , value ) in propertyValues )
91+ {
92+ var property = type . GetProperty ( propName , BindingFlags . Public | BindingFlags . Instance ) ;
93+ if ( property != null && property . CanWrite )
94+ {
95+ property . SetValue ( instance , value ) ;
96+ }
97+ }
98+
99+ return instance ;
100+ }
101+
102+
103+ private bool IsSimpleJsonElement ( JsonElement element )
104+ {
105+ return element . ValueKind == JsonValueKind . String ||
106+ element . ValueKind == JsonValueKind . Number ||
107+ element . ValueKind == JsonValueKind . True ||
108+ element . ValueKind == JsonValueKind . False ;
109+ }
110+ private bool IsSimpleJsonNull ( JsonElement element )
111+ {
112+ return element . ValueKind == JsonValueKind . Null ;
32113 }
33114
34115 /// <summary>
@@ -42,13 +123,18 @@ internal class MagicContractResolver<T> : JsonConverter<T>
42123 if ( reader . TokenType != JsonTokenType . StartObject )
43124 throw new JsonException ( $ "Expected StartObject token for type { type . Name } .") ;
44125
45- var instance = Activator . CreateInstance ( type ) ;
46- var properties = PropertyMappingCache . GetTypeOfTProperties ( type ) ;
126+ // 🔥 Step 1: Create a dictionary to store extracted values
127+ var propertyValues = new Dictionary < string , object ? > ( ) ;
47128
129+ var properties = PropertyMappingCache . GetTypeOfTProperties ( type ) ;
48130 while ( reader . Read ( ) )
49131 {
50132 if ( reader . TokenType == JsonTokenType . EndObject )
51- return instance ;
133+ {
134+ // 🔥 Step 3: Convert the dictionary into the final object
135+ var result = CreateObjectFromDictionary ( type , propertyValues ) ;
136+ return result ;
137+ }
52138
53139 if ( reader . TokenType != JsonTokenType . PropertyName )
54140 throw new JsonException ( "Expected PropertyName token." ) ;
@@ -57,39 +143,40 @@ internal class MagicContractResolver<T> : JsonConverter<T>
57143 if ( ! reader . Read ( ) )
58144 throw new JsonException ( "Unexpected end of JSON." ) ;
59145
60- // 🔥 Resolve correct C# property name dynamically
61- string csharpPropertyName = PropertyMappingCache . GetCsharpPropertyName ( jsonPropertyName , type ) ;
62146
63- if ( properties . TryGetValue ( csharpPropertyName , out var mpe ) )
147+ string csharpPropertyName = properties . GetCsharpPropertyName ( jsonPropertyName ) ;
148+
149+ //string csharpPropertyName = PropertyMappingCache.GetCsharpPropertyName(jsonPropertyName, type);
150+
151+ if ( properties . propertyEntries . TryGetValue ( csharpPropertyName , out var mpe ) )
64152 {
65153 if ( mpe . NotMapped )
66154 {
67155 reader . Skip ( ) ;
68156 continue ;
69157 }
70158
159+ // 🔥 Step 2: Extract values and store them in the dictionary
71160 object ? value = ReadPropertyValue ( ref reader , mpe , options ) ;
72- if ( value != null )
73- {
74- try
75- {
76- mpe . Setter ( instance , value ) ;
77- }
78- catch
79- {
80- // Prevent hard crash but log if needed
81- }
82- }
161+ propertyValues [ csharpPropertyName ] = value ;
83162 }
84163 else
85164 {
86165 reader . Skip ( ) ;
87166 }
88167 }
89168
90- return instance ;
169+ throw new JsonException ( "Unexpected end of JSON while reading an object." ) ;
170+ }
171+
172+ private object ? GetDefaultValue ( Type type )
173+ {
174+ return type . IsValueType ? Activator . CreateInstance ( type ) : null ;
91175 }
92176
177+
178+
179+
93180 /// <summary>
94181 /// Reads and assigns a property value, detecting collections, simple types, and complex objects.
95182 /// </summary>
@@ -174,8 +261,6 @@ internal class MagicContractResolver<T> : JsonConverter<T>
174261 return list ;
175262 }
176263
177-
178-
179264 public override void Write ( Utf8JsonWriter writer , T value , JsonSerializerOptions options )
180265 {
181266 if ( value == null )
@@ -202,7 +287,7 @@ public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions
202287
203288 // 🔥 Handle complex objects recursively
204289 writer . WriteStartObject ( ) ;
205- SerializeComplexProperties ( writer , value , properties , options ) ;
290+ SerializeComplexProperties ( writer , value , properties . propertyEntries , options ) ;
206291 writer . WriteEndObject ( ) ;
207292 }
208293
@@ -260,7 +345,7 @@ private bool SerializeIEnumerable(Utf8JsonWriter writer, object value, JsonSeria
260345 {
261346 var nestedProperties = PropertyMappingCache . GetTypeOfTProperties ( itemType ) ;
262347 writer . WriteStartObject ( ) ;
263- SerializeComplexProperties ( writer , item , nestedProperties , options ) ;
348+ SerializeComplexProperties ( writer , item , nestedProperties . propertyEntries , options ) ;
264349 writer . WriteEndObject ( ) ;
265350 }
266351 else
@@ -356,7 +441,7 @@ private void SerializeComplexProperties(Utf8JsonWriter writer, object value, Dic
356441 {
357442 var nestedProps = PropertyMappingCache . GetTypeOfTProperties ( propValue . GetType ( ) ) ;
358443 writer . WriteStartObject ( ) ;
359- SerializeComplexProperties ( writer , propValue , nestedProps , options ) ;
444+ SerializeComplexProperties ( writer , propValue , nestedProps . propertyEntries , options ) ;
360445 writer . WriteEndObject ( ) ;
361446 }
362447 }
0 commit comments