将 JToken 显式转换为带有错误 "possible null reference return" 警告的字符串

Explicit conversion of JToken to string with false "possible null reference return" warning

我正在使用 C#10、Newtonsoft.Json 和可为 null 的引用类型。我围绕 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;
    }
}

如您所见,我在 SelectTokenAsString 方法中添加了注释。为什么 Visual Studio 用绿色波浪线在这一行下划线,并认为可能存在空引用 return 并可能转换为空?很明显,SelectToken 方法永远不会 return null。有趣的是,VS 认为 SelectTokenAsInt 方法完全没问题。

JToken 转换为 string 实际上是 returns string? (see public static explicit operator string?(JToken? value))。所以警告是关于将 JToken 转换为 string 的结果而不是 SelectToken 的结果(我假设这种行为的原因可能是处理 null json 令牌)。

您可以尝试使用 JToken.ToString 重载(它在内部调用 JToken.ToString(Formatting.Indented) 所以它可能不会给出您期望的结果):

public string SelectTokenAsString(string jsonPath) => SelectToken(jsonPath).ToString();

Guru Stron 的回答是正确的,但我想提供更多解释。

如果你进一步分解你的代码,下面是那一行发生的事情:

    JToken token = SelectToken(jsonPath); // no warning
    return (string)token!; // warning

因此,即使您的 SelectToken 方法不是 return 可空类型,将该 JToken 转换为 string 也可能 return 空值,由于执行该转换的隐式运算符。请记住,null 是一个有效的 JSON 标记,因此以下代码将为您提供一个 null 值,即使您确保 JToken? 不为空的检查通过并且不会抛出异常。

string? foo = new JsonWrapper(@"{""foo"": null}").SelectTokenAsString("foo");

您最好再做一次空值检查,以确保您不会得到这样的空字符串:

    JToken token = SelectToken(jsonPath);
    return (string?)token ?? throw new InvalidCastException($"Token value at path {jsonPath} was null");