2011-06-14 9 views
17

Estoy jugando con los ejemplos de Jackson y estoy teniendo algunos problemas para lograr que la deserialización funcione con clases e interfaces inmutables.Jackson JSON, clases inmutables e interfaces

A continuación es mi código:

package com.art.starter.jackson_starter; 

import java.io.IOException; 
import java.io.StringReader; 
import java.io.StringWriter; 

import org.codehaus.jackson.JsonGenerationException; 
import org.codehaus.jackson.map.JsonMappingException; 
import org.codehaus.jackson.map.ObjectMapper; 
/** * Hello world! * */ public class App { 
    public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException 
    { 
     System.out.println("Hello World!"); 

     AddressImpl.AddressBuilder builder = new AddressImpl.AddressBuilder(); 
     NameImpl.Builder nameBuilder = new NameImpl.Builder(); 
     UserImpl.Builder userBuilder = new UserImpl.Builder(); 


     Name name = nameBuilder.first("FirstName") 
        .last("LastName") 
        .build(); 

     Address address = builder.setCity("TestCity") 
       .setCountry("TestCountry") 
       .setState("PA") 
       .setStreet("TestAddress") 
       .setZip(123) 
       .build();  

     User user = userBuilder.address(address) 
       .gender(User.Gender.MALE) 
       .isVerified(true) 
       .userImage(new byte[5]) 
       .build(); 

     System.out.println(address);   
     System.out.println(name); 
     System.out.println(user); 

     StringWriter sw = new StringWriter(); 
     ObjectMapper mapper = new ObjectMapper(); 
     mapper.writeValue(sw, user); 
     System.out.println(sw); 


     StringReader sr = new StringReader("{\"address\":{\"state\":\"PA\",\"country\":\"TestCountry\",\"street\":\"TestAddress\",\"city\":\"TestCity\",\"zip\":123},\"verified\":true,\"gender\":\"MALE\",\"userImage\":\"AAAAAAA=\"}"); 

     /* 
      This line throws the Exception   
     */ 
     User user2 = mapper.readValue(sr, UserImpl.class); 

     System.out.println(user2); 
    } } 

package com.art.starter.jackson_starter; 

import java.util.Arrays; 

import org.codehaus.jackson.annotate.JsonCreator; 
import org.codehaus.jackson.annotate.JsonProperty; 

public final class UserImpl implements User 
{ 
    private final Address address; 
    private final Gender gender; 
    private final byte[] userImage; 
    private final boolean isVerified; 

    public static class Builder 
    { 
     private Address address; 
     private Gender gender; 
     //  private Name name; 
     private byte[] userImage; 
     private boolean isVerified; 

     public Builder address(Address address) 
     { 
     this.address = address; 
     return this; 
     } 

     public Builder gender(Gender gender) 
     { 
     this.gender = gender; 
     return this; 
     } 

     //  public Builder name(Name name) 
     //  { 
     //   this.name = name; 
     //   return this; 
     //  } 

     public Builder userImage(byte[] userImage) 
     { 
     this.userImage = userImage; 
     return this; 
     } 

     public Builder isVerified(boolean isVerified) 
     { 
     this.isVerified = isVerified; 
     return this; 
     } 

     public UserImpl build() 
     { 
     return new UserImpl(address, gender, userImage, isVerified); 
     } 
    } 

    @JsonCreator 
    public UserImpl(@JsonProperty("address") Address address, @JsonProperty("gender") Gender gender, @JsonProperty("userImage") byte[] userImage, 
     @JsonProperty("verified") boolean isVerified) 
    { 
     super(); 
     this.address = address; 
     this.gender = gender; 
     this.userImage = userImage; 
     this.isVerified = isVerified; 
    } 

    public Address getAddress() 
    { 
     return address; 
    } 

    public Gender getGender() 
    { 
     return gender; 
     } 

    public byte[] getUserImage() 
    { 
     return userImage; 
    } 

    public boolean isVerified() 
    { 
     return isVerified; 
    } 

    @Override 
    public String toString() 
    { 
     StringBuilder builder2 = new StringBuilder(); 
     builder2.append("UserImpl [address="); 
     builder2.append(address); 
     builder2.append(", gender="); 
     builder2.append(gender); 
     builder2.append(", isVerified="); 
     builder2.append(isVerified); 
     builder2.append(", name="); 
     builder2.append(", userImage="); 
     builder2.append(Arrays.toString(userImage)); 
     builder2.append("]"); 
     return builder2.toString(); 
    } 

} 

package com.art.starter.jackson_starter; 

import org.codehaus.jackson.annotate.JsonCreator; 
import org.codehaus.jackson.annotate.JsonProperty; 

public final class AddressImpl implements Address 
{ 
    private final String city; 
    private final String country; 
    private final String street; 
    private final String state; 
    private final int zip; 

    public static class AddressBuilder 
    { 
     private String city; 
     private String country; 
     private String street; 
     private String state; 
     private int zip; 

     public AddressBuilder setCity(String city) 
     { 
     this.city = city; 
     return this; 
     } 

     public AddressBuilder setCountry(String country) 
     { 
     this.country = country; 
     return this; 
     } 

     public AddressBuilder setStreet(String street) 
     { 
     this.street = street; 
     return this; 
     } 

     public AddressBuilder setState(String state) 
     { 
     this.state = state; 
     return this; 
     } 

     public AddressBuilder setZip(int zip) 
     { 
     this.zip = zip; 
     return this; 
     } 

     public AddressImpl build() 
     { 
     return new AddressImpl(city, country, street, state, zip); 
     } 

    } 

    @JsonCreator 
    public AddressImpl(@JsonProperty("city") String city, @JsonProperty("country") String country, @JsonProperty("street") String street, 
     @JsonProperty("state") String state, @JsonProperty("zip") int zip) 
    { 
     this.city = city; 
     this.country = country; 
     this.street = street; 
     this.state = state; 
     this.zip = zip; 
    } 

    public String getCity() 
    { 
     return city; 
    } 

    public String getCountry() 
    { 
     return country; 
    } 

    public String getStreet() 
    { 
     return street; 
    } 

    public String getState() 
    { 
     return state; 
    } 

    public int getZip() 
    { 
     return zip; 
    } 

    @Override 
    public String toString() 
    { 
     StringBuilder builder = new StringBuilder(); 
     builder.append("AddressImpl [city="); 
     builder.append(city); 
     builder.append(", country="); 
     builder.append(country); 
     builder.append(", state="); 
     builder.append(state); 
     builder.append(", street="); 
     builder.append(street); 
     builder.append(", zip="); 
     builder.append(zip); 
     builder.append("]"); 
     return builder.toString(); 
    } 

} 

El problema parece ser con la Dirección. Tengo esta excepción:

Exception in thread "main" org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.art.starter.jackson_starter.Address, problem: abstract types can only be instantiated with additional type information 
at [Source: [email protected]; line: 1, column: 2] 
    at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163) 
    at org.codehaus.jackson.map.deser.StdDeserializationContext.instantiationException(StdDeserializationContext.java:212) 
    at org.codehaus.jackson.map.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:97) 
    at org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:230) 
    at org.codehaus.jackson.map.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:595) 
    at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:472) 
    at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:350) 
    at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2391) 
    at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1614) 
    at com.art.starter.jackson_starter.App.main(App.java:56) 

Estoy seguro que esto se debe a que no hay manera de Jackson a resolver la dirección, que es una interfaz para AddressImpl que es una aplicación concreta. He estado hurgando en los documentos y he consultado algunos artículos sobre @JsonDeserialize (as = AddressImpl.class), pero no funcionó. Así que estoy perplejo. ¿Alguien alguna vez ha conseguido que esto funcione, incluso es compatible?

Funciona como un campeón si reemplazo Address con AddressImpl en la clase UserImpl.

+0

¿cuál es exactamente la línea 56? Ayudaría a saber exactamente qué código arroja el Ex –

+0

@ Aaron-lo siento, editó la edición - está en la penúltima línea del vacío estático principal – nsfyn55

+0

Tuve un problema similar, este recurso podría ayudar: http: //wiki.fasterxml. com/JacksonPolymorphicDeserialization – will824

Respuesta

20

Por si acaso no lo ha visto, aquí hay un blog entry que explica el trabajo con objetos inmutables y Jackson.

Pero definitivamente debe poder usar @JsonDeserialize(as=AddressImpl.class); agregándolo a la interfaz Address.java (ya sea directamente o mediante el uso de mezclas), o agregándolo al campo o a la propiedad. Una cosa a tener en cuenta es que para la deserialización, DEBE estar al lado del acceso que usted usa; setter si tiene uno, si no, al lado del campo. Las anotaciones aún no se comparten entre los usuarios de acceso; así que, por ejemplo, agregarlo a 'getter' no funcionaría.

Jackson 1.8 también permite finalmente el registro de tipos abstract-to-concrete (ver http://jira.codehaus.org/browse/JACKSON-464 para más detalles) que podría ser la mejor opción para indicar que 'AddressImpl' se usará para 'Address'.

+0

Hmm déjame probar esto – nsfyn55

+2

¡Ya vamos! Agregué @JsonDeserialize (as = AddressImpl.class) a la interfaz y funcionó como un campeón. Parece que sería mucho más prudente que se agregue la clase UserImpl encima del miembro, especificando que la implementación concreta en la interfaz parece frustrar el propósito, incluso si es una anotación. ¡Gracias por tu ayuda! – nsfyn55

+0

Sí, agregarlo en la interfaz no es bonito; pero desafortunadamente lo contrario es difícil (si no imposible) de hacer, ya que no hay forma de enumerar todas las clases que JVM ha cargado (para buscar anotaciones). Además, podrías considerar agregar anotaciones a través de mix-ins (ver http://wiki.fasterxml.com/JacksonMixInAnnotations), o agregar al parámetro constructor que toma Address; debería ser posible agregarlo junto a @JsonProperty – StaxMan

Cuestiones relacionadas