I'm using C#10, Newtonsoft.Json and nullable reference types. I made a simple wrapper around JObject:
public class JsonWrapper
{
readonly JObject j;
public JsonWrapper(string json)
{
j = JObject.Parse(json);
}
public string SelectTokenAsString(string jsonPath)
{
// 1st warning: Possible null reference return
// 2nd warning: Converting null literal or possible null value to non-nullable
return (string)SelectToken(jsonPath);
}
public int SelectTokenAsInt(string jsonPath)
{
return (int)SelectToken(jsonPath);
}
JToken SelectToken(string jsonPath)
{
if (jsonPath == null) throw new ArgumentNullException(nameof(jsonPath));
JToken? result = j.SelectToken(jsonPath);
if (result == null) throw new MyException("Bla-bla-bla");
return result;
}
}
As you can see I put comments in SelectTokenAsString
method. Why Visual Studio underline this row with green wiggily line and thinks there are possible null reference return and possible conversion to null? It's pretty clear that SelectToken method is never return null. Interestingly that VS thinks that SelectTokenAsInt
method is totally ok which it is.
CodePudding user response:
Casting JToken
to string
actually returns string?
(see public static explicit operator string?(JToken? value)
). So warning is raised about the result of casting JToken
to string
not the result of SelectToken
(I assume the reason for such behaviour can be handling of null
json token).
You can try using JToken.ToString
overload (which internally calls JToken.ToString(Formatting.Indented)
so potentially it can give not the result you expect):
public string SelectTokenAsString(string jsonPath) => SelectToken(jsonPath).ToString();
CodePudding user response:
Guru Stron's answer is correct, but I wanted to provide a little more explanation.
If you break your code down a little further, here's what's happening on that line:
JToken token = SelectToken(jsonPath); // no warning
return (string)token!; // warning
So even though your SelectToken
method doesn't return a nullable type, the conversion of that JToken into a string
might return a null value, due to the implicit operator that performs that conversion. Remember that null
is a valid JSON token, so the following code would give you a null
value, even though your check to ensure the JToken?
isn't null passes and doesn't throw an exception.
string? foo = new JsonWrapper(@"{""foo"": null}").SelectTokenAsString("foo");
You're probably better off doing one more null check to make sure you don't end up with a null string like this:
JToken token = SelectToken(jsonPath);
return (string?)token ?? throw new InvalidCastException($"Token value at path {jsonPath} was null");