I'm trying and failing to deserialize an enum with Jackson 2.5.4, and I don't quite see my case out there. My input strings are camel case, and I want to simply map to standard Enum conventions.
#JsonFormat(shape = JsonFormat.Shape.STRING)
public enum Status {
READY("ready"),
NOT_READY("notReady"),
NOT_READY_AT_ALL("notReadyAtAll");
private static Map<String, Status> FORMAT_MAP = Stream
.of(Status.values())
.collect(toMap(s -> s.formatted, Function.<Status>identity()));
private final String formatted;
Status(String formatted) {
this.formatted = formatted;
}
#JsonCreator
public Status fromString(String string) {
Status status = FORMAT_MAP.get(string);
if (status == null) {
throw new IllegalArgumentException(string + " has no corresponding value");
}
return status;
}
}
I've also tried #JsonValue on a getter to no avail, which was an option I saw reported elsewhere. They all blow up with:
com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of ...Status from String value 'ready': value not one of declared Enum instance names: ...
What am I doing wrong?
EDIT: Starting from Jackson 2.6, you can use #JsonProperty on each element of the enum to specify its serialization/deserialization value (see here):
public enum Status {
#JsonProperty("ready")
READY,
#JsonProperty("notReady")
NOT_READY,
#JsonProperty("notReadyAtAll")
NOT_READY_AT_ALL;
}
(The rest of this answer is still valid for older versions of Jackson)
You should use #JsonCreator to annotate a static method that receives a String argument. That's what Jackson calls a factory method:
public enum Status {
READY("ready"),
NOT_READY("notReady"),
NOT_READY_AT_ALL("notReadyAtAll");
private static Map<String, Status> FORMAT_MAP = Stream
.of(Status.values())
.collect(Collectors.toMap(s -> s.formatted, Function.identity()));
private final String formatted;
Status(String formatted) {
this.formatted = formatted;
}
#JsonCreator // This is the factory method and must be static
public static Status fromString(String string) {
return Optional
.ofNullable(FORMAT_MAP.get(string))
.orElseThrow(() -> new IllegalArgumentException(string));
}
}
This is the test:
ObjectMapper mapper = new ObjectMapper();
Status s1 = mapper.readValue("\"ready\"", Status.class);
Status s2 = mapper.readValue("\"notReadyAtAll\"", Status.class);
System.out.println(s1); // READY
System.out.println(s2); // NOT_READY_AT_ALL
As the factory method expects a String, you have to use JSON valid syntax for strings, which is to have the value quoted.
This is probably a faster way to do it:
public enum Status {
READY("ready"),
NOT_READY("notReady"),
NOT_READY_AT_ALL("notReadyAtAll");
private final String formatted;
Status(String formatted) {
this.formatted = formatted;
}
#Override
public String toString() {
return formatted;
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
ObjectReader reader = mapper.reader(Status.class);
Status status = reader.with(DeserializationFeature.READ_ENUMS_USING_TO_STRING).readValue("\"notReady\"");
System.out.println(status.name()); // NOT_READY
}
#JsonCreator
public static Status forValue(String name)
{
return EnumUtil.getEnumByNameIgnoreCase(Status.class, name);
}
Adding this static method would resolve your problem of deserializing
For whoever is searching for enums with integer json properties. Here is what worked for me:
enum class Status (private val code: Int) {
PAST(0),
LIVE(2),
UPCOMING(1);
companion object {
private val codes = Status.values().associateBy(Status::code)
#JvmStatic #JsonCreator fun from (value: Int) = codes[value]
}
}
#JsonCreator(mode = JsonCreator.Mode.DELEGATING) was the solution for me.
https://github.com/FasterXML/jackson-module-kotlin/issues/336#issuecomment-630587525
The solutions on this page work only for single field and #JsonFormat(shape = JsonFormat.Shape.NATURAL) (default format)
this works for multiple fields and #JsonFormat(shape = JsonFormat.Shape.OBJECT)
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum PinOperationMode {
INPUT("Input", "I"),
OUTPUT("Output", "O")
;
private final String mode;
private final String code;
PinOperationMode(String mode, String code) {
this.mode = mode;
this.code = code;
}
public String getMode() {
return mode;
}
public String getCode() {
return code;
}
#JsonCreator
static PinOperationMode findValue(#JsonProperty("mode") String mode, #JsonProperty("code") String code) {
return Arrays.stream(PinOperationMode.values()).filter(pt -> pt.mode.equals(mode) && pt.code.equals(code)).findFirst().get();
}
}
You could use #JsonCreator annotation to resolve your problem. Take a look at https://www.baeldung.com/jackson-serialize-enums, there's clear enough explanation about enum and serialize-deserialize with jackson lib.
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 want to use Jackson to convert a Java object into JSON format. I have a class which looks pretty much the following structure
public Class Event
{
String type;
String timestamp;
String hostname;
String service;
Payload payload;
}
I have the getters and setters for the above fields and also the getters/setters in Payload class.
Here is the json format, i want
{
"type":"end",
"time":"2016-08-01 11:11:11:111",
"origin":{
"hostname":"<hostname>",
"service":"<service>"
},
"version":"1.0"
"data":{ .... }
}
I can't seem to find a jackson way to get the above format, don't know how to put the whole payload object in "data" node and how to put the hostname, service in the "origin" node.
from your question, this is one approach that should showcase on how to solve it. Since you only posted 1 class, I am changing the payload to be a map. It works the same way with other classes as well.
Consider this example:
public class JacksonTest {
public static void main(String[] args) throws JsonProcessingException {
Event e = new Event();
e.type="end";
e.service="<service>";
e.hostname = "<hostname>";
e.timestamp = LocalDateTime.now().toString();
Map<String,String> payload = new HashMap<>();
payload.put("param1", "xyz");
e.payload = payload;
String writeValueAsString = new ObjectMapper().writeValueAsString(e);
System.out.println(writeValueAsString);
}
public static class Event {
#JsonProperty
String type;
#JsonProperty("time")
String timestamp;
#JsonIgnore
String hostname;
#JsonIgnore
String service;
#JsonProperty("data")
Map<String, String> payload;
#JsonProperty("origin")
Map<String,String> getOrigin() {
Map<String,String> tmp = new HashMap<>();
tmp.put("hostname", hostname);
tmp.put("service", service);
return tmp;
}
#JsonProperty("version")
private String getVersion() {
return "1.0";
}
}
}
I annotate the Event class with the necessary properties I want and the names they should have. Since you want the hostname and service to be in a nested setting and not create a new object for it (a new object would be easier as you could just have that serialised), I ignore those and instead use a getter to create the necessary structure as a map.
The output is:
{
"type":"end",
"time":"2016-08-19T16:45:18.072",
"data":{"param1":"xyz"},
"origin":{
"hostname":"<hostname>",
"service":"<service>"
},
"version":"1.0"
}
Regads,
Artur
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'm trying to use an enum to wrap certain error codes in my application.
public enum ErrorStatus
{
PAGE_NOT_FOUND("http 404", "lorem ipsum")
private final String code;
private final String description;
private ErrorStatus(String code, String description)
{
this.code = code;
this.description = description;
}
public String getDescription()
{
return description;
}
}
When I receive an error, it is a String with the code e.g. "http 404".
"http 404" (or any other error code) doesn't seem to be a clear and readable enum name:
http_404("page not found", "lorem ipsum")
I found the valueOf() and name() methods can't be overridden, and I want to prevent other from mistakenly using the valueOf() on the String value instead of a custom implementation with a different name.
What is the cleanest way to map the enum from and to that String value?
Edit: After reading your comment I think you are looking for something like this.
import java.util.HashMap;
import java.util.Map.Entry;
public enum ErrorStatus {
PAGE_NOT_FOUND("404", "Description for error 404");
private static final HashMap<String, ErrorStatus> ERRORS_BY_CODE;
private static final HashMap<String, ErrorStatus> ERRORS_BY_DESCR;
static {
ERRORS_BY_CODE = new HashMap<String, ErrorStatus>();
ERRORS_BY_CODE.put("404", PAGE_NOT_FOUND);
ERRORS_BY_DESCR = new HashMap<String, ErrorStatus>();
ERRORS_BY_DESCR.put("Description for error 404", PAGE_NOT_FOUND);
}
So the most important thing here is the use of HashMaps, much like ZouZou suggested. If you want to efficiently look for a description by a givwn code you'll need a map for that, if you want to efficiently look for a code by a given description you'll need a map for that too.
If you have a string like "404" or "500" and want to get the corresponding description you can use
public static ErrorStatus getErrorByCode(String code) {
return ERRORS_BY_CODE.get(code);
}
If you have the description like "Description for error 404" and want to get the corresponding error code you can use
public static ErrorStatus getErrorByDescr(String descr) {
return ERRORS_BY_DESCR.get(descr);
}
If you only have a string containing the description it gets a bit nasty. This is not the most efficient way to do it but assuming you wont have that many error codes it's all right. So if we have a string like "Here is the description of the page not found error 'Description for error 404'" then you can use
public static ErrorStatus getErrorByString(String str) {
for (Entry<String, ErrorStatus> entry : ERRORS_BY_DESCR.entrySet()){
if (str.contains(entry.getKey())) {
return entry.getValue();
}
}
return null;
}
Be carefull about the last method as it returns null if nothing was found and also only gives only the first error object it succeeds (while there can be more than one error description in a code).
You can use a map to hold the mapping code-enum.
enum ErrorStatus {
PAGE_NOT_FOUND("404", "lorem ipsum");
private static class Holder {
private static Map<String, ErrorStatus> MAP = new HashMap<>();
}
private String code;
private String description;
private ErrorStatus(String code, String description) {
this.code = code;
this.description = description;
Holder.MAP.put(code, this);
}
public static ErrorStatus fromCode(String code) {
ErrorStatus error = Holder.MAP.get(code);
if(error == null) {
throw new IllegalArgumentException();
}
return error;
}
}
and then call it like:
ErrorStatus status = ErrorStatus.fromCode("404"); //PAGE_NOT_FOUND
The trick is that the class loader initializes the static inner class before the enum class so that you can use the map in the enum constructor, which implies minimal code.
For mapping the enum to its code, you can just add a method that will give the code value:
public String code() { return this.code; }
There seems no way to prevent valueof or override it.
No it's right you cannot. But you can create a class with public static final fields.
class ErrorStatus {
public static final ErrorStatus PAGE_NOT_FOUND = new ErrorStatus("404", "lorem ipsum");
//override toString, hashCode and equals
//same code as in the enum
}
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.