如何使用 System.Text.Json 将 json 数组转换为集合;

How to convert json array to collection with System.Text.Json;

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

我试过写这个class:

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: Cannot get the value of a token type 'StartArray' as a string.

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

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 枚举作为 post- 或预处理步骤。 (为了比较,您现有的 Write() 方法会生成 "Roles": ["Admin,User"],它与问题开头段落中显示的 JSON 不匹配。)

示例 fiddle #1 here.

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

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

示例 fiddle #2 here.

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