【问题标题】:Deserialize JSON to 2 different models将 JSON 反序列化为 2 个不同的模型
【发布时间】:2018-04-04 08:06:44
【问题描述】:

Newtonsoft.JSON 库是否有一种简单的方法可以自动将 JSON 反序列化为 2 个不同的模型/类?

例如我得到 JSON:

[{
  "guardian_id": "1453",
  "guardian_name": "Foo Bar",
  "patient_id": "938",
  "patient_name": "Foo Bar",
}]

我需要将其反序列化为以下模型:

class Guardian {

  [JsonProperty(PropertyName = "guardian_id")]
  public int ID { get; set; }

  [JsonProperty(PropertyName = "guardian_name")]
  public int Name { get; set; }
}


class Patient {

  [JsonProperty(PropertyName = "patient_id")]
  public int ID { get; set; }

  [JsonProperty(PropertyName = "patient_name")]
  public int Name { get; set; }
}

有没有一种简单的方法可以将此 JSON 反序列化为 2 个模型,而无需遍历 JSON?也许 JSON 属性 ID 可以正常工作?

Pair<Guardian, Patient> pair = JsonConvert.DeserializeObject(response.Content);

【问题讨论】:

  • 不知道大家喜不喜欢,不过我用的挺多的:json2csharp.com
  • @Thomas 编辑 |选择性粘贴
  • 重组 JSON 是一种选择吗?

标签: c# json json.net


【解决方案1】:

首先,您的模型有点不正确。名称属性需要是字符串,而不是整数:

class Guardian
{

    [JsonProperty(PropertyName = "guardian_id")]
    public int ID { get; set; }

    [JsonProperty(PropertyName = "guardian_name")]
    public string Name { get; set; }            // <-- This
}


class Patient
{

    [JsonProperty(PropertyName = "patient_id")]
    public int ID { get; set; }

    [JsonProperty(PropertyName = "patient_name")]
    public string Name { get; set; }            // <-- This
}

修正该问题后,您可以将 JSON 字符串反序列化为两个不同类型的列表。在你的情况下,List&lt;Guardian&gt;List&lt;Patient&gt; 分别是:

string json = @"[{'guardian_id':'1453','guardian_name':'Foo Bar','patient_id':'938','patient_name':'Foo Bar'}]";
var guardians = JsonConvert.DeserializeObject<List<Guardian>>(json);
var patients = JsonConvert.DeserializeObject<List<Patient>>(json);

【讨论】:

    【解决方案2】:

    如果您想通过 1 次调用来完成,您需要创建一个与 JSON 匹配的类。然后,该类可以根据需要返回 GuardianPatient 对象。此外,您还需要使用数组或列表作为返回类型,因为 源 JSON 是一个数组。

    要创建的类:

    public class Pair
    {
        public Pair()
        {
            Guardian = new Guardian();
            Patient = new Patient();
        }
    
        [JsonIgnore]
        public Guardian Guardian { get; set; }
    
        [JsonIgnore]
        public Patient Patient { get; set; }
    
        [JsonProperty(PropertyName = "guardian_id")]
        public int GuardianID
        {
            get { return Guardian.ID; }
            set { Guardian.ID = value; }
        }
    
        [JsonProperty(PropertyName = "guardian_name")]
        public string GuardianName
        {
            get { return Guardian.Name; }
            set { Guardian.Name = value; }
        }
    
        [JsonProperty(PropertyName = "patient_id")]
        public int PatientID
        {
            get { return Patient.ID; }
            set { Patient.ID = value; }
        }
    
        [JsonProperty(PropertyName = "patient_name")]
        public string PatientName
        {
            get { return Patient.Name; }
            set { Patient.Name = value; }
        }
    }
    

    以及如何使用它:

    var pairs = JsonConvert.DeserializeObject<Pair[]>(response.Content);
    
    if (pairs.Any())
    {
        var pair = pairs[0];
        Console.WriteLine(pair.Guardian.Name);
        Console.WriteLine(pair.Patient.Name);
    }
    

    【讨论】:

      【解决方案3】:

      不是一次调用,而且数据似乎是一个数组,所以你需要多做一些工作。

      Zip 是这里加入两个独立对象列表的关键方法:

      Guardian[] guardians = JsonConvert.DeserializeObject<Guardian[]>(response.Content);
      Patient[] patients = JsonConvert.DeserializeObject<Patient[]>(response.Content);
      
      var combined = guardians.Zip(patients, (g, p) => Tuple.Create(g, p)).ToList();
      

      一次读取 JSON 会容易得多,它是一个对象。

      【讨论】:

        【解决方案4】:

        无法通过 1 次调用您显示的类型来完成。您可以尝试对每种类型使用通用 &lt;T&gt; 方法,还需要使用数组或列表作为返回类型,因为 源 JSON 是一个数组:

        var guardians = JsonConvert.DeserializeObject<Guardian[]>(response.Content);
        var patients = JsonConvert.DeserializeObject<Patient[]>(response.Content);
        

        如果需要配对,然后将两者结合起来。例如。如果你确定你总是只有一个:

        var pair = new Pair(guardians[0], patients[0]);
        

        【讨论】:

        • 我添加了第二个答案,以展示如何使用 1 Deserialize-call 和旨在实现这一目标的类来做到这一点。见下或上。
        【解决方案5】:

        另一种方法是创建与 JSON 格式匹配的类,即具有四个具有相应名称的属性的类。然后,将 JSON 反序列化为该类,然后在您的代码中使用它(使用来自 JSON 的值设置对象的属性,将反序列化的对象传递给另一个类的构造函数)。

        【讨论】:

          【解决方案6】:

          您可以创建一个类型来容纳两个子对象:

          [JsonConverter(typeof(GuardianPatientConverter))]
          class GuardianPatient
          {
              public Guardian Guardian { get; set; }
              public Patient Patient { get; set; }
          }
          

          然后创建一个 JSON 转换器来处理 JSON:

          class GuardianPatientConverter : JsonConverter
          {
              public override bool CanRead
              {
                  get { return true; }
              }
          
              public override bool CanWrite
              {
                  get { return false; }
              }
          
              public override bool CanConvert(Type objectType)
              {
                  return typeof(GuardianPatient) == objectType;
              }
          
              public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
              {
                  if (reader.TokenType == JsonToken.Null)
                  {
                      return null;
                  }
          
                  var jObject = JObject.Load(reader);
                  var guardian = new Guardian();
                  var patient = new Patient();
                  serializer.Populate(jObject.CreateReader(), guardian);
                  serializer.Populate(jObject.CreateReader(), patient);
                  return new GuardianPatient()
                  {
                      Guardian = guardian,
                      Patient = patient
                  };
              }
          
              public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
              {
                  throw new NotImplementedException();
              }
          }
          

          然后你可以像这样使用它:

          var json = "[{\"guardian_id\":\"1453\",\"guardian_name\":\"Foo Bar\",\"patient_id\":\"938\",\"patient_name\":\"Foo Bar\",}]";
          var objects = JsonConvert.DeserializeObject<IEnumerable<GuardianPatient>>(json);
          

          如果你想要它作为一个数组对:

          var objects = JsonConvert.DeserializeObject<IEnumerable<GuardianPatient>>(json)
              .Select(o => new Pair(o.Guardian, o.Patient))
              .ToArray();
          

          这不会让它变得更快,但我怀疑您正在寻找一种更简单的方式来处理 JSON。

          【讨论】:

            【解决方案7】:

            在您的模型中,名称属性必须是字符串,而不是整数。改正后。

            你可以使用Tuple

            string json = @"[{'guardian_id':'1453','guardian_name':'Foo Bar','patient_id':'938','patient_name':'Foo Bar'}]";
            
            var combination = new Tuple<List<Guardian>, List<Patient>>(JsonConvert.DeserializeObject<List<Guardian>>(json), JsonConvert.DeserializeObject<List<Patient>>(json));
            

            【讨论】:

              【解决方案8】:
                  static void Main(string[] args)
                  {
                      string json = JsonConvert.SerializeObject(new[] 
                      {
                          new
                          {
                              guardian_id = "1453",
                              guardian_name = "Foo Bar",
                              patient_id = "938",
                              patient_name = "Bar Foo",
                          }
                      });
              
                      Guardian[] guardians = JsonConvert.DeserializeObject<Guardian[]>(json);
                      Patient[] patients = JsonConvert.DeserializeObject<Patient[]>(json);
                  }
              

              【讨论】:

                【解决方案9】:

                由于您的两个对象都是相同的,因此仅具有单个基类的 ID/Name 结构不是更有意义吗?如果您需要同时发送所有数据,您可以重组数据并使用数据传输对象模式。 JSON 对象将变为

                [{
                  "guardian": {
                    "id": "1453",
                    "name": "Foo Bar"
                  },
                  "patient": {
                    "id" : "938",
                    "name": "Foo Bar"
                  }
                }]
                

                您对应的数据对象将是:

                public class Record {
                  public int id { get; set; } // or string.  I'm not sure which would be more appropriate
                  public string name { get; set;}
                }
                

                public class RecordDto {
                  public Record guardian { get; set; }
                  public Record patient { get; set; }
                }
                

                你的 API 会收到一个

                List<RecordDto>
                

                参数(因为您要传递一个对象数组)。

                【讨论】:

                • 假设您不能只更改 JSON 模型
                猜你喜欢
                • 2017-04-13
                • 1970-01-01
                • 2020-09-29
                • 1970-01-01
                • 1970-01-01
                • 2019-01-27
                • 2019-06-05
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多