c# - deserializeanonymoustype - Deserializar JSON a 2 modelos diferentes
deserializeanonymoustype c# (9)
¿La biblioteca de Newtonsoft.JSON tiene una manera simple de deserializar automáticamente JSON en 2 modelos / clases diferentes?
Por ejemplo, obtengo el JSON:
[{
"guardian_id": "1453",
"guardian_name": "Foo Bar",
"patient_id": "938",
"patient_name": "Foo Bar",
}]
Y necesito deserializar esto para los siguientes modelos:
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; }
}
¿Hay una manera simple de deserializar este JSON en 2 modelos sin tener que iterar sobre el JSON? ¿Tal vez los ID de propiedad JSON funcionarán?
Pair<Guardian, Patient> pair = JsonConvert.DeserializeObject(response.Content);
Dado que ambos objetos son iguales, ¿no tendría más sentido tener una estructura ID / Nombre de una única clase base? Si necesita enviar todos los datos al mismo tiempo, puede reestructurar sus datos y usar un patrón de objetos de transferencia de datos. El objeto JSON se convertiría
[{
"guardian": {
"id": "1453",
"name": "Foo Bar"
},
"patient": {
"id" : "938",
"name": "Foo Bar"
}
}]
Y sus objetos de datos correspondientes serían:
public class Record {
public int id { get; set; } // or string. I''m not sure which would be more appropriate
public string name { get; set;}
}
y
public class RecordDto {
public Record guardian { get; set; }
public Record patient { get; set; }
}
Y tu API recibiría un
List<RecordDto>
parámetro (ya que está pasando una matriz de objetos).
En primer lugar, sus modelos son ligeramente incorrectos. Las propiedades del nombre deben ser cadenas, en lugar de enteros:
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
}
Una vez que haya corregido eso, puede deserializar la cadena JSON en dos listas de diferentes tipos. En su caso, List<Guardian>
y List<Patient>
respectivamente:
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);
En sus modelos, las propiedades del nombre deben ser cadenas, en lugar de números enteros. Después de corregirlo.
Puedes usar la clase 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));
No en una llamada, y parece que los datos son una matriz, por lo que necesita un poco más de trabajo.
Zip
es el método clave aquí para unir las dos listas de objetos por separado:
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();
Sería mucho más fácil simplemente leer el JSON de una vez, es un solo objeto.
No se puede hacer con 1 llamada con los tipos que muestra. Puede intentar usar el enfoque genérico <T>
para cada tipo, también necesitará usar matrices o listas para el tipo de retorno porque el JSON de origen es una matriz:
var guardians = JsonConvert.DeserializeObject<Guardian[]>(response.Content);
var patients = JsonConvert.DeserializeObject<Patient[]>(response.Content);
Y luego combine los dos si necesita emparejarlos. Por ejemplo, si está seguro de que siempre tiene solo uno de cada uno:
var pair = new Pair(guardians[0], patients[0]);
Otro enfoque sería crear una clase que coincida con el formato JSON, es decir, una clase con cuatro propiedades con los nombres correspondientes. Luego, deserialice JSON en esa clase y luego úselo en su código (establezca las propiedades de los objetos con valores de JSON, pase el objeto deserializado al constructor de otra clase).
Podría hacer un tipo para albergar los dos subobjetos:
[JsonConverter(typeof(GuardianPatientConverter))]
class GuardianPatient
{
public Guardian Guardian { get; set; }
public Patient Patient { get; set; }
}
Y luego crea un convertidor JSON para manejar el 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();
}
}
Y luego puedes usarlo así:
var json = "[{/"guardian_id/":/"1453/",/"guardian_name/":/"Foo Bar/",/"patient_id/":/"938/",/"patient_name/":/"Foo Bar/",}]";
var objects = JsonConvert.DeserializeObject<IEnumerable<GuardianPatient>>(json);
y si lo quieres como una matriz de pares:
var objects = JsonConvert.DeserializeObject<IEnumerable<GuardianPatient>>(json)
.Select(o => new Pair(o.Guardian, o.Patient))
.ToArray();
Esto no lo hará más rápido, pero sospecho que está buscando una forma más fácil de trabajar con JSON.
Si desea hacerlo con 1 llamada, debe crear una clase que coincida con el JSON. Esa clase puede devolver objetos Guardian
y Patient
según sea necesario. También necesitará usar una matriz o lista para el tipo de devolución porque la fuente JSON es una matriz.
La clase para crear:
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; }
}
}
Y cómo usarlo:
var pairs = JsonConvert.DeserializeObject<Pair[]>(response.Content);
if (pairs.Any())
{
var pair = pairs[0];
Console.WriteLine(pair.Guardian.Name);
Console.WriteLine(pair.Patient.Name);
}
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);
}