将 C# 列表序列化为具有内部数组的复数命名对象

Posted

技术标签:

【中文标题】将 C# 列表序列化为具有内部数组的复数命名对象【英文标题】:Serialize C# List into plural named object with an inner array 【发布时间】:2020-04-09 16:01:38 【问题描述】:

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

这是一个 List 示例,具有 XmlArray/XmlArrayItem 属性。 有点像[How do I Set XmlArrayItem Element name for a List<Custom> implementation?

[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.ThingId = 1, new Foo.ThingId = 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;
    

【问题讨论】:

您可以使用自定义的JsonConverter -- 尽管CustomCreationConverter 不起作用。问题是内部属性 "Thing" 的名称对于每个列表属性都会有所不同,对吧?它目前由[XmlArrayItem(string name)] 控制,但传递给构造函数的名称因属性而异,您打算如何保留该名称?你会继续用XmlArrayItem 属性装饰你的房产吗? Thing 内部属性只会设置一次(它是一个 Thing 数组)。请参见上面的代码示例 2。这不是我选择输出 json 的方式。但这正是我必须使用的。如何使用 JsonConverter 的任何示例? 【参考方案1】:

感谢@Brian Name array elements in 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;
        
    

【讨论】:

谢谢,我做到了

以上是关于将 C# 列表序列化为具有内部数组的复数命名对象的主要内容,如果未能解决你的问题,请参考以下文章

Jackson - 将内部对象列表反序列化为更高级别的列表

无法将 JSON 数组反序列化为 C# 对象 [关闭]

JavaScriptSerializer类 对象序列化为JSON,JSON反序列化为对象

C#中json的命名空间是哪个,还要添加啥引用?

C# JSON 将文件反序列化为对象列表失败,并将字符串转换为集合错误

将 JSON 反序列化为 C# 对象以在网格中将嵌套数组显示为字符串