提问人:Matthew Tenczar 提问时间:11/18/2023 最后编辑:Matthew Tenczar 更新时间:11/18/2023 访问量:26
空字符串 JValue 的 JTokenType 与其他空 JValue 不匹配
JTokenType for null string JValue does not match other null JValues
问:
当我遇到一个困扰我的失败测试时,我正在比较 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
答:
如 json.net 中的奇怪行为:为什么 null JToken 替换为非 null 值?中所述,当您尝试向 or 添加值时,预期行为是,它将被替换为 类型的非 null 值。null
JObject
JArray
JValue
JTokenType.Null
然而,令人惊讶的是
var jObj = new JObject()
{
["str"] = nullStr, // null
}
生成类型为 的令牌。我认为这可以解释如下。您正在使用字典索引初始值设定项语法来填充 .现在,JObject
实现的字典类型是 。但是不是,那么你是如何初始化它的呢?好吧,事实证明,有一个隐式运算符可以将 a 转换为 .而且,从此处和此处的引用源中可以看出,即使对于字符串,这也总是返回 类型 ,也是如此:JTokenType.String
JObject
IDictionary<string, JToken>
string
JToken
string
JToken
JValue
JTokenType.String
null
public static implicit operator JToken(string? value) { return new JValue(value); } public JValue(string? value) : this(value, JTokenType.String) { }
这解释了您看到的不一致之处。
如果你不喜欢这种行为,你可以使用一个构造函数或 setter,它采用一个而不是一个 ,比如 JProperty(string, object)
构造函数:object
JToken
var jObj = new JObject()
{
new JProperty("str", nullStr),
new JProperty("tkn", nullToken),
new JProperty("ltrl", null),
};
通过使用此构造函数,不会调用隐式运算符,并且所有三个值最终都是 类型。JTokenType.Null
笔记:
- 没有静态方法,只有
JToken.DeepEquals()。
因此,当您编写时,您实际上是在调用基类静态方法对象。等于(对象,对象)。
JToken.Equals()
JToken.Equals(jObj["str"], jObj["tkn"])
在这里演示小提琴。
评论
null
JValue
JTokenType.Null
JObject
JArray