Berbagai nama properti JSON selama serialisasi dan deserialisasi

149

Apakah mungkin: untuk memiliki satu bidang di kelas, tetapi nama yang berbeda untuk itu selama serialisasi / deserialisasi di perpustakaan Jackson?

Sebagai contoh, saya memiliki kelas "Coordiantes".

class Coordinates{
  int red;
}

Untuk deserialisasi dari JSON ingin memiliki format seperti ini:

{
  "red":12
}

Tetapi ketika saya akan membuat serial objek, hasilnya harus seperti ini:

{
  "r":12
}

Saya mencoba menerapkan ini dengan menerapkan @JsonPropertyanotasi pada pengambil dan penyetel (dengan nilai yang berbeda):

class Coordiantes{
    int red;

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

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

tapi saya mendapat pengecualian:

org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Bidang tidak dikenal "merah"

kiRach
sumber

Jawaban:

203

Baru diuji dan ini berfungsi:

public class Coordinates {
    byte red;

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

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

Idenya adalah bahwa nama metode harus berbeda, jadi jackson mem-parsingnya sebagai bidang yang berbeda, bukan sebagai satu bidang.

Berikut ini adalah kode tes:

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

Hasil:

Serialization: {"r":5}
Deserialization: 25
bezmax
sumber
apakah mungkin sama dengan jaxb?
Cui Pengfei 崔鹏飞
38

Anda dapat menggunakan @jsonAliasyang diperkenalkan di jackson 2.9.0

Contoh:

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

Ini menggunakan rselama serialisasi, tetapi memungkinkan redsebagai alias selama deserialisasi. Ini masih memungkinkan runtuk deserialized juga.

Asura
sumber
8
The dokumentasi untuk @JsonAlias secara eksplisit menyatakan bahwa itu has no effect during serialization where primary name is always used. Ini bukan yang diinginkan OP.
Xaero Degreaz
3
@XaeroDegreaz Saya kira @ Asura berarti, bahwa Anda dapat menggunakan rsebagai nama utama, tetapi reduntuk @JsonAlias, yang memungkinkan untuk membuat serialisasi r, tetapi menambah reddiakui pada deserialization. Membubuhi keterangan dengan @JsonProperty("r")dan juga @JsonAlias("red")akan berfungsi dengan baik untuk masalah yang diberikan.
Jerrot
16

Anda dapat menggunakan kombinasi @JsonSetter , dan @JsonGetter untuk masing-masing mengontrol deserialisasi, dan serialisasi properti Anda. Ini juga akan memungkinkan Anda untuk menjaga nama metode metode pengambil dan penyetel standar yang sesuai dengan nama bidang Anda yang sebenarnya.

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
sumber
15

Saya akan mengikat dua pasangan getter / setter berbeda ke satu variabel:

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
sumber
13
Tetapi dalam kasus ini, selama serialisasi, kita akan mendapatkan kedua properti: "r" dan "red", dengan nilai yang sama.
kiRach
@JsonIgnore tentang metode yang Anda tidak ingin diproses akan memperbaiki masalah itu
Stephan
Saat ini ada anotasi yang lebih nyaman: @JsonGetterdan @JsonSetter. Jadi orang dapat dengan tepat mengatur bagaimana serializer akan berperilaku.
DRCB
6

Mungkin saja memiliki pasangan pengambil / penyetel normal. Anda hanya perlu menentukan mode akses di@JsonProperty

Berikut ini adalah unit test untuk itu:

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

Saya mendapatkan output sebagai berikut:

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

Ini bukan yang saya harapkan sebagai solusi (meskipun ini adalah kasus penggunaan yang sah). Persyaratan saya adalah mengizinkan klien kereta yang ada (aplikasi seluler yang sudah dirilis) menggunakan nama alternatif.

Solusinya terletak pada penyediaan metode penyetel terpisah seperti ini:

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

Saya tahu ini pertanyaan lama, tetapi bagi saya saya membuatnya berfungsi ketika saya menemukan bahwa itu bertentangan dengan pustaka Gson jadi jika Anda menggunakan Gson maka gunakanlah @SerializedName("name")alih-alih @JsonProperty("name")harap ini membantu

Khaled
sumber
2

Penjelasan dengan @JsonAliasyang diperkenalkan dengan Jackson 2.9+, tanpa menyebutkan @JsonPropertyitem yang akan di-deserialisasi dengan lebih dari satu alias (nama berbeda untuk properti json) berfungsi dengan baik.

Saya menggunakan com.fasterxml.jackson.annotation.JsonAliaskonsistensi paket dengan com.fasterxml.jackson.databind.ObjectMapperuse case saya.

Untuk misalnya:

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

}

hanya bekerja dengan baik.

Arnab Das
sumber
1

Mereka harus memasukkan ini sebagai fitur, karena sekarang pengaturan yang berbeda @JsonPropertyuntuk pengambil dan penyetel menghasilkan apa yang Anda harapkan (nama properti berbeda selama serialisasi dan deserialisasi untuk bidang yang sama). Jackson versi 2.6.7

fetta
sumber
0

Anda dapat menulis kelas serial untuk melakukannya:

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
sumber