I am working with jackson-core-2.8.3 and I have a json which has element provided in multiple cases. I want to map it to my class but I am not able to do so because I can have only one type of PropertyNamingStratergy in my class.
Example Json:-
{"tableKey": "1","not_allowed_pwd": 10}
There can be another json like
{"tableKey": "1","notAllowedPwd": 10}
ClassToMap :-
class MyClass {
public String tableKey;
public Integer notAllowedPwd;
}
ObjectMapper code :-
ObjectMapperobjectMapper=new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES,true);
objectMapper.setSerializationInclusion(Include.NON_NULL);
objectMapper.setVisibility(PropertyAccessor.ALL,Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.FIELD,Visibility.ANY);
MyClass obj = objectMapper.readValue(s, MyClass.class);
I am not finding any solution anywhere. It will be good if anyone can help how to proceed.
Use jackson-annotations library and add #JsonProperty as below.
class MyClass {
public String tableKey;
#JsonProperty("not_allowed_pwd")
public Integer notAllowedPwd;
}
You can have a second setter with a #JsonProperty annotation for the second field name:
class MyClass {
private String tableKey;
private Integer notAllowedPwd;
public String getTableKey() {
return tableKey;
}
public void setTableKey(String tableKey) {
this.tableKey = tableKey;
}
public Integer getNotAllowedPwd() {
return notAllowedPwd;
}
public void setNotAllowedPwd(Integer notAllowedPwd) {
this.notAllowedPwd = notAllowedPwd;
}
#JsonProperty("not_allowed_pwd")
public void setNotAllowedPwd2(Integer notAllowedPwd) {
this.notAllowedPwd = notAllowedPwd;
}
}
Take into consideration that if the two properties are present in the json, they will be overwritten.
Use these annotations on field:
#JsonProperty("not_allowed_pwd")
#JsonAlias("notAllowedPwd")
public Integer notAllowedPwd;
Related
I have JSON response which looks like that:
{
"response":[
"Some number (for example 8091)",
{
"Bunch of primitives inside the first JSONObject"
},
{
"Bunch of primitives inside the second JSONObject"
},
{
"Bunch of primitives inside the third JSONObject"
},
... (and so on)
]
}
So it's an array with first integer element and other elements are JSONObject.
I don't need integer element to be parsed. So how do I handle it using GSON?
I would solve this problem by creating a custom JsonDeserializer and registering it to your Gson instance before parsing. This custom deserializer would be set up to handle both ints and real objects.
First you need to build up a series of model objects to represent the data. Here's a template for what that might look like:
private static class TopLevel {
#SerializedName("response")
private final List<ResponseElement> elements;
private TopLevel() {
this.elements = null;
}
}
private static class ResponseInteger implements ResponseElement {
private final int value;
public ResponseInteger(int value) {
this.value = value;
}
}
private static class ResponseObject implements ResponseElement {
#SerializedName("id")
private final String id;
#SerializedName("text")
private final String text;
private ResponseObject() {
this.id = null;
this.text = null;
}
}
private interface ResponseElement {
// marker interface
}
TopLevel and ResponseObject have private constructors because they are going to let Gson set their fields using reflection, while ResponseInteger has a public constructor because we're going to manually invoke it from our custom deserializer.
Obviously you will have to fill out ResponseObject with the rest of its fields.
The deserializer is relatively simple. The json you posted contains only two kinds of elements, and we'll leverage this. Each time the deserializer is invoked, it checks whether the element is a primitive, and returns a ResponseInteger if so (or a ResponseObject if not).
private static class ResponseElementDeserializer implements JsonDeserializer<ResponseElement> {
#Override
public ResponseElement deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonPrimitive()) {
return new ResponseInteger(json.getAsInt());
}
else {
return context.deserialize(json, ResponseObject.class);
}
}
}
To use this deserializer, you'll have to register it with Gson using the GsonBuilder object.
private static Gson getGson() {
return new GsonBuilder()
.registerTypeAdapter(ResponseElement.class, new ResponseElementDeserializer())
.create();
}
And that's it. Now you can use this Gson object to easily parse TopLevel objects!
public void parseJson() {
TopLevel t = getGson().fromJson(json, TopLevel.class);
for (ResponseElement element : t.elements) {
System.out.println(element);
}
}
8061
[450602: Поздравляем!]
[451700: С реакцией чата и рассуждениями Папани после рипа..]
[451578: Помним...Любим...Скорбим...<br>2107 забирает лучших]
[451371: Земля тебе пухом братишка]
[451332: Доигрался, минус 900 экзов<br><br>R I P]
[451269: ]
[451242: https://www.twitch.tv/arthas подрубка<br><br>evilpapech.ru - скидка 30% на футболки!]
[451217: ]
[451181: или так це жерстко?]
[451108: ]
I used these toString() methods, which I omitted above for brevity:
#Override
public String toString() {
return Integer.toString(value);
}
#Override
public String toString() {
return "[" + id + ": " + text + "]";
}
Try this
Gson gson = new Gson();
// Reading from a file.
Example example = gson.fromJson(new FileReader("D:\\content.json"), Example.class);
POJO
package com.example;
public class Example {
private List<Integer> response = null;
public List<Integer> getResponse() {
return response;
}
public void setResponse(List<Integer> response) {
this.response = response;
}
}
Basically this structure is the wrong format for JSON data.
You need to remove the number, or put this number as a field in the same object like the one below (call ObjectA) and consider this is an array of ObjectA.
Then everything should work well. Try the code below:
public class Response {
#SerializedName("response")
#Expose
public List<ObjectA> objectA = null;
}
public class ObjectA {
#SerializedName("value")
#Expose
public Integer value;
#SerializedName("description")
#Expose
public String description;
}
Response response = new Gson().fromJson(responseString, Response.class);
Please use below ValueObject format which doesn't parse first integer element
public class ResponseVO {
public List<Response> response = new ArrayList();
public class Response {
public final long id;
public final long from_id;
...
}
}
I have converted a DOM document to json String. However, there are some issues with the way List is mapped in scenario where the List has only one value and List has multiple values.
For ex:
1) After DOM document has been convered to json string, here AlphaStatus List has only with one value:
{
"Gamma": {
.
.
.
.
"AlphaStatuses": {
"AlphaStatus": {
"AlphaHeaderKey": "201612221122273660",
"AlphaLineKey": "201612221122273661",
}
},
"Delta": {
...
}
}
}
2) After DOM document has been convered to json string, here AlphaStatus List has only with multiple values is shown as:
{
"Gamma": {
.
.
.
.
"AlphaStatuses": {
"AlphaStatus": [
{
"AlphaHeaderKey": "201612221122273660",
"AlphaLineKey": "201612221122273661",
},
{
"AlphaHeaderKey": "201612221122273660",
"AlphaLineKey": "201612221122273662",
},
{
"AlphaHeaderKey": "201612221122273660",
"AlphaLineKey": "2016}2221122273663",
}
]
},
"Delta": {
...
}
}
}
I am using the below jackson code to convert xml string to json:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
Object json = mapper.readValue(jObject.toString(), Object.class);
String output = mapper.writeValueAsString(json);
My question is, how do i ensure that AlphaStatus List is always starting with [{ and ending with }], no matter whether it has only one value or multiple values. How can this be resolved.
It is causing issues in the other system which assumes that AlphaStatus is a List always and expects [{ to be part of the token.
Any help is appreciated.? Or should i use some string utility in such cases to parse AlphaStatus and replace with [{ and }]. How can this be done
First, it seems the line
Object json = mapper.readValue(jObject.toString(), Object.class);
is useless, because you already have an object (jObject) to serialize.
Just use it:
String output = mapper.writeValueAsString(jObject);
For second, it seems your problematic field is of type java.lang.Object, right?
If you as assign a single value to it, it will result in one single Json object:
jObject.setAlphaStatuses(alphaStatus); -> result -> {...}
If you as assign some kind of collection, it will result in a Json array:
jObject.setAlphaStatuses(Arrays.asList(alphaStatus1, alphaStatus2)); -> result -> [{...},{...}]
To avoid that, either always pass a list or (if you can change the definition of the class) make it to a Collection (maybe some List).
Here a small snippet to test:
import java.util.Arrays;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonObjects {
private final static ObjectMapper mapper = new ObjectMapper();
private final static AlphaStatus as1 = new AlphaStatus();
private final static AlphaStatus as2 = new AlphaStatus();
static {
as1.setAlphaHeaderKey("A");
as1.setAlphaLineKey("B");
as2.setAlphaHeaderKey("C");
as2.setAlphaLineKey("D");
}
public static void main(String[] args) throws JsonProcessingException {
final Gamma gamma = new Gamma();
gamma.setAlphaStatuses(Arrays.asList(as1, as2));
System.out.println(mapper.writeValueAsString(gamma));
gamma.setAlphaStatuses(as1);
System.out.println(mapper.writeValueAsString(gamma));
}
static class Gamma {
Object alphaStatuses;
public Object getAlphaStatuses() {
return alphaStatuses;
}
public void setAlphaStatuses(Object alphaStatuses) {
this.alphaStatuses = alphaStatuses;
}
}
static class AlphaStatus {
String alphaHeaderKey;
String alphaLineKey;
public String getAlphaHeaderKey() {
return alphaHeaderKey;
}
public void setAlphaHeaderKey(String alphaHeaderKey) {
this.alphaHeaderKey = alphaHeaderKey;
}
public String getAlphaLineKey() {
return alphaLineKey;
}
public void setAlphaLineKey(String alphaLineKey) {
this.alphaLineKey = alphaLineKey;
}
}
}
And the result (not exactly your result, only for demonstration):
{"alphaStatuses":[{"alphaHeaderKey":"A","alphaLineKey":"B"},{"alphaHeaderKey":"C","alphaLineKey":"D"}]}
{"alphaStatuses":{"alphaHeaderKey":"A","alphaLineKey":"B"}}
#JsonRootName("Gamma")
public class Gamma {
private AlphaStatuses AlphaStatuses;
// getters and setters
}
public class AlphaStatuses {
#JsonProperty("alphaStatus")
private List<AlphaStatus> alphaStatuses;
// getters and setters
}
public class AlphaStatus{
#JsonProperty("alphaHeaderKey")
private String alphaHeaderKey;
#JsonProperty("alphaLineKey")
private String alphaLineKey;
// getters and setters
}
**Test class**:
#Test
public void test() throws Exception {
Gamma gamma=new Gamma();
gamma.setAlphaStatuses(new AlphaStatuses(Arrays.asList(new AlphaStatus("201612221122273660","201612221122273660"))));
ObjectMapper mapper=new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE,true);
String jsonString=mapper.writeValueAsString(gamma);
System.out.println("output "+jsonString);
}
**Output**:
output {"Gamma":{"alphaStatues":{"alphaStatus":[{"alphaHeaderKey":"201612221122273660","alphaLineKey":"201612221122273660"}]}}}
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"
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
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.
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.
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;
}
}
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"}
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
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;
}
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.
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
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
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) {
// ...
}
}
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);
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)}"
}
}
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.
I have this json:
[{"cdCondicaoPagto":"1","NrParcela":1,"NrDias":0}]
and this class:
public static class CondicaoPagtoItem implements Serializable {
private String cdCondicaoPagto;
private Integer NrParcela;
private Integer NrDias;
public CondicaoPagtoItem() {
}
public String getCdCondicaoPagto() {
return cdCondicaoPagto;
}
public void setCdCondicaoPagto(String cdCondicaoPagto) {
this.cdCondicaoPagto = cdCondicaoPagto;
}
public Integer getNrParcela() {
return NrParcela;
}
public void setNrParcela(Integer NrParcela) {
this.NrParcela = NrParcela;
}
public Integer getNrDias() {
return NrDias;
}
public void setNrDias(Integer NrDias) {
this.NrDias = NrDias;
}
}
And I'm trying to read it by streaming, this way:
JsonFactory jsonFactory = new JsonFactory();
ObjectMapper jsonMapper = new ObjectMapper(jsonFactory);
JsonNode jsonNodeGeral = jsonMapper.readTree(new File("/home/cechinel/Documentos/CondicaoPagtoItem.json"));
Iterator<JsonNode> elements = jsonNodeGeral.getElements();
while(elements.hasNext()){
JsonNode jsonNode = elements.next();
CondicaoPagtoItem condicao = jsonMapper.treeToValue(jsonNode, CondicaoPagtoItem.class);
}
But It causing the following error:
UnrecognizedPropertyException: Unrecognized field "NrParcela"
If I use the annotation #JsonProperty it works, but I don't want to do it in which integer field.
It sounds to me more like it's a naming convention mismatch. setNrParcela would map to a field name nrParcela but your JSON document has the 'n' capitalized as NrParcela.
If you cannot change the JSON field capitalization, you can use #JsonProperty with an overridden name:
#JsonProperty("NrParcela")
But since you didn't want to do that, another option to consider is implementing a PropertyNamingStrategy.
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"
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
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.
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.
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;
}
}
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"}
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
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;
}
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.
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
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
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) {
// ...
}
}
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);
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)}"
}
}
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.