Newtonsoft.Json icon indicating copy to clipboard operation
Newtonsoft.Json copied to clipboard

[Question] In which cases JsonConvert.DeserializeObject<T>(string, JsonSerializerSettings) actually returns null?

Open EnricoMassone opened this issue 3 years ago • 8 comments

I'm working with .NET core 3.1, C# 8 and nullable reference types enabled.

From the class library I'm writing, I'm referencing the version 12.0.3 of Newtonsoft JSON.

I noticed that, by calling JsonConvert.DeserializeObject<T>(string, JsonSerializerSettings?), I can get a null reference (Visual Studio analyzers detect a possible dereferencing of a null reference).

Notice that I'm calling the overload which takes a string and an instance of JsonSerializerSettings. I'm only using the JsonSerializerSettings in order to handle the possible deserialization errors (via the Error property).

The github source code confirms that the overload I'm calling can possible return a null reference, via the MaybeNull attribute: take a look here for a confirmation.

My question is: in which cases newtonsoft JSON returns a null reference when deserializing a JSON string to a .NET type ?

Usually it returns an object of the given type populated or having its properties at the default value for their type, I have never encountered a case where null is returned instead.

EnricoMassone avatar Jul 20 '20 16:07 EnricoMassone

JsonConvert.DeserializeObject<string>("null") will return null.

TylerBrinkley avatar Jul 20 '20 17:07 TylerBrinkley

@TylerBrinkley thanks for the contibution.

I noticed that your code actually does not raise any warning inside of visual studio 2019, even with nullable reference types enabled.

The only way to get a warning is calling the other overload, the one taking a second parameter of type JsonSerializerSetting.

So, in my opinion, one of the followings is true (maybe both of them):

  • apart from the mentioned "null" input case, there is some other subtle corner case for a null reuturn value, involving JsonSerializerSettings in some way

  • the type annotation for the library is not complete and even the overload of JsonConvert.DeserializeObject\<T\> taking only a string parameter should be annoted as possibly returning null

EnricoMassone avatar Jul 20 '20 17:07 EnricoMassone

I think the single string parameter overload just got missed when the library was annotated.

I'd imagine with custom converters you could conceivably return null from a non "null" input as well.

TylerBrinkley avatar Jul 20 '20 19:07 TylerBrinkley

I think the single string parameter overload just got missed when the library was annotated.

I'd imagine with custom converters you could conceivably return null from a non "null" input as well.

@TylerBrinkley this definitely clarify the point.

To summarize the possibility of getting back a null value is only relegated to corner cases. Apart from an exotic custom converter or the "null" input case, we can stay sure that we never get a null reference back. Correct ?

I think this is a useful information for the library users which have enabled the nullable reference types feature.

EnricoMassone avatar Jul 21 '20 06:07 EnricoMassone

@TylerBrinkley do you now actually why JsonConvert.DeserializeObject<string>("null") returns null? I think "null" is not a correct JSON regarding to JSON format docs. So, I think more sense has the method throwing some ArgumentException that input string is not in JSON format.

lechu445 avatar Mar 23 '21 22:03 lechu445

@TylerBrinkley do you now actually why JsonConvert.DeserializeObject<string>("null") returns null? I think "null" is not a correct JSON regarding to JSON format docs. So, I think more sense has the method throwing some ArgumentException that input string is not in JSON format.

It seems that the string "null" is actually valid JSON. You can verify yourself here

EnricoMassone avatar Mar 24 '21 09:03 EnricoMassone

@EnricoMassone you are right, thank you for the answer.

In RFC 7159 there is such definition:

JSON can represent four primitive types (strings, numbers, booleans, and null) and two structured types (objects and arrays).

so, null, 1, "text", true are all valid JSONs.

lechu445 avatar Mar 24 '21 22:03 lechu445

Calling JsonConvert.DeserializeObject() with "null" will return null when deserializing into a class, too.

e.g.

public class User
{
}

...

User user = JsonConvert.DeserializeObject<User>("null");

// Prints `true`
Console.WriteLine(user == null);

This means that if you have nullable reference types enabled and you want JsonConvert.DeserializeObject() to return T instead of T?, you need to write a helper method.

Eli-Black-Work avatar May 20 '22 08:05 Eli-Black-Work