2013-04-03 18 views
5

Ho un problema con il mixin e l'ereditarietà di Jackson. Ho due classi target, un genitore e un bambino. Per queste due classi di destinazione ho definito due classi MixIn (interfacce) senza relazione di ereditarietà. Ho anche provato con un'interfaccia MixIn che estendeva l'altra ma non c'era alcuna differenza nel risultato. Quando Jackson serializza la classe genitore usa il mixin correttamente definito per la configurazione di serializzazione e tutto funziona bene. Tuttavia, quando Jackson serializza la classe figlia, userà le definizioni di mixin della classe genitore per serializzare le proprietà che esistono sia nella classe genitore che nella classe figlio. Quindi utilizza le definizioni di mixin classe figlio per serializzare le proprietà definite nella classe figlio ma non nella classe genitore. Ora questo probabilmente ha qualcosa a che fare con il confronto delle classi base o l'implementazione di interfacce in Jackson.Jackson mixin selection and inheritance

Ora la domanda è: esiste un modo in cui io possa istruire Jackson a utilizzare solo le definizioni di mixin per la classe figlio durante la serializzazione di oggetti della classe figlio? E sì, vorrei mantenere entrambe le definizioni del mixin in atto per due casi d'uso separati, quindi rimuovere la mappatura del mixin della classe genitore non risolverà il mio problema.

Esempio di codice e JSON di output previsto ed effettivo di seguito.

Ambiente: versione Jackson versione 2.1.4 Tomcat 7.0.34.0

classi di destinazione e interfacce attuano:

public interface TestI { 

    public String getName(); 
} 


public interface TestExtendI extends TestI { 

    public Integer getAge(); 
} 


public class Test implements TestI { 

    String name; 

    public Test(String name) { 
    this.name = name; 
    } 

    @Override 
    public String getName() { 
    return name; 
    } 
} 

public class TestExtend extends Test implements TestExtendI { 

    private Integer age; 

    public TestExtend(String name) { 
    super(name); 
    } 

    public TestExtend(String name, Integer age) { 
    super(name); 
    this.age = age; 
    } 

    @Override 
    public Integer getAge() { 
    return age; 
    } 
} 

Mixin definizioni

public interface TestMixIn { 

    @JsonProperty("base-name") 
    public String getName(); 
} 


public interface TestExtendMixIn { 

    @JsonProperty("ext-name") 
    public String getName(); 

    @JsonProperty("ext-age") 
    public Integer getAge(); 
} 

Se vengono aggiunti entrambi mixins al mapper l'output JSON è:

{ 
    "base-name": "5", // from parent class mixin definition 
    "ext-age": 50  // from child class mixin defition 
} 

Con mixin per TestI.class commentato tutto funziona come previsto e l'uscita JSON è (questo è quello che vorrei realizzare):

{ 
    "ext-name": "5", // from child class mixin defition 
    "ext-age": 50  // from child class mixin defition 
} 

configurazione Object Mapper

@Provider 
@Produces(MediaType.APPLICATION_JSON) 
public class JacksonObjectMapper implements ContextResolver<ObjectMapper> { 

    private ObjectMapper mapper; 

    public JacksonObjectMapper() { 
    mapper = new ObjectMapper(); 
    mapper.addMixInAnnotations(TestI.class, TestMixIn.class); 
    mapper.addMixInAnnotations(TestExtendI.class, TestExtendMixIn.class);  
    } 

    public ObjectMapper getContext(Class<?> type) { 
    return this.mapper; 
    } 
} 

REST api per la gestione della richiesta/risposta

@Path("api/test/{id}") 
public class TestRestApi { 
    @GET 
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) 
    public TestI getTest(@PathParam("id") String id) { 

    TestI ret = new TestExtend(id, 50);  
    return ret; 
    } 
} 

Soluzione

Come descritto da pgelinas nella prima risposta, la soluzione a questo problema è definire i metodi che devono essere gestiti dal mixin "figlio" di nuovo nell'interfaccia figlio. Per il codice di esempio di cui sopra che significherebbe modifiche all'interfaccia TestExtendI:

public interface TestExtendI extends TestI { 

    public Integer getAge(); 

    // override the method from the parent interface here 
    @Override 
    public String getName(); 
} 

questo risolverà il problema e non aggiungere il codice boilerplate troppo alla soluzione. Inoltre non cambierà i contratti di interfaccia poiché l'interfaccia figlio estende già l'interfaccia genitore.

risposta

5

Questo è difficile; la risposta alla tua domanda specifica è no, non puoi dire a una classe figlia di non utilizzare il Mixin applicato a una classe genitore.

Tuttavia, una semplice soluzione al problema qui è di ripetere il metodo getName() nell'interfaccia TestExtendI. Credo che la risoluzione delle annotazioni MixIn non segua la normale sovrascrittura genitore-figlio (come nel caso delle normali annotazioni), ma preferiremo preferibilmente il MixIn che viene applicato alla classe che dichiara il metodo. Questo potrebbe essere un bug in Jackson o una scelta di design, è sempre possibile compilare un numero su github.

+0

Grazie, funziona davvero. – user2239050