Уничтожьте JSON на 2 разных моделях

Имеет ли библиотека 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 будут работать?

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

Ответы

Ответ 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<Guardian> и List<Patient> соответственно:

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

Не в одном вызове, и кажется, что данные представляют собой массив, поэтому вам нужно немного больше работать.

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 сразу, это единственный объект.

Ответ 3

Вы хотите сделать это с помощью 1 вызова, вам нужно создать класс, соответствующий JSON. Затем этот класс может вернуть объекты Guardian и Patient мере необходимости. Также вам понадобится использовать массив или список для возвращаемого типа, поскольку исходный 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);
}

Ответ 4

Невозможно выполнить 1 вызов с отображаемыми вами типами. Вы можете попробовать использовать общий подход <T> для каждого типа, также вам нужно будет использовать массивы или списки для возвращаемого типа, поскольку исходный JSON - это массив:

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

И затем объедините их, если вам нужно их спарить. Например, если вы уверены, что у вас всегда есть только один из них:

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

Ответ 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>

параметр (поскольку вы передаете массив объектов).