为什么 System.Text.Json.JsonElement 没有 TryGetString() 或 TryGetBoolean()

And*_*ndy 13 c# json asp.net-core system.text.json

System.Text.Json我正在使用返回对象的.NET Core 命名空间解析一些 JSON 数据JsonElement

例如,对于 Int32 类型,JsonElement有一个GetInt32()将值作为整数返回,如果不是整数则抛出异常,还有一个TryGetInt32()将解析后的值复制到out变量并返回 true 或 false,具体取决于是否它能够正确解析。

这同样适用于几乎所有其他基本类型,但由于某种原因,GetBoolean()并且GetString()没有try...等效项,即使如果无法正确解析值,它们也会抛出异常。

这似乎是一个明显的疏忽,它让我觉得我做错了什么。谁能解释为什么不需要它们?

Gur*_*ron 8

UPD

不要介意原来的答案,这些TryGet_number_type方法不会像我(我假设你)期望的那样工作 - 如果你尝试从ValueKind不是 a 的元素中获取“number_type” Number(例如decimal文档),它们会抛出异常。

因此,这个API 基本上尝试将内部值解析为某种具体类型TryGet...,但前提是该尝试的具体类型的值是有效的json 类型Number(对于所有数字类型,StringforGuid和),否则它将抛出,因此没有意义有一个and方法,因为这里没有歧义(字符串始终是字符串,布尔值始终是布尔值),并且它们的行为与对应的完全相同。DateTimeDateTimeOffsetInvalidOperationExceptionTryGetStringTryGetBooleanGet

原答案

无法找到没有这个 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;
}
Run Code Online (Sandbox Code Playgroud)

如果值既不是也不是则GetString 抛出ValueKindStringNull

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;
}
Run Code Online (Sandbox Code Playgroud)

以及样本测试:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)


pin*_*x33 7

文档注释中的注释似乎暗示了答案(但没有完全解释) :

\n
\n

此方法不解析 JSON 字符串值的内容。

\n
\n

但我仍然很困惑,直到我在 github 问题中发现了一些描述这些方法的评论。这是该评论的片段(稍微省略,**由我添加):

\n
// InvalidOperationException if Type is not True or False\npublic bool GetBoolean();\n\n// InvalidOperationException if Type is not Number \n// FormatException if value does not fit \npublic decimal GetDecimal(); \npublic double GetDouble(); \npublic int GetInt32(); \n\n// InvalidOperationException if Type is not Number\n// false if value **does not fit.** \npublic bool TryGetDecimal(out decimal value); \npublic bool TryGetDouble(out double value); \npublic bool TryGetInt32(out int value);\n
Run Code Online (Sandbox Code Playgroud)\n

因此,最终归结为 aFormatException和 an之间的区别InvalidOperationException

\n

后者用于指示标记ValueKind(数字、字符串、True、False)与预期类型不匹配。前一个 ( FormatException) 的使用有点偏离标签,超出了人们通常的预期;它不是因解析错误*(即“1.sg”不是 )而抛出,而是因超出范围错误int而抛出!

\n

如果我们首先看看数字重载,就会更好地理解这一点。非Try变量要么返回一个值,要么抛出两个异常之一:InvalidOperationException如果值不是数字,FormatExceptions如果它们不适合。从文档中GetInt32

\n
\n

例外情况

\n

InvalidOperationException

\n

该值的\xc2\xa0 ValueKind\xc2\xa0 不是\xc2\xa0 Number

\n

FormatException

\n

该值不能表示为Int32

\n
\n

将此与Try抛出一个异常的变体(InvalidOperationException如果类型不是数字)进行比较,但如果值不适合则返回。 false从文档中TryGetInt32

\n
\n

例外情况

\n

InvalidOperationException

\n

该值的\xc2\xa0 ValueKind\xc2\xa0 不是\xc2\xa0 Number

\n

退货

\n

布尔值true\xc2\xa0 如果数字可以表示为Int32; 否则,\xc2\xa0 false

\n
\n

在这种情况下,“不适合”意味着该值对于基础类型来说太大/太小,即。大于int.MaxValue使用时[Try]GetInt32

\n

现在让我们回到booleans您正确地注意到只有一个非Try变体的情况。查看同一 github 问题中的评论,我们看到:

\n
// InvalidOperationException if Type is not True or False\npublic bool GetBoolean();\n
Run Code Online (Sandbox Code Playgroud)\n

和文档:

\n
\n

例外情况

\n

InvalidOperationException

\n

该值的\xc2\xa0 ValueKind\xc2\xa0 既不是True也不是False

\n
\n

这里缺少的是FormatException“不适合”的情况。正如我们在上面看到的数字情况,该Try变体让我们检测到“是的,这是一个数字,但它超出了适当的范围”。Booleans只有两个可能的值——true并且false——没有范围可检测,也没有FormatException可区分。与strings--it 类似,要么是string令牌,要么不是。

\n

需要注意的是,在所有情况下,InvalidOperationException如果ValueKind与方法所期望的不匹配,就会抛出异常。如果假设遇到“abc”值,则TryGetBoolean不会返回,它会抛出一个,因为不是或。但这已经是这样了!因此不需要单独的方法。falsestringInvalidOperationExceptionValueKindTrueFalseGetBoolean

\n

*注意:没有解析错误,因为这些方法实际上并不尝试解析json令牌内的数字/布尔值string,它们只考虑正确令牌类型的值。换句话说,当前不支持引用的数字/布尔值:

\n
{\n   "number": 1234,\n   "notNumber": "1234",\n   "bool": true,\n   "notBool": "false"\n}\n
Run Code Online (Sandbox Code Playgroud)\n

目前(2020-05-30)请求添加对此的支持。届时我们可能会看到方法的可用性/功能发生变化TryGet

\n