为什么 System.Text.Json.JsonElement 没有 TryGetString() 或 TryGetBoolean()
Why doesn't System.Text.Json.JsonElement have TryGetString() or TryGetBoolean()
我正在使用 returns JsonElement
个对象的 .NET Core System.Text.Json
命名空间解析一些 JSON 数据。
对于 Int32 类型,例如,JsonElement
有一个 GetInt32()
,它将 return 值作为整数,如果不是整数则抛出异常,并且有也是一个 TryGetInt32()
,它将解析的值复制到一个 out
变量,return 的真或假取决于它是否能够正确解析。
这同样适用于几乎所有其他基本类型,但由于某些原因,GetBoolean()
和 GetString()
没有 try...
等价物,即使它们在值不能时也会抛出异常被正确解析。
这似乎是一个明显的疏忽,让我觉得我做错了什么。谁能解释为什么不需要它们?
UPD
不要介意原来的答案,TryGet_number_type
方法并不像我(我假设你)期望的那样工作 - 如果你试图从 "number_type" ValueKind
不是 Number
的元素(例如 decimal
docs)。
所以这个 TryGet...
API 基本上尝试将内部值解析为某种具体类型,但前提是值对于此尝试的具体类型有效 json type(Number
对于所有数字类型,String
用于 Guid
、DateTime
和 DateTimeOffset
),否则它会抛出 InvalidOperationException
,因此使用 TryGetString
和 TryGetBoolean
方法导致这里没有歧义(字符串始终是字符串,布尔值始终是布尔值)并且它们的行为与 Get
对应物完全相同。
原回答:
无法找到没有这个 API 的任何原因,但是自己实现它们应该不是什么大问题(将它们放在标准库中仍然很好):
根据 docs GetBoolean
如果值的 ValueKind
既不是 True
也不是 False
.
则抛出
public static bool TryGetBoolean(this JsonElement je, out bool parsed)
{
var (p, r) = je.ValueKind switch
{
JsonValueKind.True => (true, true),
JsonValueKind.False => (false, true),
_ => (default, false)
};
parsed = p;
return r;
}
和 GetString
throws 如果值的 ValueKind
既不是 String
也不是 Null
:
public static bool TryGetsString(this JsonElement je, out string parsed)
{
var (p, r) = je.ValueKind switch
{
JsonValueKind.String => (je.GetString(), true),
JsonValueKind.Null => (null, true),
_ => (default, false)
};
parsed = p;
return r;
}
和样本测试:
using (JsonDocument document = JsonDocument.Parse(@"{""bool"": true, ""str"": ""string""}"))
{
if (document.RootElement.GetProperty("bool").TryGetBoolean(out var b))
{
Console.WriteLine(b);
}
if (document.RootElement.GetProperty("str").TryGetString( out var s))
{
Console.WriteLine(s);
}
}
文档 remarks 中的注释似乎暗示了答案(但未完全解释):
This method does not parse the contents of a JSON string value.
但直到我找到一些 comments in a github issue 描述这些方法之前,我仍然感到困惑。这是该评论的片段(略有删减,**
由我添加):
// InvalidOperationException if Type is not True or False
public bool GetBoolean();
// InvalidOperationException if Type is not Number
// FormatException if value does not fit
public decimal GetDecimal();
public double GetDouble();
public int GetInt32();
// InvalidOperationException if Type is not Number
// false if value **does not fit.**
public bool TryGetDecimal(out decimal value);
public bool TryGetDouble(out double value);
public bool TryGetInt32(out int value);
因此,最终归结为 FormatException
和 InvalidOperationException
之间的差异。
后者用于表示token的ValueKind
(Number, String, True, False)与预期类型不匹配。前者 (FormatException
) 的使用有点偏离人们通常期望的标签;它不是因解析错误*(即“1.sg”不是int
)而被抛出,而是因超出范围错误而被抛出!
如果我们先看一下数字重载,就可以更好地理解这一点。非 Try
变体 return 一个值或抛出两个异常之一:InvalidOperationException
如果值不是数字,FormatExceptions
如果它们 不适合。来自 GetInt32
的文档:
Exceptions
InvalidOperationException
This value's ValueKind
is not Number
.
FormatException
The value cannot be represented as an Int32
.
将此与抛出一个异常的 Try
变体进行比较 - InvalidOperationException
如果类型不是数字 - 但 return false
如果值不合适。来自 TryGetInt32
的文档:
Exceptions
InvalidOperationException
This value's ValueKind
is not Number
.
Returns
Boolean true
if the number can be represented as an Int32
; otherwise, false
.
在这种情况下,“不适合”意味着该值对于基础类型来说太 large/small,即。使用 [Try]GetInt32
时大于 int.MaxValue
现在让我们回到 booleans
的情况,您正确地注意到只有一个非 Try
变体。查看同一 github 问题中的评论,我们看到:
// InvalidOperationException if Type is not True or False
public bool GetBoolean();
和文档:
Exceptions
InvalidOperationException
This value's ValueKind
is neither True
nor False
.
这里缺少的是 FormatException
和“不适合”的情况。正如我们在上面的数字案例中看到的那样,Try
变体让我们检测到“是的,这是一个数字,但它超出了适当的范围”。 Booleans
只有两个可能的值——true
和 false
——没有检测范围,没有 FormatException
可以区分。与 strings
相似——它要么是 string
标记,要么不是。
需要注意的重要一点是,在所有情况下,如果 ValueKind
与方法预期的不匹配,则会抛出 InvalidOperationException
。假设的 TryGetBoolean
不会 return false
如果它遇到 string
值为“abc”,它会抛出 InvalidOperationException
因为 ValueKind
不是 True
或 False
。 但这已经是 GetBoolean
所做的! 所以不需要单独的方法。
* 注意:没有解析错误,因为这些方法实际上并没有尝试 解析 numbers/bools json string
令牌,他们只考虑正确令牌类型的值。换句话说,目前不支持引用numbers/bools:
{
"number": 1234,
"notNumber": "1234",
"bool": true,
"notBool": "false"
}
目前 (2020-05-30) request to add support 为此。届时我们可能会看到 TryGet
方法的 availability/functionality 发生变化。
我正在使用 returns JsonElement
个对象的 .NET Core System.Text.Json
命名空间解析一些 JSON 数据。
对于 Int32 类型,例如,JsonElement
有一个 GetInt32()
,它将 return 值作为整数,如果不是整数则抛出异常,并且有也是一个 TryGetInt32()
,它将解析的值复制到一个 out
变量,return 的真或假取决于它是否能够正确解析。
这同样适用于几乎所有其他基本类型,但由于某些原因,GetBoolean()
和 GetString()
没有 try...
等价物,即使它们在值不能时也会抛出异常被正确解析。
这似乎是一个明显的疏忽,让我觉得我做错了什么。谁能解释为什么不需要它们?
UPD
不要介意原来的答案,TryGet_number_type
方法并不像我(我假设你)期望的那样工作 - 如果你试图从 "number_type" ValueKind
不是 Number
的元素(例如 decimal
docs)。
所以这个 TryGet...
API 基本上尝试将内部值解析为某种具体类型,但前提是值对于此尝试的具体类型有效 json type(Number
对于所有数字类型,String
用于 Guid
、DateTime
和 DateTimeOffset
),否则它会抛出 InvalidOperationException
,因此使用 TryGetString
和 TryGetBoolean
方法导致这里没有歧义(字符串始终是字符串,布尔值始终是布尔值)并且它们的行为与 Get
对应物完全相同。
原回答:
无法找到没有这个 API 的任何原因,但是自己实现它们应该不是什么大问题(将它们放在标准库中仍然很好):
根据 docs GetBoolean
如果值的 ValueKind
既不是 True
也不是 False
.
public static bool TryGetBoolean(this JsonElement je, out bool parsed)
{
var (p, r) = je.ValueKind switch
{
JsonValueKind.True => (true, true),
JsonValueKind.False => (false, true),
_ => (default, false)
};
parsed = p;
return r;
}
和 GetString
throws 如果值的 ValueKind
既不是 String
也不是 Null
:
public static bool TryGetsString(this JsonElement je, out string parsed)
{
var (p, r) = je.ValueKind switch
{
JsonValueKind.String => (je.GetString(), true),
JsonValueKind.Null => (null, true),
_ => (default, false)
};
parsed = p;
return r;
}
和样本测试:
using (JsonDocument document = JsonDocument.Parse(@"{""bool"": true, ""str"": ""string""}"))
{
if (document.RootElement.GetProperty("bool").TryGetBoolean(out var b))
{
Console.WriteLine(b);
}
if (document.RootElement.GetProperty("str").TryGetString( out var s))
{
Console.WriteLine(s);
}
}
文档 remarks 中的注释似乎暗示了答案(但未完全解释):
This method does not parse the contents of a JSON string value.
但直到我找到一些 comments in a github issue 描述这些方法之前,我仍然感到困惑。这是该评论的片段(略有删减,**
由我添加):
// InvalidOperationException if Type is not True or False
public bool GetBoolean();
// InvalidOperationException if Type is not Number
// FormatException if value does not fit
public decimal GetDecimal();
public double GetDouble();
public int GetInt32();
// InvalidOperationException if Type is not Number
// false if value **does not fit.**
public bool TryGetDecimal(out decimal value);
public bool TryGetDouble(out double value);
public bool TryGetInt32(out int value);
因此,最终归结为 FormatException
和 InvalidOperationException
之间的差异。
后者用于表示token的ValueKind
(Number, String, True, False)与预期类型不匹配。前者 (FormatException
) 的使用有点偏离人们通常期望的标签;它不是因解析错误*(即“1.sg”不是int
)而被抛出,而是因超出范围错误而被抛出!
如果我们先看一下数字重载,就可以更好地理解这一点。非 Try
变体 return 一个值或抛出两个异常之一:InvalidOperationException
如果值不是数字,FormatExceptions
如果它们 不适合。来自 GetInt32
的文档:
Exceptions
InvalidOperationException
This value's
ValueKind
is notNumber
.
FormatException
The value cannot be represented as an
Int32
.
将此与抛出一个异常的 Try
变体进行比较 - InvalidOperationException
如果类型不是数字 - 但 return false
如果值不合适。来自 TryGetInt32
的文档:
Exceptions
InvalidOperationException
This value's
ValueKind
is notNumber
.Returns
Boolean
true
if the number can be represented as anInt32
; otherwise,false
.
在这种情况下,“不适合”意味着该值对于基础类型来说太 large/small,即。使用 [Try]GetInt32
int.MaxValue
现在让我们回到 booleans
的情况,您正确地注意到只有一个非 Try
变体。查看同一 github 问题中的评论,我们看到:
// InvalidOperationException if Type is not True or False
public bool GetBoolean();
和文档:
Exceptions
InvalidOperationException
This value's
ValueKind
is neitherTrue
norFalse
.
这里缺少的是 FormatException
和“不适合”的情况。正如我们在上面的数字案例中看到的那样,Try
变体让我们检测到“是的,这是一个数字,但它超出了适当的范围”。 Booleans
只有两个可能的值——true
和 false
——没有检测范围,没有 FormatException
可以区分。与 strings
相似——它要么是 string
标记,要么不是。
需要注意的重要一点是,在所有情况下,如果 ValueKind
与方法预期的不匹配,则会抛出 InvalidOperationException
。假设的 TryGetBoolean
不会 return false
如果它遇到 string
值为“abc”,它会抛出 InvalidOperationException
因为 ValueKind
不是 True
或 False
。 但这已经是 GetBoolean
所做的! 所以不需要单独的方法。
* 注意:没有解析错误,因为这些方法实际上并没有尝试 解析 numbers/bools json string
令牌,他们只考虑正确令牌类型的值。换句话说,目前不支持引用numbers/bools:
{
"number": 1234,
"notNumber": "1234",
"bool": true,
"notBool": "false"
}
目前 (2020-05-30) request to add support 为此。届时我们可能会看到 TryGet
方法的 availability/functionality 发生变化。