Почему в System.Text. Json .JsonElement нет TryGetString () или TryGetBoolean () - PullRequest
2 голосов
/ 30 мая 2020

Я анализирую некоторые JSON данные с помощью пространства имен. NET Core System.Text.Json, которое возвращает JsonElement объекты.

Например, для типов Int32 JsonElement имеет GetInt32() который вернет значение как целое число или вызовет исключение, если оно не является целым числом, а также есть TryGetInt32(), который копирует проанализированное значение в переменную out и возвращает истину или ложь в зависимости от того, было ли оно способен правильно анализировать.

То же самое применимо почти ко всем другим примитивным типам, но по какой-то причине GetBoolean() и GetString() не имеют эквивалента try..., хотя они также вызовут исключение, если значение не может быть правильно проанализировано.

Это кажется такой очевидной оплошностью, что заставляет меня думать, что я что-то делаю неправильно. Кто-нибудь может объяснить, почему они не нужны?

Ответы [ 2 ]

4 голосов
/ 30 мая 2020

UPD

Не обращайте внимания на исходный ответ, методы TryGet_number_type не работают так, как я (и я предполагаю, что вы) ожидаете - они бросят, если вы попробуйте получить "number_type" из элемента, который ValueKind не является Number (например, decimal docs ).

Итак, этот TryGet... API в основном пытается проанализировать внутреннее значение как некоторый конкретный тип, но только если значение является допустимым json тип для этого попытки конкретного типа (Number для всех numeri c типов, 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 выдает , если значение 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);
    }
}
1 голос
/ 30 мая 2020

Кажется, что на ответ намекает (но не полностью объясняет) примечание в примечаниях в документации:

Этот метод не анализирует содержимое 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 токена (Number, String, True, False) не соответствует ожидаемому типу. Первый (FormatException) используется немного не по метке, чем можно было бы ожидать; вместо того, чтобы выдаваться за ошибки анализа * (ie. «1.sg» не является int), он выдается за ошибки вне диапазона !

Это лучше Понятно, если мы сначала посмотрим на число перегрузок c. Варианты, отличные от Try, либо возвращают значение, либо вызывают одно из двух исключений: InvalidOperationException, если значения не являются числами, FormatExceptions, если они не подходят . Из документации для GetInt32:

Исключения

InvalidOperationException

Это значение ValueKind не Number.

FormatException

Значение не может быть представлено как Int32.

Сравните это с вариантами Try, которые вызывают одно исключение - InvalidOperationException, если тип не является числом - но возвращает false, если значение не подходит. Из документации для TryGetInt32:

Исключения

InvalidOperationException

Это значение ValueKind не Number.

Возвращает

Boolean true, если число может быть представлено как Int32; в противном случае false.

В этом случае «не подходит» означает, что значение слишком велико / мало для базового типа, ie. больше int.MaxValue при использовании [Try]GetInt32

Теперь давайте вернемся к случаю booleans, где вы правильно заметили, что существует только вариант, отличный от Try. Глядя на комментарии в том же выпуске github, мы видим следующее:

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

И документация:

Исключения

InvalidOperationException

Это значение ValueKind не является ни True, ни False.

Здесь отсутствует FormatException и случай «не подходит». Как мы видели выше в случае чисел, вариант Try дает нам определение «да, это число, но оно выходит за пределы допустимого диапазона». Booleans имеет только два возможных значения - true и false - нет диапазона для определения, нет FormatException, от которого можно было бы отличить guish. Аналогично strings - либо это токен string, либо нет.

Важно отметить, что во всех случаях выдается InvalidOperationException, если ValueKind не соответствует тому, что ожидается по методу. Гипотетический TryGetBoolean не вернет false, если встретит string значение «ab c», он выдаст InvalidOperationException, потому что ValueKind не был True или False . Но это уже то, что делает GetBoolean! Так что нет необходимости в отдельном методе.

* Примечание : ошибки синтаксического анализа нет, потому что методы фактически не попытка синтаксического анализа чисел / логических значений внутри токена json string, они учитывают только значения правильного типа токена. Другими словами, числа в кавычках / bools в настоящее время не поддерживаются:

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

В настоящее время (2020-05-30) имеется запрос на добавление поддержки для этого. В это время мы можем увидеть изменение доступности / функциональности методов TryGet.

...