4

The signature of the method JsonConvert.DeserializeObject() method in Newtonsoft.Json (Json.NET) is:

public static object? DeserializeObject(string value)

Source code here.

The method (and all its overloads) return a nullable object. I want to know under what circumstances will it return null? I was always under the impression that this method either throws Newtonsoft.Json.JsonException in case of unsuccessful deserialization or a properly constructed object in case of successful deserialization. The official documentation doesn't help in explaining the nullability either.

One possible circumstance might be that the exception was handled by a custom handler. Is there any other case that the method can return null?

var obj = JsonConvert.DeserializeObject<MyObject>("invalid json", new JsonSerializerSettings
{
    Error = (sender, args) => args.ErrorContext.Handled = true
});
// obj is null here?
scharnyw
  • 2,347
  • 2
  • 18
  • 32
  • `JsonConvert.DeserializeObject("")` for example. – Guru Stron Jun 11 '21 at 09:36
  • @GuruStron Thanks, I didn't know that. Seems that deserializing whitespace to any reference type will cause it to return null. It would be nice if these things can be more clearly documented though. – scharnyw Jun 11 '21 at 09:43
  • Can you not just read the code of the library? It's open source – Caius Jard Jun 11 '21 at 09:46
  • I skipped through some source code, looks like the abstract `JsonConverter` class defines the method `ReadJson` to have a return type op `object?` and it basically boils up from there. Not really a circumstance but was pretty interesting to look at. – Jochem Van Hespen Jun 11 '21 at 09:47
  • 2
    @CaiusJard Of course I can. But this is an API contract issue. What's the point of nullability annotations if everyone is told to "just read the code of the library"? And by reading the code, I'm in constant worry that I'm depending on an implementation detail that may change in a future release. – scharnyw Jun 11 '21 at 09:51
  • Turns out that if you pass a `JsonConverter` to `DeserializeObject` with `TokenType` of `JsonToken.None` and if the `JsonReader` `ReadForType` method returns false, `null` will be returned as well. Edit: this will only be the case if the JsonContract for a given type is null of it's `IsNullable` property is `true` – Jochem Van Hespen Jun 11 '21 at 09:53
  • _"What's the point of nullability annotations if everyone is told to "just read the code of the library""_ Point is that you know that null can be returned without reading the source code. – Guru Stron Jun 11 '21 at 10:00
  • 1
    @JochemVanHespen Seems complicated enough that I probably should always check for null when using this API… – scharnyw Jun 11 '21 at 10:08
  • @scharnyw what's the source of your json? – Jochem Van Hespen Jun 11 '21 at 10:18
  • So is your question actually "what was JNK thinking when he declared DeserObject may return null? Under what overarching circumstances might a null response be expected?" - because if so, it's probably not a good fit for SO (it already feels like a bit of list question without the added mind-reading part); it can only reasonably be reliably answered by a limited number of individuals who know the reasoning behind the present and the overall goals that will be kept to for the future. Sure, the impl may change; consequently any answer may be invalidated easily. It may be better to ask on github? – Caius Jard Jun 11 '21 at 10:22
  • 3
    `.DeserializeObject<..>("")` or `.DeserializeObject<..>("null")`. – Lasse V. Karlsen Jun 11 '21 at 12:22
  • @JochemVanHespen It indirectly comes from user input. But even in cases where the JSON is not user provided, it might be better to just do a null check instead of risking a null reference exception, since the contract doesn't seem so straightforward. – scharnyw Jun 11 '21 at 12:45
  • @CaiusJard I do not intend to ask about the intention of the design, but about the behavior of a commonly-used API which I think is a valid question for SO. Btw, I did consider asking on GitHub, but Newtonsoft repository specifically says "_GitHub issues are only for reporting bugs, not questions or help_" and "_If you have questions about how to use Json.NET, please read the Json.NET documentation or ask on Stack Overflow_"... – scharnyw Jun 11 '21 at 12:52

1 Answers1

4

As mentioned by @Lasse V. Karlsen in the comments, the following code will return null

JsonConvert.DeserializeObject<SomeClass>("")
// or
JsonConvert.DeserializeObject<SomeClass>("null")

The same applies to the value of any property

JsonConvert.DeserializeObject<SomeClass>("{someProp : null}")

Note that an actual null string will throw an ArgumentNullException

JsonConvert.DeserializeObject<SomeClass>(null)
Charlieface
  • 52,284
  • 6
  • 19
  • 43
  • `JsonConvert.DeserializeObject("{someProp : \"\"}")` will **not** set a `null` value for `someProp`, it will set an empty string. That's because the c# string literal `""` represents a JSON string with no content at all, but `"\"\""` represents a JSON string containing single JSON value, namely an empty string. (In fact it's arguable whether an empty string (rather than a string containing an empty JSON string) is even well-formed JSON, see [Why does JSON.parse fail with the empty string?](https://stackoverflow.com/q/30621802/3744182) for a discussion.) – dbc Jun 12 '21 at 15:45
  • RIghto, fixed that. For the root object, I think Newtonsoft is somewhat lenient there for empty strings, but `"null"` is definitely valid JSON representation for a `null` object – Charlieface Jun 12 '21 at 23:01