我正在使用返回JsonElement
对象的.NET CoreSystem.Text.Json
命名空间解析一些JSON数据。
例如,对于 Int32 类型,JsonElement
有一个GetInt32()
,如果值不是整数,则会以整数形式返回值或引发异常,还有一个TryGetInt32()
将分析的值复制到out
变量并根据它是否能够正确分析返回 true 或 false。
这同样适用于几乎所有其他基元类型,但由于某种原因,GetBoolean()
和GetString()
没有try...
等效项,即使如果无法正确解析值,它们也会引发异常。
这似乎是一个明显的疏忽,它让我觉得我做错了什么。谁能解释为什么不需要它们?
UPD
不要介意原始答案,TryGet_number_type
方法并不像我(我假设您)所期望的那样工作 - 如果您尝试从ValueKind
不是Number
的元素中获取"number_type",它们会抛出(例如decimal
文档)。
所以这个TryGet...
API 基本上尝试将内部值解析为某种具体类型,但前提是这个尝试的具体类型的值是有效的 json 类型(Number
对于所有数值类型,String
对于Guid
、DateTime
和DateTimeOffset
),否则它会抛出InvalidOperationException
,因此有一个TryGetString
和TryGetBoolean
方法没有意义,因为这里没有歧义(字符串总是字符串和布尔值始终是布尔值),它们的行为将与Get
对应项完全相同。
原答案:
无法找到没有此 API 的任何理由,但自己实现它们应该不是一个大问题(将它们放在标准库中仍然会很好):
根据文档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;
}
如果值的ValueKind
既不String
也不Null
,则GetString
抛出:
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);
}
}
答案似乎通过文档中注释中的注释暗示(但未完全解释):
此方法不会分析 JSON 字符串值的内容。
但我仍然感到困惑,直到我在 github 问题中找到一些描述这些方法的评论。以下是该评论的片段(略微省略,**
我添加的):
// 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
(数字、字符串、真、假)与预期类型不匹配。前者(FormatException
)的使用有点偏离标签,与人们通常的预期不同;而不是因为解析错误*而被抛出(即"1.sg"不是int
),而是因为超出范围的错误而被抛出!
如果我们先看一下数字重载,可以更好地理解这一点。非Try
变体要么返回一个值,要么抛出以下两个异常之一:如果值不是数字,InvalidOperationException
,如果它们不适合,FormatExceptions
。从GetInt32
的文档:
异常
InvalidOperationException
此值的
ValueKind
不是Number
。
FormatException
该值不能表示为
Int32
。
将此与引发一个异常的Try
变体进行比较 - 如果类型不是数字,则InvalidOperationException
- 但如果值不合适,则返回false
。从TryGetInt32
的文档:
异常
InvalidOperationException
此值的
ValueKind
不是Number
。返回
布尔
true
该数是否可以表示为Int32
;否则,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
变体让我们检测到"是的,这是一个数字,但它超出了适当的范围"。Booleans
只有两个可能的值 -true
和false
- 没有要检测的范围,没有要区分FormatException
。与strings
类似 - 它要么是string
令牌,要么不是。
需要注意的重要一点是,在所有情况下,如果ValueKind
与方法的预期不匹配,则会引发InvalidOperationException
。假设的TryGetBoolean
不会返回false
如果遇到string
值"abc",它会抛出InvalidOperationException
,因为ValueKind
不是True
或False
。但这已经是GetBoolean
所做的了!因此,不需要单独的方法。
*注意:没有解析错误,因为这些方法实际上并不尝试解析jsonstring
令牌中的数字/布尔值,它们只考虑正确令牌类型的值。换句话说,目前不支持带引号的数字/布尔值:
{
"number": 1234,
"notNumber": "1234",
"bool": true,
"notBool": "false"
}
目前(2020-05-30)有一个请求添加对此的支持。此时,我们可能会看到TryGet
方法的可用性/功能发生变化。