Différents noms de propriété JSON lors de la sérialisation et de la désérialisation

149

Est-il possible: d'avoir un champ dans la classe, mais des noms différents pour celui-ci lors de la sérialisation / désérialisation dans la bibliothèque Jackson?

Par exemple, j'ai la classe "Coordiantes".

class Coordinates{
  int red;
}

Pour la désérialisation de JSON, vous voulez avoir un format comme celui-ci:

{
  "red":12
}

Mais quand je sérialiserai un objet, le résultat devrait être comme celui-ci:

{
  "r":12
}

J'ai essayé de l'implémenter en appliquant une @JsonPropertyannotation à la fois sur getter et setter (avec des valeurs différentes):

class Coordiantes{
    int red;

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

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

mais j'ai une exception:

org.codehaus.jackson.map.exc.UnrecognizedPropertyException: champ non reconnu "rouge"

kiRach
la source

Réponses:

203

Je viens de tester et cela fonctionne:

public class Coordinates {
    byte red;

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

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

L'idée est que les noms de méthode doivent être différents, donc Jackson les analyse comme des champs différents et non comme un seul champ.

Voici le code de test:

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

Résultat:

Serialization: {"r":5}
Deserialization: 25
bezmax
la source
est la même chose possible avec jaxb?
Cui Pengfei 崔鹏飞
38

Vous pouvez utiliser @jsonAliasce qui a été introduit dans jackson 2.9.0

Exemple:

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

Cela utilise rpendant la sérialisation, mais permet redcomme alias pendant la désérialisation. rCependant, cela permet toujours d'être désérialisé.

Asura
la source
8
La documentation de @JsonAlias le déclare explicitement has no effect during serialization where primary name is always used. Ce n'est pas ce que veut le PO.
Xaero Degreaz
3
@XaeroDegreaz Je suppose que @Asura signifie que vous pouvez utiliser rcomme nom principal, mais redpour le @JsonAlias, ce qui permet de le sérialiser r, mais ajoute redà être reconnu lors de la désérialisation. L'annoter avec @JsonProperty("r")et @JsonAlias("red")devrait également fonctionner correctement pour le problème donné.
Jerrot
16

Vous pouvez utiliser une combinaison de @JsonSetter et @JsonGetter pour contrôler respectivement la désérialisation et la sérialisation de votre propriété. Cela vous permettra également de conserver des noms de méthode getter et setter normalisés qui correspondent à votre nom de champ réel.

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
la source
15

Je lierais deux paires getters / setters différentes à une 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
la source
13
Mais dans ce cas, lors de la sérialisation, nous obtiendrons les deux propriétés: "r" et "red", avec les mêmes valeurs.
kiRach
@JsonIgnore sur les méthodes que vous ne souhaitez pas traiter résoudra ce problème
Stephan le
Il existe de nos jours des annotations plus pratiques: @JsonGetteret @JsonSetter. On peut donc définir exactement comment le sérialiseur se comportera.
DRCB
6

Il est possible d'avoir une paire getter / setter normale. Il vous suffit de spécifier le mode d'accès dans@JsonProperty

Voici le test unitaire pour cela:

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

J'ai eu la sortie comme suit:

Serialized colotObject: {"device_color":"red"}
Raman Yelianevich
la source
5

Ce n'était pas ce à quoi je m'attendais en tant que solution (bien que ce soit un cas d'utilisation légitime). Mon exigence était de permettre à un client buggy existant (une application mobile déjà publiée) d'utiliser des noms alternatifs.

La solution consiste à fournir une méthode de définition distincte comme celle-ci:

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

Je sais que c'est une vieille question, mais pour moi, je l'ai fait fonctionner quand j'ai compris que c'était en conflit avec la bibliothèque Gson, donc si vous utilisez Gson, utilisez @SerializedName("name")au lieu d' @JsonProperty("name")espérer que cela aide

Khaled
la source
2

Annoter avec @JsonAliasqui a été introduit avec Jackson 2.9+, sans mentionner @JsonPropertyl'élément à désérialiser avec plus d'un alias (noms différents pour une propriété json) fonctionne bien.

J'ai utilisé com.fasterxml.jackson.annotation.JsonAliaspour la cohérence du package avec com.fasterxml.jackson.databind.ObjectMapperpour mon cas d'utilisation.

Par exemple:

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

}

fonctionne très bien.

Arnab Das
la source
1

Ils doivent avoir inclus cela comme une fonctionnalité, car maintenant la définition d'un différent @JsonPropertypour un getter et un setter donne exactement ce que vous attendez (nom de propriété différent pendant la sérialisation et la désérialisation pour le même champ). Jackson version 2.6.7

fetta
la source
0

Vous pouvez écrire une classe de sérialisation pour ce faire:

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
la source