2014-11-15 29 views
18

Voglio serializzare una classe POJO che non è sotto il mio controllo, ma voglio evitare di serializzare qualsiasi proprietà proveniente dalla superclasse, e non dalla classe finale. Esempio:serializzazione Jackson: come ignorare le proprietà della superclasse

public class MyGeneratedRecord extends org.jooq.impl.UpdatableRecordImpl<...>, 
    example.generated.tables.interfaces.IMyGenerated { 
    public void setField1(...); 
    public Integer getField1(); 

    public void setField2(...); 
    public Integer getField2(); 
... 
} 

si può intuire dal l'esempio che che questa classe è generata da JOOQ, ed eredita da un complesso UpdatableRecordImpl classe base che ha anche alcuni metodi di proprietà simili a fagioli, che causano problemi durante la serializzazione. Inoltre, ho diverse classi simili, quindi sarebbe bene evitare di duplicare la stessa soluzione per tutti i miei POJO generati.

Ho trovato le seguenti soluzioni possibili finora:

  • ignorare i campi specifici provenienti dalla superclasse utilizzando la tecnica mixin come questo: How can I tell jackson to ignore a property for which I don't have control over the source code?

    Il problema di questo è che se i cambiamenti della classe base (Ad esempio, un nuovo metodo getAnything() appare in esso), può rompere la mia implementazione.

  • implementare un serializzatore personalizzato e gestire il problema lì. Questo mi sembra un po 'eccessivo.

  • Come per inciso, ho un'interfaccia che descrive esattamente le proprietà che desidero serializzare, forse posso mescolare un'annotazione @JsonSerialize (as = IMyGenerated.class) ...? Posso usare questo per il mio scopo?

Ma, dal punto di progettazione pura di vista, la cosa migliore sarebbe di essere in grado di dire jackson che voglio serializzare solo le proprietà della classe finale, e ignorare tutti quelli ereditati. C'è un modo per farlo?

Grazie in anticipo.

risposta

15

È possibile registrare un numero personalizzato Jackson annotation intropector che ignorerebbe tutte le proprietà che derivano dal tipo super certo. Ecco un esempio:

public class JacksonIgnoreInherited { 

    public static class Base { 
     public final String field1; 

     public Base(final String field1) { 
      this.field1 = field1; 
     } 
    } 

    public static class Bean extends Base { 
     public final String field2; 

     public Bean(final String field1, final String field2) { 
      super(field1); 
      this.field2 = field2; 
     } 
    } 

    private static class IgnoreInheritedIntrospector extends JacksonAnnotationIntrospector { 
     @Override 
     public boolean hasIgnoreMarker(final AnnotatedMember m) { 
      return m.getDeclaringClass() == Base.class || super.hasIgnoreMarker(m); 
     } 
    } 

    public static void main(String[] args) throws JsonProcessingException { 
     final ObjectMapper mapper = new ObjectMapper(); 
     mapper.setAnnotationIntrospector(new IgnoreInheritedIntrospector()); 
     final Bean bean = new Bean("a", "b"); 
     System.out.println(mapper 
         .writerWithDefaultPrettyPrinter() 
         .writeValueAsString(bean)); 
    } 

} 

uscita:

{ "field2": "b" }

+1

Jackson è una biblioteca meravigliosa con una miriade di possibilità di personalizzazione. Mi fa sempre sentire stupido perché non trovo il posto giusto per agganciare il mio comportamento personalizzato. :) Grazie, questo è quello che mi serviva! – Ferenc

+0

La soluzione ottiene il risultato richiesto, ma nasconde o rende difficile conoscere l'elenco delle proprietà serializzate. – jordiburgos

+2

Questa soluzione ha funzionato per me. Tralasciando le complesse classi base di JOOQ, la mia implementazione '' 'hasIgnoreMarker''' è' '' return m.getDeclaringClass(). GetName(). Contains ("org.jooq") || super.hasIgnoreMarker (m) '' ' – Nathan

2

Il buon uso dell'ereditarietà è che le classi figlie estendono o aggiungono funzionalità. Quindi il solito modo è di serializzare i dati.

Un workarround sarebbe utilizzare un oggetto valore (VO) o un oggetto Data Transfer (DTO) con i campi che è necessario serializzare. Passi:

  • Creare una classe VO con i campi che devono essere serializzati.
  • Utilizzare BeanUtils.copyProperties (VO di destinazione, dati di origine) per copiare le proprietà
  • Serializzare l'istanza VO.
+0

che sarebbe un copia-incolla pure in questo caso. L'intero punto utilizzando le classi generate è per evitare la duplicazione delle informazioni sullo schema (campi, tipi, ecc.). Idealmente, il nome di un campo deve essere digitato esattamente una volta - nel mio schema SQL (o in un POJO scritto a mano in caso di ORM). Finora, è più o meno vero nel mio design, e non voglio romperlo. – Ferenc

+0

Altrimenti, è necessario ridefinire il campo nella classe figlio e contrassegnarlo come "non serializzabile". – jordiburgos

+1

@Ferenc: Puoi avere jOOQ per generare tutti quei POJO/VO/DTO per te: http://www.jooq.org/doc/latest/manual/code-generation/codegen-pojos/ –

3

È possibile ignorare i metodi della superclasse che desideri vengano dall'essere uscita e annotale con @JsonIgnore. L'override sposta il controllo della creazione della proprietà nella sottoclasse pur consentendo la sua capacità di filtrarlo dall'output.

Per esempio:

public class SomeClass { 
    public void setField1(...); 
    public Integer getField1(); 

    public void setField2(...); 
    public Integer getField2(); 

    @Override 
    @JsonIgnore 
    public String superClassField1(...){ 
     return super.superClassField1(); 
    }; 

    @Override 
    @JsonIgnore 
    public String superClassField2(...){ 
     return super.superClassField2(); 
    }; 
... 
}