@@ -146,7 +146,15 @@ pub const IncomingMessage = struct {
146146 return error .SkippedParams ;
147147 }
148148
149- try self .scanUntil ("params" );
149+ self .scanUntil ("params" ) catch | err | {
150+ // handle nullable type
151+ if (@typeInfo (T ) == .Optional ) {
152+ if (err == error .InvalidToken or err == error .EndOfDocument ) {
153+ return null ;
154+ }
155+ }
156+ return err ;
157+ };
150158
151159 // parse "params"
152160 const options = std.json.ParseOptions {
@@ -250,3 +258,34 @@ test "read incoming message with null session id" {
250258 try std .testing .expectEqual (1 , try msg .getId ());
251259 }
252260}
261+
262+ test "message with nullable params" {
263+ const T = struct {
264+ bar : []const u8 ,
265+ };
266+
267+ // nullable type, params is present => value
268+ const not_null =
269+ \\{"id": 1,"method":"foo","params":{"bar":"baz"}}
270+ ;
271+ var msg = IncomingMessage .init (std .testing .allocator , not_null );
272+ defer msg .deinit ();
273+ const input = try Input (? T ).get (std .testing .allocator , & msg );
274+ defer input .deinit ();
275+ try std .testing .expectEqualStrings (input .params .? .bar , "baz" );
276+
277+ // nullable type, params is not present => null
278+ const is_null =
279+ \\{"id": 1,"method":"foo","sessionId":"AAA"}
280+ ;
281+ var msg_null = IncomingMessage .init (std .testing .allocator , is_null );
282+ defer msg_null .deinit ();
283+ const input_null = try Input (? T ).get (std .testing .allocator , & msg_null );
284+ defer input_null .deinit ();
285+ try std .testing .expectEqual (null , input_null .params );
286+ try std .testing .expectEqualStrings ("AAA" , input_null .sessionId .? );
287+
288+ // not nullable type, params is not present => error
289+ const params_or_error = msg_null .getParams (std .testing .allocator , T );
290+ try std .testing .expectError (error .EndOfDocument , params_or_error );
291+ }
0 commit comments