2013-02-27 7 views
12

Próbuję serializować niezmienne POJO do iz JSON, używając Jackson 2.1.4, bez konieczności pisania niestandardowego serializera i z jak najmniejszą liczbą adnotacji. Chciałbym również uniknąć konieczności dodawania niepotrzebnych modułów pobierających lub domyślnych konstruktorów tylko po to, aby zadowolić bibliotekę Jacksona.Immutable/polimorficzny POJO <-> Serializacja JSON z Jacksonem

Jestem teraz zatrzymany na wyjątek:

JsonMappingException: Brak odpowiedniego konstruktora znalezionego na typ [prostego typu, klasy Circle]: nie można utworzyć wystąpienia z obiektu JSON (trzeba dodać/włączyć informacje o typie?)

Kod:

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; 
    } 
} 

Kod testu:

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); 

Każda pomoc jest bardzo ceniona, dzięki!

Odpowiedz

10

Można (wg API) opisywanie konstruktora z @JsonCreator i parametry z @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: Może trzeba opisywać klasę Shape z @JsonSubTypes tak że beton podklasa kształtu można określić.

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

Wyglądał obiecująco, ale teraz pojawia się następujący wyjątek zamiast: JsonMappingException: Argument # 0 konstruktora [konstruktora Circle, adnotacji {interfejsu [email protected]son .annotation.JsonCreator()}] nie ma adnotacji do nazwy właściwości; musi mieć nazwę, gdy konstruktor składający się z wielu parami jest opisany jako Creator – hammarback

+0

Mimo że adnotacja @JsonProperty jest obecna? – nutlike

+0

Tak, jak w twoim przykładzie. We wszystkich argumentach konstruktora. – hammarback

1

Prostokąt ma dwa parametry, a FAQ mówi:

deserializacji typów prostych

Jeśli chcę deserializowania prostych wartości JSON (łańcuchy, Integer/ liczb dziesiętnych) do typów innych niż obsługiwane domyślnie, czy muszę , aby napisać niestandardowy deserializer?

Niekoniecznie. Jeśli klasa deserializacji do ma jeden:

  • konstruktora jednego argumentu o typie dopasowania (String, int/double) lub
  • jednego argumentu metody statycznej z nazwą „valueOf()”, a dopasowanie Argument typ:

Jackson użyje takiej metody, przekazując w dopasowaniu wartość JSON jako argument .

Obawiam trzeba pisać własne 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

spojrzeć Genson biblioteki niektóre z jego kluczowych funkcji są adresowania dokładną problem: polimorfizmu, nie wymagając adnotacje i najważniejsze niezmienne POJOs. Wszystko działa w twoim przykładzie z 0 adnotacjami lub ciężkim conf.

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 daje również możliwość używania aliasów (używanych zamiast nazw klas).

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

To świetna wskazówka, dzięki! Na pewno się tym zajrzę! – hammarback