65.9K
CodeProject 正在变化。 阅读更多。
Home

通过 dynamic 关键字轻松访问 Microsoft JSON DOM

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2024 年 4 月 4 日

CPOL

1分钟阅读

viewsIcon

5245

如果您正在使用 JSON 和 C# 构建模板或其他复杂结构,这可能是一个不错的解决方案。

引言

我不喜欢 JSON 和静态类型语言之间的极性不匹配。例如,在 Javascript 中,您可以像访问原生对象一样访问 JSON 数据——因为在 Javascript 中它们本质上就是原生对象。在使用 C# 时,您通常需要使用某种 DOM 或解析器来导航和访问 JSON 信息集中的数据。这会降低可读性并使您的代码变得笨拙。

C# 并非绝对需要这种限制,因为它具有 dynamic 关键字,可用于后期绑定。不幸的是,这样做会失去静态类型检查。因此,我将 JSON 对象设置为只读,以防止随意修改。这些的主要用途是在类似 ASP.NET 页面的场景中,正如我将演示的那样。

使用代码

首先,将以下代码复制到您项目中的一个新文件中

using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Text;
using System.Text.Json;

class DynamicJsonElement : DynamicObject, IEnumerable<object?>
{
    JsonElement _inner;
    private static object? ElemToObj(JsonElement elem)
    {
        object? result = null;
        switch (elem.ValueKind)
        {
            case JsonValueKind.String:
                result = elem.GetString()!;
                break;
            case JsonValueKind.Number:
                result = elem.GetDouble();
                break;
            case JsonValueKind.Null:
                result = null;
                break;
            case JsonValueKind.True:
                result = true; break;
            case JsonValueKind.False:
                result = false; break;
            case JsonValueKind.Object:
            case JsonValueKind.Array:
                result = new DynamicJsonElement(elem);
                break;
            default:
                result = null;
                break;
        }
        return result;
    }
    public DynamicJsonElement(JsonElement json)
    {
        _inner = json;

    }
    public int Count
    {
        get
        {
            if (_inner.ValueKind == JsonValueKind.Array)
            {
                return _inner.GetArrayLength();
            }
            throw new NotSupportedException();
        }
    }
    public object? this[int index]
    {
        get
        {
            if (_inner.ValueKind == JsonValueKind.Array)
            {
                return ElemToObj(_inner[index]);
            }
            throw new NotSupportedException();
        }
    }
    public override bool TryGetMember(
        GetMemberBinder binder, out object? result)
    {
        JsonElement elem;
        if (_inner.TryGetProperty(binder.Name, out elem))
        {
            result = ElemToObj(elem);
            return true;
        }
        result = null;
        return false;
    }

    public override bool TrySetMember(
        SetMemberBinder binder, object? value)
    {
        return false;
    }

    public IEnumerator<object?> GetEnumerator()
    {
        if (_inner.ValueKind == JsonValueKind.Array)
        {
            foreach (var elem in _inner.EnumerateArray())
            {
                yield return ElemToObj(elem);
            }
        }
        else
        {
            throw new NotSupportedException();
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

现在,每当您收到 JsonElement 时,都可以使用 new DynamicJsonElement(myJson) 包装它。 

dynamic root = new DynamicJsonElement(JsonDocument.Parse(stream).RootElement);

从那里,root 是文档的根,您可以像在 Javascript 中一样访问字段和值

Console.WriteLine("{0} {1}",root.first_name,root.last_name);
Console.WriteLine("City: {0}",root.addresses[0].city);

请记住,您需要将获取到的值进行类型转换,因为它从其属性和访问器返回 object。 上面我们不需要进行转换,因为 WriteLine() 可以接受 object 类型。

这在 T4 和 ASP.NET 模板中特别有用

<span>Age: <%=root.age%></span>

结果可读性大大提高。

一个需要注意的点是,在使用 foreach 循环时,必须将迭代的值重新赋值给一个动态对象,如所示

foreach(var item in root.addresses) {
    dynamic address = item;
    // now you can access address
}

历史

  • 2024 年 4 月 4 日 - 初始提交
© . All rights reserved.