【问题标题】:Why doesn't System.Text.Json.JsonElement have TryGetString() or TryGetBoolean()为什么 System.Text.Json.JsonElement 没有 TryGetString() 或 TryGetBoolean()
【发布时间】:2020-05-30 08:45:22
【问题描述】:

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

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

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

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

【问题讨论】:

  • 那是因为json数据中的每个数据都可以成功转换为字符串,所以不需要TryGetString()。对于boolean数据,除非你传递数字零,否则它总是可以为真。所以我认为没有必要给TryGetBoolean方法。

标签: c# json asp.net-core system.text.json


【解决方案1】:

UPD

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

所以这个TryGet... API 基本上会尝试将内部值解析为某种具体类型,但前提是该尝试的具体类型的值是有效的json typeNumber 用于所有数字类型,String 用于Guid , DateTimeDateTimeOffset),否则它将抛出 InvalidOperationException,因此使用 TryGetStringTryGetBoolean 方法没有意义,因为这里没有歧义(字符串始终是字符串,布尔值始终是boolean) 并且它们的行为与 Get 对应物完全相同。

原答案

无法找到没有此 API 的任何理由,但自己实现它们应该不是一个大问题(在标准库中包含它们仍然很好):

如果值的ValueKind 既不是True 也不是False,则根据docs 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;
}

如果值ValueKind 既不是String 也不是Null,则GetString throws

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);
    }
}

【讨论】:

  • 谢谢,我现在看到了 - 看起来“TryGet”不像 C# 的内置“TryParse”方法那样努力,而我假设他们会这样做
  • @Andy 对我来说也一样,虽然你可以为这种行为提出理由,但仍然在名称中使用 Try 使其不是很清晰)
【解决方案2】:

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

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

但是直到我找到一些comments in a github issue 描述这些方法之前,我仍然感到困惑。这是该评论的 sn-p(略微省略,** 由我添加):

// 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);

所以,最终归结为FormatExceptionInvalidOperationException 之间的区别。

后者用于表示令牌的ValueKind(数字、字符串、真、假)与预期类型不匹配。前者 (FormatException) 的使用与人们通常期望的有所不同;而不是因为解析错误而被抛出*(即“1.sg”不是int),而是因为超出范围错误

如果我们首先查看数字重载,则可以更好地理解这一点。非Try 变体要么返回一个值,要么抛出两个异常之一:InvalidOperationException 如果值不是数字,FormatExceptions 如果它们不适合。来自GetInt32 的文档:

例外情况

InvalidOperationException

此值的 ValueKind 不是 Number

FormatException

该值不能表示为Int32

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

例外情况

InvalidOperationException

此值的 ValueKind 不是 Number

退货

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

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

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

// InvalidOperationException if Type is not True or False
public bool GetBoolean();

还有文档:

例外情况

InvalidOperationException

此值的 ValueKind 既不是 True 也不是 False

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

需要注意的重要一点是,在所有情况下,如果 ValueKind 与方法所期望的不匹配,则会抛出 InvalidOperationException。假设的TryGetBoolean 不会返回false,如果遇到“abc”的string 值,它会抛出InvalidOperationException,因为ValueKind 不是TrueFalse但这已经是 GetBoolean 所做的了! 所以不需要单独的方法。

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

{
   "number": 1234,
   "notNumber": "1234",
   "bool": true,
   "notBool": "false"
}

目前 (2020-05-30) 有一个 request to add support 用于此。届时我们可能会看到 TryGet 方法的可用性/功能发生变化。

【讨论】:

  • 我的回答迟到了 12 分钟——因为我一直在我的手机上打字。它触及了其他答案的一些观点,但更详细一点,所以我选择不删除它
猜你喜欢
  • 2015-01-30
  • 2015-12-08
  • 1970-01-01
  • 1970-01-01
  • 2020-06-17
  • 2019-12-05
  • 2011-05-31
  • 2011-09-14
  • 1970-01-01
相关资源
最近更新 更多