Pages

Thursday, March 20, 2014

Usar herança nos parâmetros dos métodos na WebApi

Num dos desenvolvimentos em que estou envolvido surgiu a necessidade de usar herança num dos parâmetros de entrada dum método na WebApi. O que eu quero dizer por usar herança é ter na assinatura do método um tipo base e depois quando os vários componentes chamam o método passam tipos derivados com informação mais especifica.

Ora então temos algo como:
 void Process(BaseEvent baseEvent);   
   
 public class BaseEvent   
 {   
   public int Id { get; set; }  
   public DateTime EventOn { get; set; }  
   public string User { get; set; }  
 }   
   
 public class LocationEvent : BaseEvent  
 {   
   public decimal Latitude { get; set; }  
   public decimal Longitude { get; set; }  
 }   
   
 public class VehicleEvent : BaseEvent  
 {   
   public string LicencePlate { get; set; }  
 }   

O problema surge quando os vários componentes chamam a WebApi, passam os tipos derivados, a WebApi recebe o tipo derivado mas faz sempre deserialize para o tipo base.

Para dar a volta à questão temos de controlar o deserialize através dum JsonConverter e mais uns pózinhos mágicos :)
  1. Adicionar o pacote nuget do Newtonsoft.Json
 Install-Package Newtonsoft.Json  
  1. Alteramos a classe base para ter o tipo que realmente é
 public class BaseEvent    
 {  
   public string typename { get { return this.GetType().Name; } }  
   
   public int Id { get; set; }  
   public DateTime EventOn { get; set; }  
   public string User { get; set; }  
 }   
  1. Criamos um JsonConverter 
 public class BaseEventJsonCreationConverter : JsonConverter  
 {  
   public override bool CanWrite  
   {  
     get  
     {  
       return false;  
     }  
   }  
   
   public override bool CanConvert(Type objectType)  
   {  
     return typeof(BaseEvent).IsAssignableFrom(objectType);  
   }  
   
   private BaseEvent Create(Type objectType, JObject jObject)  
   {  
     var typename = jObject.Value("typename");  
     if (typeof(LocationEvent).Name.Equals(typename))  
     {  
       return new LocationEvent();  
     }  
     else if (typeof(VehicleEvent).Name.Equals(typename))  
     {  
       return new VehicleEvent();  
     }  
     else  
     {  
       return new BaseEvent();  
     }  
   }  
   
   public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)  
   {  
     if (reader.TokenType == JsonToken.Null)  
       return null;  
     // Load JObject from stream   
     JObject jObject = JObject.Load(reader);  
   
     // Create target object based on JObject   
     BaseEvent target = Create(objectType, jObject);  
   
     // Populate the object properties   
     serializer.Populate(jObject.CreateReader(), target);  
   
     return target;  
   }  
   
   public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)  
   {  
     throw new NotImplementedException();  
   }  
 }
  1. Decoramos a classe base com JsonConverter
 [JsonConverter(typeof(BaseEventJsonCreationConverter))]  
 public class BaseEvent 
 {  
   . . .   
 }  

E agora a WebApi já faz o deserialize dos objectos correctamente :)


Fonte: Deserialising Json to derived types in Asp.Net Web API

No comments:

Post a Comment