c# - 用于深层嵌套对象的自定义 Json 序列化程序

标签 c# .net json serialization json.net

我有几个生成的 .NET 类,它们深入三个级别,我想以特殊格式序列化它们。因此,我开始使用 Newtonsoft.Json 编写自定义 Json Serializer。

我相信这很难完全解释清楚,所以我将代码和目标一起发布在这里:https://dotnetfiddle.net/CDGcMW

本质上,有一个包含对象的初始数组,并且会有该对象的属性。困难的部分是这些属性是未知的,因此我试图创建一个自定义序列化程序。

在确定如何制作此处生成的 Json 方面的任何帮助 https://dotnetfiddle.net/CDGcMW成为被注释掉的“目标”JSON 将不胜感激。

编辑:将 dotnetfiddle 更新为一个较小的示例。原文在这里:https://dotnetfiddle.net/dprfDu

最佳答案

您的“目标”JSON 很难处理,因为 SubDataMappers 列表的处理方式不同,具体取决于子项是否具有非空 DataMapperProperty 或非空SubDataMappers 的空列表。在前一种情况下,您希望它呈现为一个对象,每个子对象包含一个属性 DataMapper;在后者中,作为一个对象数组,每个对象包含一个 DataMapper。此外,我看到您正在使用 DataMapperName 属性作为 JSON 中的键,而不是作为众所周知的属性的值。考虑到这两个限制,我认为最好的攻击计划是制作一个 JsonConverter,它在 DataMapperslist 而不是单个实例上运行.否则,转换器代码会变得非常困惑。如果可以接受,那么以下转换器应该可以满足您的需求:

public class DataMapperListConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(List<DataMapper>);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        List<DataMapper> list = (List<DataMapper>)value;
        if (list.Any(dm => dm.DataMapperProperty != null))
        {
            JObject obj = new JObject(list.Select(dm =>
            {
                JToken val;
                if (dm.DataMapperProperty != null)
                    val = JToken.FromObject(dm.DataMapperProperty, serializer);
                else 
                    val = JToken.FromObject(dm.SubDataMappers, serializer);
                return new JProperty(dm.Name, val);
            }));
            obj.WriteTo(writer);
        }
        else
        {
            serializer.Serialize(writer,
                list.Select(dm => new Dictionary<string, List<DataMapper>>
                {
                    { dm.Name, dm.SubDataMappers }
                }));
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Object)
        {
            return token.Children<JProperty>()
                 .Select(jp => 
                 {
                     DataMapper mapper = new DataMapper { Name = jp.Name };
                     JToken val = jp.Value;
                     if (val["data-type"] != null)
                         mapper.DataMapperProperty = jp.Value.ToObject<DataMapperProperty>(serializer);
                     else
                         mapper.SubDataMappers = jp.Value.ToObject<List<DataMapper>>(serializer);
                     return mapper;
                 })
                 .ToList();
        }
        else if (token.Type == JTokenType.Array)
        {
            return token.Children<JObject>()
                .SelectMany(jo => jo.Properties())
                .Select(jp => new DataMapper
                {
                    Name = jp.Name,
                    SubDataMappers = jp.Value.ToObject<List<DataMapper>>(serializer)
                })
                .ToList();
        }
        else
        {
            throw new JsonException("Unexpected token type: " + token.Type.ToString());
        }
    }
}

假设:

  • 您永远不会单独序列化单个 DataMapper;它将始终包含在列表中。
  • DataMappers 可以嵌套到任意深度。
  • DataMapper 将始终有一个非空的 Name,它在每个级别都是唯一的。
  • DataMapper 永远不会同时具有非空的 DataMapperProperty 和非空的 SubDataMappers 列表。
  • DataMapperProperty 将始终具有非空的 DataType
  • DataMapper 永远不会有 data-typeName

如果最后四个假设不成立,那么这种 JSON 格式将无法用于您尝试做的事情,您需要重新考虑。

要使用转换器,您需要将其添加到序列化程序设置中,如下所示。在序列化和反序列化时都使用这些设置。从 DataMapper 类中删除 [JsonConverter] 属性。

var settings = new JsonSerializerSettings()
{
    Converters = new List<JsonConverter> { new DataMapperListConverter() },
    Formatting = Formatting.Indented
};

这是一个往返演示:https://dotnetfiddle.net/8KycXB

关于c# - 用于深层嵌套对象的自定义 Json 序列化程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47804125/

相关文章:

c# - VS2010 Windows 应用程序中的 SQL Compact 4 EF 模型

c# - 任务还没有开始调用Task.wait可能不会等待?

c# - FluentMigrator - 它如何知道要执行哪个迁移

java - 遍历 javax.json.JsonArray

javascript - 将字符串拆分为 JSON 和非 JSON 部分

c# - 在 Blazor 页面中渲染 react 组件

c# - 语音命令,使用 .NET 的语音识别

c# - 多个应用程序域调用同一个非托管 dll

.net - linq 与 sql(或 .NET 应用程序与 SQL Server Management Studio)

php - Javascript xmlhttp - 获取 JSON 中变量的名称