实施JsonConverter后,嵌套节点的格式不正确 [英] Nested nodes are not formatted correctly after implementing JsonConverter

查看:50
本文介绍了实施JsonConverter后,嵌套节点的格式不正确的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个要序列化为JSON的对象列表,如下所示,因为此列表中存在复杂的类型.我想将此复杂类型更改为Key/Value对,其中每个Key是该类型中属性的名称,每个Value是该属性的对应值.我尝试了多种解决方案,但没有一个对我有用.

I have a list of objects that I want to serialize as JSON like below, since there is a complex type in this list. I want to change this complex type to Key/Value pairs where each Key is the name of a property in the type, and each Value is the corresponding value of that property. I've tried multiple solutions but none of them worked for me.

这是对象结构

public class Metadata
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Data
{
    // If I change the type of Metadata to IList<IDictionary<string, object>>
    // custom converter won't catch it at all when I pass it to its constructor
    //public IList<IDictionary<string, object>> Metadata { get; set; }
    public IList<Metadata> Metadata { get; set; }
    public int Length { get; set; }
    public string Type { get; set; }
}

这是我想要的输出,在IList<Metadata>

Here is my desired output with two entries in IList<Metadata>

{
    "Metadata": [{
            "Key": "FirstName",
            "Value": "ABC"
        },
        {
            "Key": "LastName",
            "Value": "XYZ"
        },
        {
            "Key": "FirstName",
            "Value": "DEF"
        },
        {
            "Key": "LastName",
            "Value": "MNL"
        }
    ],
    "Length": 25,
    "Type": "application/mp3"
}

我知道JsonSerializer本身不会更改对象的外观,因此我尝试通过实现自定义JsonConverter来更改它:

I know that JsonSerializer does not change the face of the object by itself, so I tried to change it by implementing a custom JsonConverter:

public class KeyValue
{
    public string Key { get; set; }
    public string Value { get; set; }
}

class CustomMetadataConverter : JsonConverter
{
    private readonly Type[] _types;

    public CustomMetadataConverter(params Type[] types)
    {
        _types = types;
    }

    public override bool CanConvert(Type objectType)
    {
        return _types.Any(t => t == objectType);
    }

    // I've removed ReadJson and CanRead here to keep the question clear

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JToken token = JToken.FromObject(value);

        if (token.Type != JTokenType.Object)
            token.WriteTo(writer);
        else
        {
            JObject jsonObject = (JObject)token;

            IList<KeyValue> properties = jsonObject.Properties()
                .Select(p => new KeyValue { Key = p.Name, Value = p.Value.ToString() }).ToList();

            // If I change the IList<KeyValue> to anonymous array, output would be the same
            //var properties = jsonObject.Properties().Select(p => new { Key = p.Name, Value = p.Value.ToString() }).ToArray();

            jsonObject.RemoveAll();

            jsonObject.Add(new JProperty("Metadata", JToken.FromObject(properties)));
            jsonObject.WriteTo(writer);
        }
    }
}

这就是我的称呼:

var serializedObject = JsonConvert.SerializeObject(listOfData, Formatting.Indented, new CustomMetadataConverter(typeof(Metadata)));

我尝试了此解决方案,将其移至根目录,但是自定义转换器的输出被父文件包装更换它.我知道这是因为自定义转换器仅读取Metadata的子级,但是如果将CustomMetadataConverter(typeof(Metadata))更改为CustomMetadataConverter(typeof(Data)),它将把整个查询转换为Key/Value对.那不是我想要的.

I tried this solution to move it to root, but the output of the custom converter is wrapped with the parent instead of replacing it. I know it's because the custom converter only reads children of Metadata but if I change CustomMetadataConverter(typeof(Metadata)) to CustomMetadataConverter(typeof(Data)) it converts the whole query to Key/Value pairs. And that's not what I want.

这是实现自定义转换器后的输出

{
  "Metadata": [
    {
      "Metadata": [
        {
          "Key": "FirstName",
          "Value": "ABC"
        },
        {
          "Key": "LastName",
          "Value": "XYZ"
        }
      ]
    }
  ],
  "Length": 25,
  "Type": "application/mp3"
}

推荐答案

如果列表中有两个或多个Metadata项,并且将它们序列化为平面数组中的键-值对对象,如您在您的描述中所述问题,那么如果以后需要反序列化JSON,将很难分辨出哪些键值对属于在一起.最好改用像这样的二维数组结构:

If you have two or more Metadata items in the list, and you serialized them to key-value pair objects in a flat array like you described in your question, then it would be difficult to tell which key-value pairs belong together if you need to deserialize the JSON later. It would be better to use a two-dimensional array structure like this instead:

{
  "Metadata": [
    [
      {
        "Key": "FirstName",
        "Value": "ABC"
      },
      {
        "Key": "LastName",
        "Value": "XYZ"
      }
    ],
    [
      {
        "Key": "FirstName",
        "Value": "DEF"
      },
      {
        "Key": "LastName",
        "Value": "MNL"
      }
    ]
  ],
  "Length": 25,
  "Type": "application/mp3"
}

这是将执行此操作的转换器:

Here is a converter which will do that:

class ObjectToKvpArrayConverter<T> : JsonConverter where T : class
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(T);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JArray array = new JArray(
            JObject.FromObject(value)
                   .Properties()
                   .Select(jp => 
                       new JObject(
                           new JProperty("Key", jp.Name),
                           new JProperty("Value", jp.Value)
                       )
                   )
        );
        array.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject obj = new JObject(
            JArray.Load(reader)
                  .Children<JObject>()
                  .Select(jo => new JProperty((string)jo["Key"], jo["Value"]))
        );
        T result = Activator.CreateInstance<T>();
        serializer.Populate(obj.CreateReader(), result);
        return result;
    }
}

您可以像这样使用转换器:

You can use the converter like this:

var json = JsonConvert.SerializeObject(data, Formatting.Indented, new ObjectToKvpArrayConverter<Metadata>());

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

这篇关于实施JsonConverter后,嵌套节点的格式不正确的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆