77
88namespace OneBitSoftware . Utilities
99{
10+ // TODO: comments: used to read an OperationResult, because we don't know what operation errors exist in the JSON
1011 public class OperationResultJsonConverter : JsonConverter < OperationResult >
1112 {
13+ private readonly Dictionary < string , Type > _valueMappings = new Dictionary < string , Type > ( ) ;
14+ private readonly Dictionary < Type , string > _typeMappings = new Dictionary < Type , string > ( ) ;
15+
1216 public OperationResultJsonConverter ( )
1317 {
18+ this . AddMapping ( "operation_error" , typeof ( OperationError ) ) ;
1419 }
1520
1621 public override bool CanConvert ( Type typeToConvert )
@@ -24,6 +29,7 @@ public override bool CanConvert(Type typeToConvert)
2429 try
2530 {
2631 var serializationOptions = this . ConstructSafeFallbackOptions ( options ) ;
32+ serializationOptions . Converters . Add ( new ReadOnlyPartialConverter ( this ) ) ;
2733 return JsonSerializer . Deserialize ( ref reader , typeToConvert , serializationOptions ) as OperationResult ;
2834 }
2935 catch ( Exception ex )
@@ -32,6 +38,27 @@ public override bool CanConvert(Type typeToConvert)
3238 }
3339 }
3440
41+ protected bool AddMapping ( string typeValue , Type type )
42+ {
43+ if ( string . IsNullOrWhiteSpace ( typeValue ) || type is null ) return false ;
44+ if ( this . _valueMappings . ContainsKey ( typeValue ) || this . _typeMappings . ContainsKey ( type ) ) return false ;
45+
46+ this . _valueMappings [ typeValue ] = type ;
47+ this . _typeMappings [ type ] = typeValue ;
48+ return true ;
49+ }
50+
51+ private Type GetType ( JsonElement typeElement )
52+ {
53+ if ( typeElement . ValueKind != JsonValueKind . String ) return null ;
54+
55+ var stringValue = typeElement . GetString ( ) ;
56+ if ( string . IsNullOrWhiteSpace ( stringValue ) ) return null ;
57+
58+ this . _valueMappings . TryGetValue ( stringValue , out var type ) ;
59+ return type ;
60+ }
61+
3562 public override void Write ( Utf8JsonWriter writer , OperationResult value , JsonSerializerOptions options )
3663 {
3764 if ( value is null )
@@ -50,41 +77,89 @@ private JsonSerializerOptions ConstructSafeFallbackOptions(JsonSerializerOptions
5077 return fallbackSerializationOptions ;
5178 }
5279
53- //private class ReadOnlyPartialConverter : JsonConverter<OperationResult>
54- //{
55- // private readonly OperationResultJsonConverter _operationResultConverter;
56-
57- // internal ReadOnlyPartialConverter(OperationResultJsonConverter operationResultConverter)
58- // {
59- // this._operationResultConverter = operationResultConverter ?? throw new ArgumentNullException(nameof(operationResultConverter));
60- // }
61-
62- // public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(OperationResult);
63-
64- // public override OperationResult Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
65- // {
66- // // We copy the value here so we can easily reuse the reader for the subsequent deserialization.
67- // var readerRestore = reader;
68-
69- // // Get the `type` value by parsing the JSON string into a JsonDocument.
70- // var jsonDocument = JsonDocument.ParseValue(ref reader);
71- // jsonDocument.RootElement.TryGetProperty(this._operationResultConverter.TypePropertyName, out var typeElement);
72-
73- // var returnType = this._operationResultConverter.GetType(typeElement);
74- // if (returnType is null) throw new InvalidOperationException("The received JSON cannot be deserialized to any known type.");
75-
76- // try
77- // {
78- // // Deserialize the JSON to the specified type.
79- // return (OperationResult)JsonSerializer.Deserialize(ref readerRestore, returnType, options);
80- // }
81- // catch (Exception ex)
82- // {
83- // throw new InvalidOperationException("Invalid JSON in request.", ex);
84- // }
85- // }
86-
87- // public override void Write(Utf8JsonWriter writer, OperationResult value, JsonSerializerOptions options) => throw new InvalidOperationException("The `Read only partial converter` cannot be used for serialization.");
88- //}
80+ private class ReadOnlyPartialConverter : JsonConverter < OperationResult >
81+ {
82+ private readonly OperationResultJsonConverter _operationResultConverter ;
83+
84+ internal ReadOnlyPartialConverter ( OperationResultJsonConverter operationResultConverter )
85+ {
86+ this . _operationResultConverter = operationResultConverter ?? throw new ArgumentNullException ( nameof ( operationResultConverter ) ) ;
87+ }
88+
89+ public override bool CanConvert ( Type typeToConvert ) => typeToConvert == typeof ( OperationResult ) ;
90+
91+ public override OperationResult ? Read ( ref Utf8JsonReader reader , Type typeToConvert , JsonSerializerOptions options )
92+ {
93+ // We copy the value here so we can easily reuse the reader for the subsequent deserialization.
94+ var readerRestore = reader ;
95+
96+ // Get the `type` value by parsing the JSON string into a JsonDocument.
97+ var jsonDocument = JsonDocument . ParseValue ( ref reader ) ;
98+
99+ try
100+ {
101+ var fallbackDeserializationOptions = this . ConstructSafeFallbackOptions ( options ) ;
102+
103+ // Deserialize the JSON to the specified type.
104+ var deserializeResult = JsonSerializer . Deserialize ( ref readerRestore , typeToConvert , fallbackDeserializationOptions ) ;
105+ var operationResult = deserializeResult as OperationResult ;
106+
107+ if ( jsonDocument . RootElement . TryGetProperty ( "Errors" , out var errors ) )
108+ {
109+ foreach ( var item in errors . EnumerateArray ( ) )
110+ {
111+ if ( item . TryGetProperty ( "type" , out var typeProperty ) )
112+ {
113+ var returnType = this . _operationResultConverter . GetType ( typeProperty ) ;
114+
115+ AppendErrorType ( item , returnType , operationResult , options ) ;
116+ }
117+ else
118+ {
119+ operationResult . AppendError ( this . ToObject < OperationError > ( item ) ) ;
120+ }
121+ }
122+ }
123+
124+
125+ return operationResult ;
126+ }
127+ catch ( Exception ex )
128+ {
129+ throw new InvalidOperationException ( "Invalid JSON in request." , ex ) ;
130+ }
131+
132+ }
133+
134+ public T ToObject < T > ( JsonElement element )
135+ {
136+ var json = element . GetRawText ( ) ;
137+ return JsonSerializer . Deserialize < T > ( json ) ;
138+ }
139+
140+ public virtual void AppendErrorType ( JsonElement element , Type mappedType , OperationResult operationResult , JsonSerializerOptions options )
141+ {
142+ if ( mappedType . Equals ( "operation_error" ) )
143+ {
144+ operationResult . AppendError ( ToObject < OperationError > ( element ) ) ;
145+ }
146+ else
147+ {
148+ var json = element . GetRawText ( ) ;
149+ var deserializeResult = JsonSerializer . Deserialize ( json , mappedType , options ) ;
150+ operationResult . AppendError ( error : deserializeResult as IOperationError ) ;
151+ }
152+ }
153+
154+ // TODO
155+ private JsonSerializerOptions ConstructSafeFallbackOptions ( JsonSerializerOptions options )
156+ {
157+ var fallbackSerializationOptions = new JsonSerializerOptions ( options ) ;
158+ fallbackSerializationOptions . Converters . Remove ( this ) ;
159+ return fallbackSerializationOptions ;
160+ }
161+
162+ public override void Write ( Utf8JsonWriter writer , OperationResult value , JsonSerializerOptions options ) => throw new InvalidOperationException ( "The `Read only partial converter` cannot be used for serialization." ) ;
163+ }
89164 }
90165}
0 commit comments