2013-02-27 17 views
12

Sto provando a serializzare un POJO immutabile da e verso JSON, utilizzando Jackson 2.1.4, senza dover scrivere un serializzatore personalizzato e con il minor numero possibile di annotazioni. Mi piace anche evitare di dover aggiungere getter o costruttori predefiniti solo per soddisfare la libreria di Jackson.POJO immutabile/polimorfico <-> serializzazione JSON con Jackson

ora sto bloccato su eccezione:

JsonMappingException: Nessun costruttore adatto trovati per il tipo [tipo semplice, classe Circle]: non può creare un'istanza di oggetto JSON (necessità di aggiungere/attivare le informazioni di tipo?)

Il codice:

public abstract class Shape {} 


public class Circle extends Shape { 
    public final int radius; // Immutable - no getter needed 

    public Circle(int radius) { 
    this.radius = radius; 
    } 
} 


public class Rectangle extends Shape { 
    public final int w; // Immutable - no getter needed 
    public final int h; // Immutable - no getter needed 

    public Rectangle(int w, int h) { 
    this.w = w; 
    this.h = h; 
    } 
} 

Il codice di prova:

ObjectMapper mapper = new ObjectMapper(); 
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); // Adds type info 

Shape circle = new Circle(10); 
Shape rectangle = new Rectangle(20, 30); 

String jsonCircle = mapper.writeValueAsString(circle); 
String jsonRectangle = mapper.writeValueAsString(rectangle); 

System.out.println(jsonCircle); // {"@class":"Circle","radius":123} 
System.out.println(jsonRectangle); // {"@class":"Rectangle","w":20,"h":30} 

// Throws: 
// JsonMappingException: No suitable constructor found. 
// Can not instantiate from JSON object (need to add/enable type information?) 
Shape newCircle = mapper.readValue(jsonCircle, Shape.class); 
Shape newRectangle = mapper.readValue(jsonRectangle, Shape.class); 

System.out.println("newCircle = " + newCircle); 
System.out.println("newRectangle = " + newRectangle); 

Qualsiasi aiuto è molto apprezzato, grazie!

risposta

10

Si potrebbe (secondo l'API) annotare il costruttore con @JsonCreator ed i parametri con @JsonProperty.

public class Circle extends Shape { 
    public final int radius; // Immutable - no getter needed 

    @JsonCreator 
    public Circle(@JsonProperty("radius") int radius) { 
     this.radius = radius; 
    } 
} 

public class Rectangle extends Shape { 
    public final int w; // Immutable - no getter needed 
    public final int h; // Immutable - no getter needed 

    @JsonCreator   
    public Rectangle(@JsonProperty("w") int w, @JsonProperty("h") int h) { 
     this.w = w; 
     this.h = h; 
    } 
} 

Edit: Forse si deve annotare la classe Shape con @JsonSubTypes in modo che la sottoclasse concreta di Shape potrebbe essere determinata.

@JsonSubTypes({@JsonSubTypes.Type(Circle.class), @JsonSubTypes.Type(Rectangle.class)}) 
public abstract class Shape {} 
+1

sembrava promettente, ma ora ottengo la seguente eccezione invece: JsonMappingException: Argomento # 0 del costruttore [costruttore per Circle, annotazioni: {interfaccia [email protected]son .annotation.JsonCreator()}] non ha annotazioni sul nome della proprietà; deve avere un nome quando il costruttore di parametri multipli ha annotato come Creatore – hammarback

+0

Sebbene sia presente l'annotazione @JsonProperty? – nutlike

+0

Sì, come nel tuo esempio. Su tutti gli argomenti del costruttore. – hammarback

1

rettangolo ha due parametri, e il FAQ dice:

deserializzazione tipi semplici

Se voglio deserializzare valori JSON semplici (stringhe, interi/ numeri decimali) in tipi diversi supportato di default, ho bisogno di per scrivere un deserializzatore personalizzato?

Non necessariamente. Se la classe di deserializzare in ha uno dei:

  • singolo argomento del costruttore con il tipo di corrispondenza (String, int/doppio), o
  • metodo statico singolo argomento con il nome "valueOf()", e la congruenza argomento tipo

Jackson utilizzerà tale metodo, passando in corrispondenza del valore JSON come argomento .

ho paura che devi scrivere il proprio deserializer as show in the Jackson documentation:

ObjectMapper mapper = new ObjectMapper(); 
SimpleModule testModule = 
    new SimpleModule("MyModule", new Version(1, 0, 0, null)) 
     .addDeserializer(MyType.class, new MyTypeDeserializer()); 
mapper.registerModule(testModule); 
3

Dai un'occhiata alla biblioteca Genson alcune delle sue caratteristiche principali sono indirizzamento il problema esatto: il polimorfismo, che non richiedono le annotazioni e le più importanti POJO immutabili. Tutto funziona nel tuo esempio con 0 annotazioni o conf pesanti.

Genson genson = new Genson.Builder().setWithClassMetadata(true) 
          .setWithDebugInfoPropertyNameResolver(true) 
          .create(); 

String jsonCircle = genson.serialize(circle); 
String jsonRectangle = genson.serialize(rectangle); 

System.out.println(jsonCircle); // {"@class":"your.package.Circle","radius":123} 
System.out.println(jsonRectangle); // {"@class":"your.package.Rectangle","w":20,"h":30} 

// Throws nothing :) 
Shape newCircle = genson.deserialize(jsonCircle, Shape.class); 
Shape newRectangle = genson.deserialize(jsonRectangle, Shape.class); 

Genson offre anche la possibilità di utilizzare alias (utilizzati invece nomi di classi).

new Genson.Builder().addAlias("shape", Shape.class) 
       .addAlias("circle", Circle.class) 
       .create(); 
+0

È un ottimo consiglio, grazie! Ne esaminerò sicuramente! – hammarback

Problemi correlati