【问题标题】:Deserialize JSON dictionary-like to object with properties将 JSON 类字典反序列化为具有属性的对象
【发布时间】:2018-07-18 12:19:35
【问题描述】:

我被一个返回以下 JSON 的糟糕 API 卡住了:

{
   "root":[
      {
         "row":[
            {
               "name":"Foo",
               "value":"Some value"
            },
            {
               "name":"Bar",
               "value":"Some other value"
            }, (...)
         ]
      },
      {
         "row":[
            {
               "name":"Foo",
               "value":"Lorem"
            },
            {
               "name":"Bar",
               "value":"Ipsum"
            }, (...)
         ]
      }, (...)
   ]
}

我想使用 Newtonsoft.Json 将其反序列化为 C# 对象列表,以便名称与对象属性匹配,值将是属性值。该类将如下所示:

class Row
{
    public string Foo { get; set; }
    public string Bar { get; set; }
    (...)
}

有什么想法吗?

【问题讨论】:

  • 您可以先将其反序列化为字典,然后再解析。
  • 作为一个快速修复,您可以反序列化为dynamic,然后编写一些代码来处理它,找到row 的每个实例,从中获取每个数组项,将数据复制到一个实例中您的 Row 课程并将其添加到您的最终列表中。我真的不知道如何仅在单个反序列化操作中直接获得List<Row> - 反序列化是将数据映射到直接匹配的对象结构的过程。因此,如果结构不是您想要的,那么您总是需要编写一些代码来将反序列化的数据转换为您真正想要的结构。
  • 使用自定义JsonConverter

标签: c# json json.net


【解决方案1】:

您可以使用自定义 JsonConverter 来处理此问题。下面是一个需要一些空检查等的简单示例,但这得到了想法:

    public class RowConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(Row[]);
        }

        public override bool CanWrite => false;

        public override bool CanRead => true;

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {

            if (reader.TokenType == JsonToken.Null)
            {
                return string.Empty;
            }
            else if (reader.TokenType == JsonToken.String)
            {
                return serializer.Deserialize(reader, objectType);
            }
            else
            {
                JObject obj = JObject.Load(reader);
                var root = obj["root"];
                if (root != null)
                {
                    var rows = new List<Row>();
                    foreach (var item in root)
                    {
                        var row = item["row"];
                        var newRow = new Row();
                        foreach(var field in row)
                        {
                            // better use reflection here to convert name-value to property setter
                            if (field.Value<string>("name") == "Foo")
                            {
                                newRow.Foo = field["value"].Value<string>();
                            }

                            if (field.Value<string>("name") == "Bar")
                            {
                                newRow.Bar = field["value"].Value<string>();
                            }
                        }

                        rows.Add(newRow);
                    }

                    return rows.ToArray();
                }
                return null;
            }
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }

然后在你的 Row 类上:

[JsonConverter(typeof(RowConverter))]
public class Row
{
    public string Foo { get; set; }

    public string Bar { get; set; }
}

如果你需要将它包含在你的 MVC 中,在 Startup.cs (asp.net core) 中:

services
    .AddMvc()
    .AddJsonOptions(options => { options.SerializerSettings.Converters.Add(new RowConverter()); });

【讨论】:

  • 除了行 JObject obj = JObject.Load(reader); 之外效果很好。返回根的 n 元素,而不是整个集合,所以我不得不摆脱一个 foreach。非常感谢您的精彩回答!
【解决方案2】:

作为一个选项,您可以将其转换为单个“行”属性的字典:

var values = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);

创建动态对象后:

private static dynamic DictionaryToObject(IDictionary<String, Object> dictionary)  
{  
    var expandoObj = new ExpandoObject();  
    var expandoObjCollection = (ICollection<KeyValuePair<String, Object>>)expandoObj;  

    foreach (var keyValuePair in dictionary)  
    {  
        expandoObjCollection.Add(keyValuePair);  
    }  
    dynamic eoDynamic = expandoObj;  
    return eoDynamic;  
}  

private static T DictionaryToObject<T>(IDictionary<String, Object> dictionary) where T : class  
{  
    return DictionaryToObject(dictionary) as T;  
}  

【讨论】:

    【解决方案3】:

    你可以像下面这样创建类,然后使用 newtosnsoft

    public class root
    {
        public List<row> row {get;set;}
    
    }
    
    public class row
    {
       public string name {get;set;}
       public string value {get;set;}
    }
    

    【讨论】:

    • 这正是我想要避免的。我希望json中的名称成为对象属性,json值成为该属性的值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-27
    相关资源
    最近更新 更多