例如,对于Int32类型,JsonElement有一个GetInt32()方法,将返回该值作为整数或在不是整数时抛出异常,还有一个TryGetInt32()方法,它将解析的值复制到一个out变量中,并根据其是否能正确解析返回true或false。
对于几乎所有其他原始类型都适用相同的规则,但由于某种原因GetBoolean()和GetString()没有try...等效方法,尽管它们也会在无法解析值时抛出异常。
这似乎是一个明显的疏忽,让我觉得我做错了什么。是否有人能解释一下为什么它们不需要?
更新
不要在意原始答案,TryGet_number_type
方法并不像我(也许你)期望的那样工作——如果您尝试从 ValueKind
不是 Number
的元素中获取 "number_type",它们将会抛出异常(例如decimal
文档)。
TryGet...
API基本上会尝试将内部值解析为某个具体类型,但仅当该值为此尝试的具体类型的有效json type时(对于所有数字类型为Number
,对于Guid
,DateTime
和DateTimeOffset
为String
),否则它将抛出InvalidOperationException
,因此没有TryGetString
和TryGetBoolean
方法是没有意义的(字符串始终是字符串,布尔值始终是布尔值),它们的行为与Get
相同。
原始答案:
找不到不使用这些API的任何理由,但自己实现它们不应该是一个大问题(在标准库中有它们仍然很好):
根据文档,当值的ValueKind
不是 True
或 False
时,GetBoolean
方法会抛出异常。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
会抛出异常,如果value的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);
}
}
但我仍然感到困惑,直到我在一个github问题的评论中找到了一些描述这些方法的注释。以下是该评论的一部分(由我略微缩编,用此方法不会解析JSON字符串值的内容。
** 表示):
// 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
之间的区别。后者用于指示令牌的ValueKind
(Number、String、True、False)与期望的类型不匹配。前者(FormatException
)与通常预期的有点不同;它不是针对解析错误*(即"1.sg"不是int)抛出,而是针对超出范围错误抛出!
如果我们先看数值重载就更容易理解了。非Try
变体要么返回一个值,要么抛出两个异常之一:InvalidOperationException
如果值不是数字,FormatException
如果它们不适合。从GetInt32
文档中可以看到:
Exceptions
InvalidOperationException
此值的ValueKind
不是Number
。
FormatException
该值无法表示为Int32
。
与此相比,Try
变体将抛出一个异常——如果类型不是数字,则为InvalidOperationException
——但如果值不合适,它会返回false
。从TryGetInt32
文档中可以看到:
Exceptions
InvalidOperationException
此值的ValueKind
不是Number
。
Returns
如果该数可以表示为Int32
,则返回布尔值true
;否则,返回false
。
在这种情况下,“不合适”意味着该值对于底层类型来说太大/太小,例如使用[Try]GetInt32
时大于int.MaxValue
。
现在让我们回到booleans
的情况,您正确地注意到这里只有一个非Try
变体。在查看同一GitHub问题的评论时,我们发现了以下内容:
// InvalidOperationException if Type is not True or False
public bool GetBoolean();
文档如下:
异常
InvalidOperationException
此值的 ValueKind
既不是 True
,也不是 False
。
这里缺失了 FormatException
和 "不合适" 的情况。正如我们在数字的情况中所看到的那样,Try
变种为我们提供了检测 "是的,这是一个数字,但它超出了适当的范围"。布尔值只有两个可能的值 -- true
和 false
-- 没有要检测的范围,没有需要区分的 FormatException
。类似于 字符串
-- 它要么是一个 string
标记,要么就不是。
重要的是要注意,在所有情况下,如果 ValueKind
不符合方法的预期,则会抛出 InvalidOperationException
。如果假设的 TryGetBoolean
遇到 "abc" 的 string
值,它将不会返回 false
,而是抛出 InvalidOperationException
,因为 ValueKind
不是 True
或 False
。但这已经是 GetBoolean
做的事情了!因此没有必要再添加一个单独的方法。
* 注意:没有解析错误,因为这些方法实际上不会尝试解析 json string
标记内部的数字/布尔值,它们只考虑正确的标记类型的值。换句话说,带引号的数字/布尔值目前不受支持:
{
"number": 1234,
"notNumber": "1234",
"bool": true,
"notBool": "false"
}
目前(2020年5月30日),有要求添加支持。届时,我们可能会看到 TryGet
方法的可用性/功能发生变化。
TryGetString()
。对于布尔数据来说,除非您传递数字零,否则它始终为真。因此我认为不需要提供TryGetBoolean
方法。 - Rena