首页 > 解决方案 > 将 C# 列表序列化为具有内部数组的复数命名对象

问题描述

我们有一个遗留的 WCF 产品,它使用 XML 序列化属性序列化 POCO。

这是一个列表示例,具有 XmlArray/XmlArrayItem 属性。有点像 [如何为 List<Custom> 实现设置 XmlArrayItem 元素名称?

[XmlArray("Things", Order = 4), XmlArrayItem("Thing")]
public List<Thing> Things { get; set; }

这会产生 XML 响应:

<m:Things>
  <m:Thing>
  ...
  </m:Thing>
  <m:Thing>
  ...
  </m:Thing>
  <m:Thing>
  ...
  </m:Thing>
</m:Things>

并且(通过外部翻译)转换为此 Json 响应(具有内部数组(单数命名)Json 响应的复数命名对象):

"Things": {
"Thing": [
    {
        ...
    },
    {
        ...
    }
]}

现在,我们有一个没有 XML 输出的 .NET Core 2.1 应用程序 - 只有 Json。所以我想将 List 序列化到这个 Json 响应中,这样我就不会破坏任何期望这个响应的客户端。

我意识到我可以围绕 List 编写一个包装器,但我有大约 40 个这样的 List 要做,并且调用代码会变得非常模糊。事实上,如果我将选择性粘贴 > 粘贴 JSON 作为类,它看起来像:

  class Model
  {
     public ThingsWrapper Things { get; set; }
  }

    public class ThingsWrapper
    {
        public Thing[] Thing { get; set; }
    }

    public class Thing
    {
...
    }

called with var x = Things.Thing; // (yuck)

基于https://dotnetfiddle.net/vUQKV1 - 我试过这个 JsonConverter。然而,结果是这样的,这是另一种方式。我需要事物作为对象,事物作为数组。

{
  "things": [
    {
      "thing": {
        "Id": 1
      }
    },
    {
      "thing": {
        "Id": 2
      }
    }
  ]
}
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public class Program
{
    public static void Main()
    {
        var foo = new Foo();
        foo.Things = new List<Foo.Thing>{new Foo.Thing{Id = 1}, new Foo.Thing{Id = 2}};
        Console.WriteLine(JsonConvert.SerializeObject(foo, Formatting.Indented));
    }
}

class Foo
{
    [JsonProperty("things")]
    [JsonConverter(typeof(CustomArrayConverter<Thing>), "thing")]
    public List<Thing> Things
    {
        get;
        set;
    }

    public class Thing
    {
        public int Id
        {
            get;
            set;
        }
    }
}

class CustomArrayConverter<T> : JsonConverter
{
    string PropertyName
    {
        get;
        set;
    }

    public CustomArrayConverter(string propertyName)
    {
        PropertyName = propertyName;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JArray array = new JArray(JArray.Load(reader).Select(jo => jo[PropertyName]));
        return array.ToObject(objectType, serializer);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        IEnumerable<T> items = (IEnumerable<T>)value;
        JArray array = new JArray(items.Select(i => new JObject(new JProperty(PropertyName, JToken.FromObject(i, serializer)))));
        array.WriteTo(writer);
    }

    public override bool CanConvert(Type objectType)
    {
        // CanConvert is not called when the [JsonConverter] attribute is used
        return false;
    }
}

标签: c#jsonxmlserializationasp.net-core-2.1

解决方案


感谢@Brian在 Json.NET 中命名数组元素? https://dotnetfiddle.net/vUQKV1,这为我指明了正确的方向。

class CustomArrayConverter<T> : JsonConverter
    {
        string PropertyName { get; set; }

        public CustomArrayConverter(string propertyName)
        {
            PropertyName = propertyName;
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JArray array = new JArray(JArray.Load(reader).Select(jo => jo[PropertyName]));
            return array.ToObject(objectType, serializer);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            IEnumerable<T> items = (IEnumerable<T>)value;
            JObject jObject = new JObject(new JProperty(PropertyName, new JArray(items.Select(i => JToken.FromObject(i, serializer)))));
            jObject.WriteTo(writer);
        }

        public override bool CanConvert(Type objectType)
        {
            // CanConvert is not called when the [JsonConverter] attribute is used
            return false;
        }
    }

推荐阅读