web-dev-qa-db-fra.com

Désérialiser JSON en 2 modèles différents

La bibliothèque Newtonsoft.JSON a-t-elle un moyen simple de désérialiser automatiquement JSON en 2 modèles/classes différents?

Par exemple, j'obtiens le JSON:

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

Et j'ai besoin de désérialiser cela sur les modèles suivants:

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

Existe-t-il un moyen simple de désérialiser ce JSON en 2 modèles sans avoir à parcourir le JSON? Peut-être que les identifiants de propriété JSON fonctionneront simplement?

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

Tout d'abord, vos modèles sont légèrement incorrects. Les propriétés de nom doivent être des chaînes, au lieu d'entiers:

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
}

Une fois que vous avez corrigé cela, vous pouvez désérialiser la chaîne JSON en deux listes de types différents. Dans ton cas, List<Guardian> et List<Patient> respectivement:

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

Si vous voulez le faire avec 1 appel, vous devez créer une classe qui correspond au JSON. Cette classe peut ensuite renvoyer les objets Guardian et Patient selon les besoins. Vous devrez également utiliser un tableau ou une liste pour le type de retour car le source JSON est un tableau.

La classe à créer:

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

Et comment l'utiliser:

var pairs = JsonConvert.DeserializeObject<Pair[]>(response.Content);

if (pairs.Any())
{
    var pair = pairs[0];
    Console.WriteLine(pair.Guardian.Name);
    Console.WriteLine(pair.Patient.Name);
}
10
Peter B

Pas en un seul appel, et il semble que les données soient un tableau, vous avez donc besoin d'un peu plus de travail.

Zip est la méthode clé ici pour joindre les deux listes d'objets distinctes:

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

Il serait beaucoup plus facile de simplement lire le JSON à la fois, c'est un seul objet.

9
Patrick Hofman

Cela ne peut pas être fait avec 1 appel avec les types que vous montrez. Vous pouvez essayer d'utiliser le générique <T> pour chaque type, vous devrez également utiliser des tableaux ou des listes pour le type de retour car source JSON est un tableau:

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

Et puis combinez les deux si vous avez besoin qu'ils soient couplés. Par exemple. si vous êtes sûr d'en avoir toujours un seul:

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

Vous pouvez créer un type pour héberger les deux sous-objets:

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

Et puis créez un convertisseur JSON pour gérer le 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();
    }
}

Et puis vous pouvez l'utiliser comme ceci:

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

et si vous le voulez comme un tableau de paires:

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

Cela ne le rendra pas plus rapide, mais je pense que vous cherchez un moyen plus simple de travailler avec le JSON.

1
John

Une autre approche consisterait à créer une classe qui correspond au format JSON, c'est-à-dire une classe avec quatre propriétés avec des noms correspondants. Ensuite, désérialisez JSON dans cette classe, puis utilisez-le dans votre code (définissez les propriétés des objets avec des valeurs de JSON, passez l'objet désérialisé au constructeur d'une autre classe).

1
Michał Turczyn

Dans vos modèles, les propriétés de nom doivent être des chaînes, au lieu d'entiers. Après l'avoir corrigé.

Vous pouvez utiliser la classe 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));
0
Gaurang Dave
    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);
    }
0
cjjgogogo