Jackson error : no suitable constructor for a simple class - java

I am in trouble, here is a class I want to Serialize/Deserialize with Jackson 2.3.2.
The serialization works fine but not the deserialization.
I have this exception as below:
No suitable constructor found for type [simple type, class Series]: can not instantiate from JSON object (need to add/enable type information?)
The weirdest thing is that it works perfectly if I comment the constructor!
public class Series {
private int init;
private String key;
private String color;
public Series(String key, String color, int init) {
this.key = key;
this.init = init;
this.color = color;
}
//Getters-Setters
}
And my unit test :
public class SeriesMapperTest {
private String json = "{\"init\":1,\"key\":\"min\",\"color\":\"767\"}";
private ObjectMapper mapper = new ObjectMapper();
#Test
public void deserialize() {
try {
Series series = mapper.readValue(json, Series.class);
} catch (IOException e) {
Assert.fail(e.getMessage());
}
}
}
This exception is throwing from the method deserializeFromObjectUsingNonDefault() of BeanDeserializerBase of Jackson lib.
Any idea?
Thanks

Jackson does not impose the requirement for classes to have a default constructor. You can annotate the exiting constructor with the #JsonCreator annotation and bind the constructor parameters to the properties using the #JsonProperty annotation.
Note: #JsonCreator can be even suppressed if you have single constructor.
This approach has an advantage of creating truly immutable objects which is a good thing for various good reasons.
Here is an example:
public class JacksonImmutable {
public static class Series {
private final int init;
private final String key;
private final String color;
public Series(#JsonProperty("key") String key,
#JsonProperty("color") String color,
#JsonProperty("init") int init) {
this.key = key;
this.init = init;
this.color = color;
}
#Override
public String toString() {
return "Series{" +
"init=" + init +
", key='" + key + '\'' +
", color='" + color + '\'' +
'}';
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
String json = "{\"init\":1,\"key\":\"min\",\"color\":\"767\"}";
System.out.println(mapper.readValue(json, Series.class));
}
}

You have no default (ie no-args) constructor.
Define a no-args constructor:
public Series() {}
The reason it works when you comment out the 3-arg constructor is in java if there are no constructors, the default constructor is implicitly defined.
This leads to the unexpected effect that if there aren't any constructors and you define a non-default constructor, the (implicit) default constructor disappears! Leading you, like many others before you, to wonder what is going on.

Related

How to add setter method for uncertain class fields using ByteBuddy?

According to the official example, generating getter/setter methods for a Field can be done by implementing an interface. But this is when I know exactly which Fields will be added to the Class. My program gets the fields to be added from the database every time, so I need to add getter/setter methods for these fields at runtime.
I define a getter method and assign it as FieldAccessor using intercept method. When I define a setter method in the same way and specify it as FieldAccessor, the system throws an exception.
Here is a simplified example codeļ¼š
#Data
public class MappingFieldBO {
private String fieldName;
private int maxScore;
public MappingFieldBO() {
}
public MappingFieldBO(String fieldName, int maxScore) {
this.fieldName = fieldName;
this.maxScore = maxScore;
}
}
#Data
public class BaseMappingFieldBO {
private Long id;
}
public class Main {
public static void main(String[] args) {
List<MappingFieldBO> mappingFields = getFromDB();
DynamicType.Builder<BaseMappingFieldBO> builder = new ByteBuddy()
.subclass(BaseMappingFieldBO.class)
.name("io.buyan.dv.console.MappingBean");
// add uncertain fields to class
for (MappingFieldBO mappingField : mappingFields) {
String fieldName = mappingField.getFieldName();
builder = builder.defineField(fieldName, String.class, Visibility.PUBLIC)
// define getter method
.defineMethod(getterName(fieldName), String.class, Visibility.PUBLIC)
.intercept(FieldAccessor.ofField(fieldName))
// define setter method
// throw IllegalArgumentException: Method public void io.buyan.dv.console.MappingBean.setShipping() is no bean accessor
.defineMethod(setterName(fieldName), Void.TYPE, Visibility.PUBLIC)
.intercept(FieldAccessor.ofField(fieldName));
}
Class<? extends BaseMappingFieldBO> clazz = builder.make().load(Thread.currentThread().getContextClassLoader()).getLoaded();
}
private static String setterName(String fieldName) {
return "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
}
private static String getterName(String fieldName) {
return "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
}
private static List<MappingFieldBO> getFromDB() {
List<MappingFieldBO> list = new ArrayList<>();
list.add(new MappingFieldBO("shipping", 10));
list.add(new MappingFieldBO("deduct", 8));
return list;
}
}
Your setter lacks a parameter of the field's type. It returns void but needs to accept a value of type String.
Byte Buddy has a convenience method for this. Simply add: withProperty(fieldName, String.class) and everything is setup correctly.

Deserialize multiple fields to one by Jackson

I have following json
{"val": 501, "scale": 2}
Field scale represent how much is decimal point shifted in value (filed val). In this case there are to places, therefore result is value 5.01.
I would like to map it to following class
public class ValueClass {
#JsonProperty("val")
#JsonDeserialize(using = ValueDeserializer.class)
private BigDecimal value;
}
I would like to use custom deserializer for this however it is not clear to me how to access the other fields of JSON from within the deserializer then the annotated one.
#SuppressWarnings("serial")
class ValueDeserializer extends StdDeserializer<BigDecimal> {
protected ValueDeserializer() {
super(BigDecimal.class);
}
#Override
public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
var val = p.readValueAs(Integer.class);
int scale = ??; // <-- How to access "scale" field here?
return new BigDecimal(val).scaleByPowerOfTen(-scale);
}
}
P.S. I know that I could you #JsonCreator in this simple case.
public class ValueClass {
private BigDecimal value;
#JsonCreator
public ValueClass(//
#JsonProperty("val") Integer val, //
#JsonProperty("scale") Integer scale //
) {
this.value = new BigDecimal(val).scaleByPowerOfTen(-scale);
}
}
Nevertheless the real use case is much more complex and it would be more beneficial to keep the the logic inside deserializer (if possible) for easier reuse.
Thanks for help.
Edit 1
As a replay to Chaosfire here is a a bit more clarification to my case.
More real JSON which I need to parse looks this
{"val1":501, "scale":2, "val2":407, "val3":86}
Value of scale filed is shared as divider for multiple fields.
The JSON object has about 10 fields like above and 50 other fields which are relatively straightforward. The reason why I would prefer the deserializer is to avoid huge #JsonCreator which would mainly repeat input values.
This is not possible with your current setup, you provide to the deserializer only the val node, but you need the entire object to access scale node.
Since using #JsonCreator is undesirable, you could change the deserializer to handle ValueClass:
public class ValueDeserializer extends StdDeserializer<ValueClass> {
public ValueDeserializer() {
super(ValueClass.class);
}
#Override
public ValueClass deserialize(JsonParser parser, DeserializationContext context) throws IOException {
JsonNode node = parser.getCodec().readTree(parser);
int scale = node.get("scale").asInt();
ValueClass valueClass = new ValueClass();
JavaType javaType = context.getTypeFactory().constructType(ValueClass.class);
// Introspect the given type
BeanDescription beanDescription = context.getConfig().introspect(javaType);
// Find properties
List<BeanPropertyDefinition> properties = beanDescription.findProperties();
for (BeanPropertyDefinition property : properties) {
String propertyName = property.getName();//get name as in json
String propertyValue = node.get(propertyName).asText();
BigDecimal decimal = new BigDecimal(propertyValue).scaleByPowerOfTen(-scale);
AnnotatedMember accessor = property.getMutator();
accessor.setValue(valueClass, decimal);
}
return valueClass;
}
}
To avoid manually writing property names and setting their values, properties are introspected from java type. This approach is heavily inspired by this answer, you can check it for additional info and possible pitfalls. I believe setting the rest of the fields should be straightforward, using this as a basis.
And simple test:
#JsonDeserialize(using = ValueDeserializer.class)
public class ValueClass {
#JsonProperty("val1")
private BigDecimal value1;
private BigDecimal val2;
private BigDecimal val3;
//setters and getters
#Override
public String toString() {
return "ValueClass{" +
"value1=" + value1 +
", val2=" + val2 +
", val3=" + val3 +
'}';
}
}
Main:
public class Main {
public static void main(String[] args) throws Exception {
String json = "{\"val1\":501, \"scale\":2, \"val2\":407, \"val3\":86}";
ObjectMapper mapper = new ObjectMapper();
ValueClass value = mapper.readValue(json, ValueClass.class);
System.out.println(value);
}
}
Prints - ValueClass{value1=5.01, val2=4.07, val3=0.86}.

Is it possible to allow Gson to just skip fields with wrong types?

I have a class from an SDK that I don't have access to change, but that I would like to serialize a JSON-valid string into.
However, an external API sometimes puts in the wrong type for a Date field.
Long story short: Can I just ignore errors in GSON, or tell Gson to ignore errors on fields, and just get the partial object?
For example, the field should be a double, but I get a Date(number) instead. But I'm not using it anyway, so I don't care, and I don't need the whole process to fail. I just want the parcable fields out, with the faulty fields left null.
NOTE: Writing a deserializer that creates the object I want to have created by Gson defeats the very purpose I propose.
This i a line of code that fails, because a single field is wrong:
Customer customer = gson.fromJson(settings.getCustomerObjectJSONString(), Customer.class);
I would like for it to just skip the field that it cannot parse, because I don't have access to the Customer class, as it is from a generated SDK/library.
I'm aware of two options.
You can use a JSON deserializer implementation to parse JSON elements on your own. However the following example would affect ALL double and Double fields for whatever DTOs passed to that single gson instance, and such a behavior can be deseriable. Unfortunately, I don't know if it's possible to use JsonDeserializer in a "context" way: e.g. let it work for all double and Double fields if those are fields of a certain parent class.
private static final class Dto {
private double primitive;
private Double nullable;
private String string;
}
private static final class FailSafeDoubleJsonDeserializer
implements JsonDeserializer<Double> {
#Override
public Double deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context)
throws JsonParseException {
if ( !element.isJsonPrimitive() ) {
return null;
}
try {
final JsonPrimitive primitive = (JsonPrimitive) element;
final Number number = primitive.getAsNumber();
return number.doubleValue();
} catch ( final NumberFormatException ignored ) {
return null;
}
}
}
private static final JsonDeserializer<Double> failSafeDoubleJsonDeserializer = new FailSafeDoubleJsonDeserializer();
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(double.class, failSafeDoubleJsonDeserializer)
.registerTypeAdapter(Double.class, failSafeDoubleJsonDeserializer)
.create();
public static void main(final String... args) {
dump(gson.fromJson("{\"primitive\": 23, \"nullable\": 42, \"string\": \"foo bar\"}", Dto.class));
dump(gson.fromJson("{\"primitive\": \"whatever\", \"nullable\": \"whatever\", \"string\": \"foo bar\"}", Dto.class));
}
private static void dump(final Dto dto) {
out.println(dto.primitive + " " + dto.nullable + " " + dto.string);
}
Another more low level option can be a type adapter implementation. One advantage of this one over the previous example is that you can annotate failing fields with the #JsonAdapter annotation in DTO classes that are known to be potentially broken.
private static final class Dto {
#JsonAdapter(FailSafeDoubleTypeAdapter.class)
private double primitive;
#JsonAdapter(FailSafeDoubleTypeAdapter.class)
private Double nullable;
private String string;
}
private static final class FailSafeDoubleTypeAdapter
extends TypeAdapter<Double> {
#Override
public void write(final JsonWriter writer, final Double value) {
throw new UnsupportedOperationException();
}
#Override
public Double read(final JsonReader reader)
throws IOException {
final JsonToken peek = reader.peek();
if ( peek != NUMBER ) {
reader.skipValue();
return null;
}
return reader.nextDouble();
}
}
private static final Gson gson = new Gson();
public static void main(final String... args) {
dump(gson.fromJson("{\"primitive\": 23, \"nullable\": 42, \"string\": \"foo bar\"}", Dto.class));
dump(gson.fromJson("{\"primitive\": \"whatever\", \"nullable\": {\"subValue\": \"whatever\"}, \"string\": \"foo bar\"}", Dto.class));
}
private static void dump(final Dto dto) {
out.println(dto.primitive + " " + dto.nullable + " " + dto.string);
}
Thus, both examples generate the following output:
23.0 42.0 foo bar
0.0 null foo bar
for
{"primitive": 23, "nullable": 42, "string": "foo bar"}
and {"primitive": "whatever", "nullable": {"subValue": "whatever"}, "string": "foo bar"}
payloads respectively.
I looked at the problem in another perspective i.e. the main requirement mentioned in the OP is
1) You don't care what value present in a particular field
2) You are not going to use the particular field value, and don't want the deserializer to fail because of invalid data
In the above case, you can mark the particular field as TRANSIENT or STATIC. By default, Gson will exclude all fields marked transient or static.
Example:-
private transient Date joiningDate;

Renaming JSON fields after the JSON is parsed, but before it's returned as a response [duplicate]

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.

Different names of JSON property during serialization and deserialization

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.

Categories