web-dev-qa-db-fra.com

Désérialisation de classes abstraites/dérivées JSON.NET avec WebAPI 2

J'implémente un service Web API 2 qui utilise JSON.NET pour la sérialisation. 

Lorsque j'essaie de PUT (désserrer) les données JSON mises à jour, la classe abstraite n'est pas présente, ce qui signifie qu'elle ne savait pas quoi en faire; elle n'a donc rien fait. J'ai également essayé de rendre la classe NON abstraite et d'en hériter, puis chaque PUT est déseralisé dans la classe de base plutôt que de laisser à la classe dérivée les propriétés de la classe dérivée.

Exemple:

public class People
{
      // other attributes removed for demonstration simplicity

      public List<Person> People { get;set; }
}

public abstract class Person
{
      public string Id {get;set;}
      public string Name {get;set;}
}

public class Employee : Person 
{
      public string Badge {get;set;}
}

public class Customer : Person
{
     public string VendorCategory {get;set;}
}

avec mon api web configuré pour gérer les noms de fichiers:

public static void Register(HttpConfiguration config)
{
     config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = 
            TypeNameHandling.Objects;
}

alors je mets le JSON comme:

{
     people: [{
          name: "Larry",
          id: "123",
          badge: "12345",
          $type: "API.Models.Employee, API"
     }]
}

à la méthode web api:

public HttpResponseMessage Put(string id, [FromBody]People value)
{
      people.Update(value); // MongoDB Repository method ( not important here )
      return Request.CreateResponse(HttpStatusCode.OK);
}

mais la sortie lors de l'inspection value est toujours:

People == { People: [] }

ou si non abstrait:

People == { People: [{ Name: "Larry", Id: "123" }] }

manque la propriété héritée. Quelqu'un at-il rencontré ce problème et a trouvé quelque chose?

19
amcdnl

La fonction $type doit être le premier attribut de l'objet.

Dans l'exemple ci-dessus, j'ai:

 {
   people: [{
      name: "Larry",
      id: "123",
      badge: "12345",
      $type: "API.Models.Employee, API"
   }]
 }

après avoir déplacé $type vers le haut comme:

 {
   people: [{
      $type: "API.Models.Employee, API",
      name: "Larry",
      id: "123",
      badge: "12345"
   }]
 }

le sérialiseur était capable de dessaler l'objet au bon casting. Je dois aimer ça!

23
amcdnl

JsonSubTypes library permet de spécifier la sous-classe de la classe à utiliser pour la désérialisation en attributs via, exactement comme le fait la bibliothèque Jackson en Java. Pour être plus précis, vous pouvez:

  • Choisissez un champ et spécifiez sa valeur pour chaque sous-classe, ou
  • Spécifiez les champs présents uniquement dans certaines sous-classes.
1
N. Kudryavtsev

Je sais que ce message est ancien et que la réponse a été marquée, mais je pensais que ma solution pourrait être utile ....

Essayez d'ajouter l'attribut JsonProperty aux propriétés de votre classe abstraite.

    using JTC.Framework.Json;
    ...
    public class People
    {
        // other attributes removed for demonstration simplicity

        public List<Person> People { get;set; }
    }

    public abstract class Person
    {
          [JsonProperty()]
          public string Id {get;set;}

          [JsonProperty()]
          public string Name {get;set;}
    }

    public class Employee : Person 
    {
          public string Badge {get;set;}
    }

    public class Customer : Person
    {
         public string VendorCategory {get;set;}
    }
1
JTC

J'ai essayé votre scénario maintenant et cela fonctionne bien. Mais j'ai remarqué qu'il vous manque un , (virgule) après la propriété id dans votre entrée json.

Je l'ai compris en utilisant le contrôle de validité ModelState suivant dans mon action, qui a ensuite montré l'erreur dans la charge utile de ma demande. Cela pourrait vous être utile aussi:

if (!ModelState.IsValid)
{
    return Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState);
}
1
Kiran Challa