210

Is it possible: to have one field in class, but different names for it during serialization/deserialization in Jackson library?

For example, I have class "Coordiantes".

class Coordinates{
  int red;
}

For deserialization from JSON want to have format like this:

{
  "red":12
}

But when I will serialize object, result should be like this one:

{
  "r":12
}

I tried to implement this by applying @JsonProperty annotation both on getter and setter (with different values):

class Coordiantes{
    int red;

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

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

but I got an exception:

org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "red"

Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
kiRach
  • 2,355
  • 2
  • 15
  • 12

14 Answers14

256

Just tested and this works:

public class Coordinates {
    byte red;

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

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

The idea is that method names should be different, so jackson parses it as different fields, not as one field.

Here is test code:

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

Result:

Serialization: {"r":5}
Deserialization: 25
bezmax
  • 25,562
  • 10
  • 53
  • 84
85

You can use @jsonAlias which got introduced in jackson 2.9.0

Example:

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

This uses r during serialization, but allows red as an alias during deserialization. This still allows r to be deserialized as well, though.

SQB
  • 3,926
  • 2
  • 28
  • 49
Asura
  • 859
  • 6
  • 2
  • 13
    The [documentation for @JsonAlias](http://fasterxml.github.io/jackson-annotations/javadoc/2.9.pr1/com/fasterxml/jackson/annotation/JsonAlias.html) explicitly states that it `has no effect during serialization where primary name is always used`. This is not what the OP wants. – Xaero Degreaz Feb 27 '18 at 17:35
  • 8
    @XaeroDegreaz I guess @Asura means, that you can use `r` as primary name, but `red` for the `@JsonAlias`, which allows to serialize it to `r`, but adds `red` to be recognized on deserialization. Annotating it with `@JsonProperty("r")` and additionally `@JsonAlias("red")` should work fine for the given problem. – Jerrot Feb 21 '19 at 15:21
28

You can use a combination of @JsonSetter, and @JsonGetter to control the deserialization, and serialization of your property, respectively. This will also allow you to keep standardized getter and setter method names that correspond to your actual field name.

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

Edit: Updated the documentation links, as the fasterxml GitHub pages are now returning 404.

Xaero Degreaz
  • 1,045
  • 11
  • 18
17

I would bind two different getters/setters pair to one 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;
    }
}
Dex Stakker
  • 121
  • 7
DRCB
  • 2,111
  • 13
  • 21
  • 16
    But in this case, during serialization, we will get both properties: "r" and "red", with same values. – kiRach Dec 19 '11 at 11:24
  • @JsonIgnore on the methods you dont want processed will fix that issue – Stephan Jul 24 '20 at 17:51
  • There are nowadays more convenient annotations: `@JsonGetter` and `@JsonSetter`. So one can exactly set how serializer will behave. – DRCB Jul 25 '20 at 21:26
12

You can use this variant:

import lombok.Getter;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonProperty;

//...

@JsonProperty(value = "rr") // for deserialization
@Getter(onMethod_ = {@JsonGetter(value = "r")}) // for serialization
private String rrrr;

with Lombok getter

Vitalii T
  • 453
  • 3
  • 13
10

It's possible to have normal getter/setter pair. You just need to specify access mode in @JsonProperty

Here is unit test for that:

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

I got the output as follows:

Serialized colotObject: {"device_color":"red"}
Raman Yelianevich
  • 1,117
  • 11
  • 10
7

This was not what I was expecting as a solution (though it is a legitimate use case). My requirement was to allow an existing buggy client (a mobile app which already released) to use alternate names.

The solution lies in providing a separate setter method like this:

@JsonSetter( "r" )
public void alternateSetRed( byte red ) {
    this.red = red;
}
7

Annotating with @JsonAlias which got introduced with Jackson 2.9+, without mentioning @JsonProperty on the item to be deserialized with more than one alias(different names for a json property) works fine.

I used com.fasterxml.jackson.annotation.JsonAlias for package consistency with com.fasterxml.jackson.databind.ObjectMapper for my use-case.

For e.g.:

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

}

just works fine.

Arnab Das
  • 113
  • 1
  • 5
4

I know its an old question but for me I got it working when I figured out that its conflicting with Gson library so if you are using Gson then use @SerializedName("name") instead of @JsonProperty("name") hope this helps

Khaled
  • 529
  • 8
  • 19
1

They must have included this as a feature, because now setting a different @JsonProperty for a getter and setter results in exactly what you would expect (different property name during serialization and deserialization for the same field). Jackson version 2.6.7

Jan Siekierski
  • 409
  • 6
  • 14
1

In my case, I had to read inputs in Brazilian portuguese and generate outputs in english.

So, a workaround which worked for me was using @JsonAlias instead of @JsonProperty:


// pseudo-java
@Value
public class User {

   String username;

   public User(
      @JsonAlias("nome_usuario") String username) {
     // ...
   }

}

Dharman
  • 30,962
  • 25
  • 85
  • 135
Jaumzera
  • 2,305
  • 1
  • 30
  • 44
0

You can write a serialize class to do that:

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

Archit Jain
  • 2,154
  • 1
  • 18
  • 32
-1

For Kotlin guys:

data class TestClassDTO(
    @JsonProperty("user_name")
    val username: String
)

You will successfull handle {"user_name": "John"} from POST payload in RestControllers

But when you need to serialize back with same name of @JsonProperty you can use this reflexe-approach

fun Any.forceSerialize(separator: String, sorted: Boolean = false): String {
    var fieldNameToAnnotatedNameMap = this.javaClass.declaredFields.map { it.name }.associateWith { fieldName ->
        val jsonFieldName =
            this::class.primaryConstructor?.parameters?.first { it.name == fieldName }?.annotations?.firstOrNull { it is JsonProperty }
        val serializedName = if (jsonFieldName != null) (jsonFieldName as JsonProperty).value else fieldName
        serializedName
    }
    if (sorted)
        fieldNameToAnnotatedNameMap = fieldNameToAnnotatedNameMap.toList().sortedBy { (_, value) -> value}.toMap()
    return fieldNameToAnnotatedNameMap.entries.joinToString(separator) { e ->
        val field = this::class.memberProperties.first { it.name == e.key }
        "${e.value}=${field.javaGetter?.invoke(this)}"
    }
}
E.Big
  • 723
  • 6
  • 15
-1

Use both JsonAlias and JsonProperty on the attribute.

data class PayoutMethodCard(
    @JsonProperty("payment_account_id")
    @JsonAlias("payout_account_id")
    val payoutAccountId: Long
)

In this case paymentAccountId can serialized from JSON either by payment_account_id or by payout_account_id, but when deserialized back to JSON JSONProperty will be used, and payment_account_id will be used.

mdev
  • 1,366
  • 17
  • 23