空字符串 JValue 的 JTokenType 与其他空 JValue 不匹配

JTokenType for null string JValue does not match other null JValues

提问人:Matthew Tenczar 提问时间:11/18/2023 最后编辑:Matthew Tenczar 更新时间:11/18/2023 访问量:26

问:

当我遇到一个困扰我的失败测试时,我正在比较 JTokens 和 JToken.DeepEquals() 作为测试代码:

testResult: { "str": null, "int": 1 } // Child of a larger JObject
expectedResult: { "str": null, "int": 1 } //Parsed from a string with JToken.Parse()
if (!JToken.DeepEquals(testResult, expectedResult)) return TestFailed

我预计这些代币是相等的。我花了一段时间修补失败的测试,发现失败是比较“str”JValues的结果。testResult 的类型为 JTokenType.String,分析的 expectedResult 的类型为 JTokenType.Null。v1._valueType != v2._valueType

我最终能够通过以下测试重现该问题:

  string nullStr = null;
  JToken nullToken = null;

  var b0 = JToken.Equals(nullStr, nullToken); // True

  var jObj = new JObject()
  {
    ["str"] = nullStr, // null
    ["tkn"] = nullToken, // null
    ["ltrl"] = null
  };

  var b1 = JToken.Equals(jObj["str"], jObj["tkn"]); // False
  var b2 = JToken.Equals(jObj["tkn"], jObj["ltrl"]); // True
  var b3 = JToken.Equals(jObj["str"], jObj["ltrl"]); // False

  var t0 = jObj["str"].Type; // String
  var t1 = jObj["tkn"].Type; // Null
  var t2 = jObj["ltrl"].Type; // Null

  var parsedJObj = JToken.Parse(jObj.ToString());

  var b4 = JToken.Equals(parsedJObj["str"], parsedJObj["tkn"]); // True
  var b5 = JToken.Equals(parsedJObj["tkn"], parsedJObj["ltrl"]); // True
  var b6 = JToken.Equals(parsedJObj["str"], parsedJObj["ltrl"]); // True

  var t3 = parsedJObj["str"].Type; // Null
  var t4 = parsedJObj["tkn"].Type; // Null
  var t5 = parsedJObj["ltrl"].Type; // Null

  Console.WriteLine($@"null string = null token: {b0}
jObj[str] = jObj[tkn]: {b1}
jObj[tkn] = jObj[ltrl]: {b2}
jObj[str] = jObj[ltrl]: {b3}
jObj[str].Type: {t0}
jObj[tkn].Type: {t1}
jObj[ltrl].Type: {t2}
parsedJObj[str] = parsedJObj[tkn]: {b4}
parsedJObj[tkn] = parsedJObj[ltrl]: {b5}
parsedJObj[str] = parsedJObj[ltrl]: {b6}
parsedJObj[str].Type: {t3}
parsedJObj[tkn].Type: {t4}
parsedJObj[ltrl].Type: {t5}");

在我看来,分配字符串 null 文字有一种特殊情况,这是预期的行为吗? 我正在使用 .NET 7.0 和 Newtonsoft.Json 包 v13.0.3

C# json.net

评论

0赞 dbc 11/18/2023
json.net 中的奇怪行为:为什么 null JToken 替换为非 null 值?中所述,当值添加到 或 时,值总是被替换为非 null 类型。这回答了你的问题吗?nullJValueJTokenType.NullJObjectJArray

答:

0赞 dbc 11/18/2023 #1

json.net 中的奇怪行为:为什么 null JToken 替换为非 null 值?中所述,当您尝试向 or 添加值时,预期行为是,它将被替换为 类型的非 null 值。nullJObjectJArrayJValueJTokenType.Null

然而,令人惊讶的是

var jObj = new JObject()
{
    ["str"] = nullStr, // null
}

生成类型为 的令牌。我认为这可以解释如下。您正在使用字典索引初始值设定项语法来填充 .现在,JObject 实现的字典类型是 。但是不是,那么你是如何初始化它的呢?好吧,事实证明,有一个隐式运算符可以将 a 转换为 .而且,从此处此处的引用源中可以看出,即使对于字符串,这也总是返回 类型 ,也是如此:JTokenType.StringJObjectIDictionary<string, JToken>stringJTokenstringJTokenJValueJTokenType.Stringnull

public static implicit operator JToken(string? value)
{
    return new JValue(value);
}

public JValue(string? value)
    : this(value, JTokenType.String)
{
}

这解释了您看到的不一致之处。

如果你不喜欢这种行为,你可以使用一个构造函数或 setter,它采用一个而不是一个 ,比如 JProperty(string, object) 构造函数:objectJToken

var jObj = new JObject()
{
    new JProperty("str", nullStr),
    new JProperty("tkn", nullToken),
    new JProperty("ltrl", null),
};

通过使用此构造函数,不会调用隐式运算符,并且所有三个值最终都是 类型。JTokenType.Null

笔记:

在这里演示小提琴。

评论

0赞 Matthew Tenczar 11/30/2023
精彩的解释!我认为它与隐式运算符有关,我将改用 JProperty 构造函数。
0赞 dbc 11/30/2023
@MatthewTenczar - 很高兴能帮上忙。如果问题得到回答,请将其标记为这样