Получение вложенных свойств с System.Text. Json - PullRequest
2 голосов
/ 02 мая 2020

Я работаю с System.Text.Json в своем проекте, так как я обрабатываю большие файлы, поэтому также решил использовать его для обработки ответов GraphQL.

Из-за характера GraphQL иногда я получаю сильно вложенные ответы, которые не исправлено и не имеет смысла сопоставлять с классом. Мне обычно нужно проверить несколько свойств в ответе.

Моя проблема с JsonElement. Проверка вложенных свойств кажется очень неуклюжей, и я чувствую, что должен быть лучший способ приблизиться к этому.

Например, возьмите мой код ниже, имитирующий полученный ответ. Я просто хочу проверить, существуют ли 2 свойства (id & originalSr c) и получают ли они свое значение, но мне кажется, что я сделал этот код. Есть ли лучший / более ясный / более лаконичный способ написать это?

var raw = @"{
""data"": {
""products"": {
    ""edges"": [
        {
            ""node"": {
                ""id"": ""gid://shopify/Product/4534543543316"",
                ""featuredImage"": {
                    ""originalSrc"": ""https://cdn.shopify.com/s/files/1/0286/pic.jpg"",
                    ""id"": ""gid://shopify/ProductImage/146345345339732""
                }
            }
        }
    ]
}
}
}";

var doc = JsonSerializer.Deserialize<JsonElement>(raw);

JsonElement node = new JsonElement();

string productIdString = null;

if (doc.TryGetProperty("data", out var data))
    if (data.TryGetProperty("products", out var products))
        if (products.TryGetProperty("edges", out var edges))
            if (edges.EnumerateArray().FirstOrDefault().ValueKind != JsonValueKind.Undefined && edges.EnumerateArray().First().TryGetProperty("node", out node))
                if (node.TryGetProperty("id", out var productId))
                    productIdString = productId.GetString();

string originalSrcString = null;

if(node.ValueKind != JsonValueKind.Undefined && node.TryGetProperty("featuredImage", out var featuredImage))
    if (featuredImage.TryGetProperty("originalSrc", out var originalSrc))
        originalSrcString = originalSrc.GetString();

if (!string.IsNullOrEmpty(productIdString))
{
    //do stuff
}

if (!string.IsNullOrEmpty(originalSrcString))
{
    //do stuff
}

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

1 Ответ

1 голос
/ 02 мая 2020

Вы можете добавить пару методов расширения, которые получают доступ к дочернему значению JsonElement по имени свойства или индексу массива, возвращая значение, которое можно обнулять, если оно не найдено:

public static partial class JsonExtensions
{
    public static JsonElement? Get(this JsonElement element, string name) => 
        element.ValueKind != JsonValueKind.Null && element.ValueKind != JsonValueKind.Undefined && element.TryGetProperty(name, out var value) 
            ? value : (JsonElement?)null;

    public static JsonElement? Get(this JsonElement element, int index)
    {
        if (element.ValueKind == JsonValueKind.Null || element.ValueKind == JsonValueKind.Undefined)
            return null;
        var value = element.EnumerateArray().ElementAtOrDefault(index);
        return value.ValueKind != JsonValueKind.Undefined ? value : (JsonElement?)null;
    }
}

Теперь вызовы для доступа к вложенным значениям могут быть объединены в цепочку с помощью нуль-условного оператора ?.:

var doc = JsonSerializer.Deserialize<JsonElement>(raw);

var node = doc.Get("data")?.Get("products")?.Get("edges")?.Get(0)?.Get("node");

var productIdString = node?.Get("id")?.GetString();
var originalSrcString = node?.Get("featuredImage")?.Get("originalSrc")?.GetString();
Int64? someIntegerValue = node?.Get("Size")?.GetInt64();  // You could use "var" here also, I used Int64? to make the inferred type explicit.

Примечания:

  • Приведенные выше методы расширения вызовут исключение если входящий элемент не имеет ожидаемого типа (объект или массив или ноль / отсутствует). Вы можете ослабить проверки на ValueKind, если вы никогда не хотите исключение для неожиданного типа значения.

  • Существует открытый запрос расширения API Добавить поддержку JsonPath в JsonDocument / JsonElement # 31068 . Запросы через JSONPath , если они будут реализованы, упростят такие вещи.

Демо-скрипта здесь .

...