Уничтожьте 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>
параметр (поскольку вы передаете массив объектов).