Десериализовать 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);

Не знаю, нравится ли вам это, но я много использовал: json2csharp.com

online Thomas 04.04.2018 11:50

@Thomas Edit | Специальная вставка

Llama 04.04.2018 17:39

Можно ли реорганизовать JSON?

jpmc26 04.04.2018 17:52
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
32
3
6 200
9

Ответы 9

Это невозможно сделать за 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]);

Я добавил второй ответ, чтобы показать, как это сделать с помощью 1 Deserialize-call и класса, предназначенного для этого. См. Ниже или выше.

Peter B 04.04.2018 11:26

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

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

Во-первых, ваши модели немного неверны. Свойства имени должны быть строками, а не целыми числами:

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);

Еще один подход заключается в создании класса, соответствующего формату JSON, то есть класса с четырьмя свойствами с соответствующими именами. Затем десериализуйте JSON в этот класс, а затем используйте его в своем коде (установите свойства объектов со значениями из JSON, передайте десериализованный объект конструктору другого класса).

В ваших моделях свойства имени должны быть строками, а не целыми числами. После исправления.

Вы можете использовать класс 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));

Вы можете создать тип для размещения двух подобъектов:

[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.

    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);
    }

Если вы хотите сделать это с помощью 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);
}

Поскольку оба ваших объекта одинаковы, не имеет ли смысла просто иметь структуру 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.

Pureferret 04.04.2018 15:55

Другие вопросы по теме