Diferentes nombres de propiedad JSON durante la serialización y deserialización

149

¿Es posible: tener un campo en clase, pero diferentes nombres para él durante la serialización / deserialización en la biblioteca de Jackson?

Por ejemplo, tengo la clase "Coordiantes".

class Coordinates{
  int red;
}

Para la deserialización de JSON, quiero tener un formato como este:

{
  "red":12
}

Pero cuando serialice el objeto, el resultado debería ser como este:

{
  "r":12
}

Intenté implementar esto aplicando @JsonPropertyanotaciones tanto en getter como en setter (con diferentes valores):

class Coordiantes{
    int red;

    @JsonProperty("r")
    public byte getRed() {
      return red;
    }

    @JsonProperty("red")
    public void setRed(byte red) {
      this.red = red;
    }
}

pero tengo una excepción:

org.codehaus.jackson.map.exc.UnrecognizedPropertyException: campo "rojo" no reconocido

kiRach
fuente

Respuestas:

203

Acabo de probar y esto funciona:

public class Coordinates {
    byte red;

    @JsonProperty("r")
    public byte getR() {
      return red;
    }

    @JsonProperty("red")
    public void setRed(byte red) {
      this.red = red;
    }
}

La idea es que los nombres de los métodos deben ser diferentes, por lo que Jackson lo analiza como campos diferentes, no como un solo campo.

Aquí está el código de prueba:

Coordinates c = new Coordinates();
c.setRed((byte) 5);

ObjectMapper mapper = new ObjectMapper();
System.out.println("Serialization: " + mapper.writeValueAsString(c));

Coordinates r = mapper.readValue("{\"red\":25}",Coordinates.class);
System.out.println("Deserialization: " + r.getR());

Resultado:

Serialization: {"r":5}
Deserialization: 25
bezmax
fuente
es lo mismo posible con jaxb?
Cui Pengfei 崔鹏飞
38

Puede usar el @jsonAliasque se introdujo en jackson 2.9.0

Ejemplo:

public class Info {
  @JsonAlias({ "red" })
  public String r;
}

Esto se usa rdurante la serialización, pero permite redcomo un alias durante la deserialización. Sin embargo, esto también permite rser deserializado.

Asura
fuente
8
La documentación de @JsonAlias lo declara explícitamente has no effect during serialization where primary name is always used. Esto no es lo que quiere el OP.
Xaero Degreaz
3
@XaeroDegreaz Supongo que @Asura significa que puedes usarlo rcomo nombre principal, pero redpara el @JsonAlias, que permite serializarlo r, pero agrega redser reconocido en la deserialización. Anotarlo @JsonProperty("r")y, además, @JsonAlias("red")debería funcionar bien para el problema dado.
Jerrot
16

Puede usar una combinación de @JsonSetter y @JsonGetter para controlar la deserialización y la serialización de su propiedad, respectivamente. Esto también le permitirá mantener los nombres estandarizados de los métodos getter y setter que corresponden a su nombre de campo real.

import com.fasterxml.jackson.annotation.JsonSetter;    
import com.fasterxml.jackson.annotation.JsonGetter;

class Coordinates {
    private int red;

    //# Used during serialization
    @JsonGetter("r")
    public int getRed() {
        return red;
    }

    //# Used during deserialization
    @JsonSetter("red")
    public void setRed(int red) {
        this.red = red;
    }
}
Xaero Degreaz
fuente
15

Me gustaría vincular dos pares getters / setters diferentes a una variable:

class Coordinates{
    int red;

    @JsonProperty("red")
    public byte getRed() {
      return red;
    }

    public void setRed(byte red) {
      this.red = red;
    }

    @JsonProperty("r")
    public byte getR() {
      return red;
    }

    public void setR(byte red) {
      this.red = red;
    }
}
DRCB
fuente
13
Pero en este caso, durante la serialización, obtendremos ambas propiedades: "r" y "rojo", con los mismos valores.
kiRach
@JsonIgnore sobre los métodos que no desea procesar solucionará ese problema
Stephan
Actualmente hay anotaciones más convenientes: @JsonGettery @JsonSetter. Entonces, uno puede establecer exactamente cómo se comportará el serializador.
DRCB
6

Es posible tener un par getter / setter normal. Solo necesita especificar el modo de acceso en@JsonProperty

Aquí hay una prueba unitaria para eso:

public class JsonPropertyTest {

  private static class TestJackson {

    private String color;

    @JsonProperty(value = "device_color", access = JsonProperty.Access.READ_ONLY)
    public String getColor() {
      return color;
    };

    @JsonProperty(value = "color", access = JsonProperty.Access.WRITE_ONLY)
    public void setColor(String color) {
      this.color = color;
    }

  }

  @Test
  public void shouldParseWithAccessModeSpecified() throws Exception {
    String colorJson = "{\"color\":\"red\"}";
    ObjectMapper mapper = new ObjectMapper();
    TestJackson colotObject = mapper.readValue(colorJson, TestJackson.class);

    String ser = mapper.writeValueAsString(colotObject);
    System.out.println("Serialized colotObject: " + ser);
  }
}

Obtuve el resultado de la siguiente manera:

Serialized colotObject: {"device_color":"red"}
Raman Yelianevich
fuente
5

Esto no era lo que esperaba como solución (aunque es un caso de uso legítimo). Mi requisito era permitir que un cliente con errores existente (una aplicación móvil que ya se lanzó) usara nombres alternativos.

La solución radica en proporcionar un método de establecimiento separado como este:

@JsonSetter( "r" )
public void alternateSetRed( byte red ) {
    this.red = red;
}
Andy Talkowski
fuente
2

Sé que es una vieja pregunta, pero para mí lo hice funcionar cuando descubrí que está en conflicto con la biblioteca Gson, así que si está usando Gson, use en @SerializedName("name")lugar de @JsonProperty("name")esperar que esto ayude

Khaled
fuente
2

La anotación con la @JsonAliasque se introdujo con Jackson 2.9+, sin mencionar @JsonPropertyel elemento que se deserializará con más de un alias (nombres diferentes para una propiedad json) funciona bien.

Solía ​​la com.fasterxml.jackson.annotation.JsonAliascoherencia del paquete con com.fasterxml.jackson.databind.ObjectMappermi caso de uso.

Por ejemplo:

@Data
@Builder
public class Chair {

    @JsonAlias({"woodenChair", "steelChair"})
    private String entityType;

}


@Test
public void test1() {

   String str1 = "{\"woodenChair\":\"chair made of wood\"}";
   System.out.println( mapper.readValue(str1, Chair.class));
   String str2 = "{\"steelChair\":\"chair made of steel\"}";
   System.out.println( mapper.readValue(str2, Chair.class));

}

Solo funciona bien.

Arnab Das
fuente
1

Deben haber incluido esto como una característica, porque ahora establecer un diferente @JsonPropertypara un captador y un definidor da como resultado exactamente lo que esperaría (nombre de propiedad diferente durante la serialización y deserialización para el mismo campo). Jackson versión 2.6.7

fetta
fuente
0

Puede escribir una clase de serialización para hacer eso:

public class Symbol

{
     private String symbol;

     private String name;

     public String getSymbol() {
        return symbol;
    }
    public void setSymbol(String symbol) {
        this.symbol = symbol;
    }    
    public String getName() {
        return name;
    }    
    public void setName(String name) {
        this.name = name;
    }
}
public class SymbolJsonSerializer extends JsonSerializer<Symbol> {

    @Override
    public void serialize(Symbol symbol, JsonGenerator jgen, SerializerProvider serializers) throws IOException, JsonProcessingException {
        jgen.writeStartObject();

        jgen.writeStringField("symbol", symbol.getSymbol());
         //Changed name to full_name as the field name of Json string
        jgen.writeStringField("full_name", symbol.getName());
        jgen.writeEndObject(); 
    }
}
            ObjectMapper mapper = new ObjectMapper();

            SimpleModule module = new SimpleModule();
            module.addSerializer(Symbol.class, new SymbolJsonSerializer());
            mapper.registerModule(module); 

            //only convert non-null field, option...
            mapper.setSerializationInclusion(Include.NON_NULL); 

            String jsonString = mapper.writeValueAsString(symbolList);
Vernon Kujyio
fuente