Utilizzo di sottoclassi per DataContracts utilizzati con JSON

1

Ho una chiamata RESTful che può modificare lo stato di un'istanza. Il mio problema è che il JSON è diverso in base al tipo di azione che l'utente sta prendendo, potrebbe essere un'azione SetProperty o un'azione SetState (tra gli altri).

Ad esempio, un POST /api/sample/ _would invia il seguente JSON

{ 
  instanceId: 12312,      
  actionId: 12345, // Maps to a SetState action
  desiredState: 'CURATION'
}

Ma quando si chiama lo stesso URL per un SetProperty , il JSON sarà simile a

{ 
  instanceId: 12312,      
  actionId: 25354, // Maps to a SetProperty action
  propName: "PropA",
  propValue: "user-input" 
}

Uso il seguente contratto di dati

[DataContract]
class PostInstanceRequest {
    [DataMember]
    int instanceId;
    [DataMember]
    int actionId;
    [DataMember]
    string desiredState;  // Only used for SetState
    [DataMember]
    string propName;   // Only used for SetProperty
    [DataMember]
    string propValue;  // Only used for SetProperty
    // Many more action specific properties here...
}

Non mi piace molto perché sembra che dovrei avere una classe base per le proprietà comuni e quindi dovrei definire più classi base con le proprietà specifiche.

  • SetStatePostInstanceRequest , che aggiunge desiredState
  • SetPropertyPostInstanceRequest , che aggiunge propName e propValue

Tuttavia, se lo faccio, non so in che classe deserializzare. Dovrei prima deserializzare come PostInstanceRequest , controllare la proprietà actionId e poi deserializzare nella sottoclasse corretta.

Un possibile compromesso è usare la composizione

[DataContract]
class SetStateAction {
    [DataMember]
    string desiredState;
}

[DataContract]
class SetPropAction {
    [DataMember]
    string propName;
    [DataMember]
    string propValue;
}

[DataContract]
class PostInstanceRequest {
    [DataMember]
    int instanceId;
    [DataMember]
    int actionId;
    [DataMember]
    SetPropAction setPropParams;
    [DataMember]
    SetStateAction setActionParams;
    // More references to action specific classes down here
}

Quale delle opzioni mi causerà meno problemi? O c'è un modo ancora migliore a cui non ho pensato?

    
posta Juan Mendes 01.05.2014 - 18:15
fonte

1 risposta

2

Hai scritto il servizio a cui stai postando? In tal caso, ti consigliamo di modificarlo, in modo da pubblicare dati SetState su qualcosa come /api/sample/State e postare i dati SetProperty su /api/sample/Property . Quindi avresti azioni di controller separate per ciascun metodo di servizio, con ciascuna che riceve solo i parametri desiderati. Se il servizio non è tuo e non puoi modificarlo, allora ovviamente sei un po 'imbottito. Altrimenti, leggi questo: link

Aggiorna

Se vuoi assolutamente mantenere una singola azione, allo stesso indirizzo, c'è qualcosa che puoi fare. È possibile definire l'azione del controllore in modo che utilizzi un parametro tipizzato per l'interfaccia / base della classe e fare in modo che il deserializzatore di JSON utilizzi le informazioni sul tipo fornite nel proprio json per determinare quale classe reidratare con i dati, ad esempio:

public abstract class ThingBase
{
    public int instanceId { get ; set; }
    public int actionId { get; set; }
}

public class SetStateThing : ThingBase
{
    public string desiredState { get; set; }
}

public class SetPropertyThing : ThingBase
{
    public string propName { get; set; }
    public string propValue { get; set; }
}

public class MyController
{
    public HttpResponseMessage Post([FromBody]ThingBase thing)
    {
        //use thing
    }
}

Il tuo json diventerebbe, ad esempio:

{
$type: "MyAssembly.SetStateThing, MyAssembly",
instanceId: 12312,      
actionId: 12345, // Maps to a SetState action
desiredState: "CURATION"
}

{
$type: "MyAssembly.SetPropertyThing, MyAssembly",
instanceId: 12312,      
actionId: 25354, // Maps to a SetProperty action
propName: "PropA",
propValue: "user-input" 
}

Il trucco è impostare correttamente le impostazioni del serializzatore (nel tuo metodo WebApiConfig.cs Register(HttpConfiguration config) per esempio):

config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;

L'ho usato con un'interfaccia, ma una classe base astratta sembra adattarsi meglio al tuo scenario.

    
risposta data 02.05.2014 - 17:48
fonte

Leggi altre domande sui tag