【问题标题】:How to convert json array to collection with System.Text.Json;如何使用 System.Text.Json 将 json 数组转换为集合;
【发布时间】:2021-03-23 02:30:45
【问题描述】:

我有一个User 类,它有一个ISet<Role> 类型的属性Roles,其中Role 是一个enum 类型。在 JSON 中,我得到了该属性的数组(例如,"Roles":["Admin","User"])。如何编写自定义转换器将此数组转换为ISet 集合?

我试过写这个类:

class CustomStringToRoleConverter : JsonConverter<ISet<User.Role>> {
    public override ISet<User.Role> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
        return null;
    }

    public override void Write(Utf8JsonWriter writer, ISet<User.Role> roles, JsonSerializerOptions options) {
        StringBuilder builder = new StringBuilder();
        builder.Append(string.Join(",", roles.Select(role => role.ToString()).ToArray()));
        writer.WriteStartArray();
        writer.WriteStringValue(builder.ToString());
        writer.WriteEndArray();
    }
}

但我不知道在Read 方法中做什么。如何从Utf8JsonReader 获取数组?有一个GetString 方法,但是当我调用它时它会抛出异常:

InvalidOperationException:无法获取令牌类型的值 'StartArray' 作为字符串。

我正在使用System.TextSystem.Text.Json

【问题讨论】:

  • 我会创建数据结构并创建自己的解析函数。

标签: c# json system.text.json


【解决方案1】:

JSON "Roles":["Admin","User"] 属性是一个字符串值数组,而不是包含逗号分隔值的单个字符串值。因此,编写自定义转换器的最简单方法如下:

class CustomStringToRoleConverter : JsonConverter<ISet<User.Role>> {
    public override ISet<User.Role> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
        JsonSerializer.Deserialize<List<string>>(ref reader, options)
            ?.Select(s => Enum.Parse<User.Role>(s))
            ?.ToHashSet(); // Or use a SortedSet if you prefer

    public override void Write(Utf8JsonWriter writer, ISet<User.Role> roles, JsonSerializerOptions options) =>
        JsonSerializer.Serialize(writer, roles.Select(r => r.ToString()), options);
}

转换器只是对字符串集合进行序列化和反序列化,然后将项目解析或格式化为 Role 枚举作为后处理或预处理步骤。 (作为比较,您现有的 Write() 方法将生成 "Roles": ["Admin,User"] 与问题开头段落中显示的 JSON 不匹配。)

小提琴样本 #1 here.

如果您非常关心性能并且更喜欢不反序列化为中间 List&lt;string&gt; 的更“手动”方法,您可以这样做:

class CustomStringToRoleConverter : JsonConverter<ISet<User.Role>> {
    public override ISet<User.Role> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.Null)
            return null; // Or throw an exception if you don't want to allow null
        else if (reader.TokenType != JsonTokenType.StartArray)
            throw new JsonException();
        var set = new HashSet<User.Role>();
        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.EndArray)
                return set;
            else if (reader.TokenType == JsonTokenType.String)
                set.Add(Enum.Parse<User.Role>(reader.GetString()));
            else
            {
                //reader.Skip();
                throw new JsonException(); // Unexpected token;
            }
        }
        throw new JsonException(); // Truncated file;
    }

    public override void Write(Utf8JsonWriter writer, ISet<User.Role> roles, JsonSerializerOptions options)
    {
        writer.WriteStartArray();
        foreach (var value in roles)
            writer.WriteStringValue(value.ToString());
        writer.WriteEndArray();
    }
}

小提琴样本 #2 here.

或者,您可以简单地将JsonStringEnumConverter 添加到JsonSerializerOptions.Converters 以将所有枚举序列化为字符串。

【讨论】:

    猜你喜欢
    • 2018-02-26
    • 1970-01-01
    • 2013-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多