I am stuck with a problem which I have been trying to solve since last two days. I have an Integer object and a Float object which I do not want to display in the JSON response if it is 0. I am trying to achieve this with #JsonInclude(value=Include.NON_NULL) but it doesn’t seem to be working.
Does anyone have any suggestion and can explain me what I am doing wrong here?
Lets say the model class is something like this:
#JsonInclude(value = Include.NON_NULL)
public class myClassInfo {
String originalQuery;
String normalizedQuery;
Long id;
Integer performanceStatus;
Float atcPercentage;
Integer ruleOn;
Integer ruleOff;
}
I have the getter and setter methods accordingly. I want to display the atcPercentage, ruleOn and ruleOff only if it is not 0. How would I do that? I hope this explanation helps in understanding my problem. I have tried NON_NULL and it doesn't seems to be working. My understanding if I define the JsonInclude in the beginning of the class, that should be applicable to all the fields. Correct me if I am wrong.
You can write your own filter and use it as below:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonApp {
public static void main(String[] args) throws Exception {
ClassInfo classInfo = new ClassInfo();
classInfo.setId(0L);
classInfo.setAtcPercentage(0F);
classInfo.setPerformanceStatus(0);
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(classInfo));
}
}
#JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = ZeroNumberFilter.class)
class ClassInfo {
private Long id;
private Integer performanceStatus;
private Float atcPercentage;
// getters, setters
}
class ZeroNumberFilter {
#Override
public boolean equals(final Object obj) {
if (obj instanceof Number) {
final Number number = (Number) obj;
return Double.compare(number.doubleValue(), 0) == 0;
}
return false;
}
}
prints {} - empty object. When we change all values to 1, it prints:
{"id":1,"performanceStatus":1,"atcPercentage":1.0}
Include.NON_NULL filters only properties with null value. You could use Include.NON_DEFAULT but in this case you should change your POJO and declare default values for all fields:
#JsonInclude(value = JsonInclude.Include.NON_DEFAULT)
class ClassInfo {
private Long id = 0L;
private Integer performanceStatus = 0;
private Float atcPercentage = 0F;
// getters, setters
}
But this solution could have some drawbacks if your business logic depends on null values somewhere.
See also:
Serialize Only Fields that meet a Custom Criteria with Jackson
Related
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 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.
Question: The Jackson ObjectMapper deserializer is converting a null value to a 0 for a Double field. I need it to either be deserialized to null or Double.NaN. How can I do this?
Do I need to write a custom Double deserializer that maps null to Double.NaN?
Already tried: I have scoured the DeserializationFeature Enum but I don't think anything applies. (http://fasterxml.github.io/jackson-databind/javadoc/2.0.0/com/fasterxml/jackson/databind/DeserializationFeature.html#FAIL_ON_NULL_FOR_PRIMITIVES)
Motivation: I am deserializing a json object into a custom object (Thing) with code similar to the following. I need the deserializer to keep the value as null or change it to Double.NaN because I need to be able to differential between the 0 case (located at latitude/longitude/altitude = 0) and the null/Double.NaN case (when these values are unavailable).
Jackson deserializing
try {
ObjectMapper mapper = new ObjectMapper();
Thing t = mapper.readValue(new File("foobar/json.txt"), Thing.class);
} catch (JsonParseException e) {
...do stuff..
}
Contents of json.txt. Note that the value null is actually written in the file. It is not left empty. It is not the empty string. It is actuall the word null.
{
"thing" : {
"longitude" : null,
"latitude" : null,
"altitude" : null
}
}
Code for Thing
import java.io.Serializable;
public class Thing implements Serializable {
private static final long serialVersionUID = 1L;
Double latitude;
Double longitude;
Double altitude;
public Thing(Double latitude, Double longitude, Double altitude) {
this.latitude = latitude;
this.longitude = longitude;
this.altitude = altitude;
}
...rest of code...
}
This is what I did:
public class DoubleDeserializer extends JsonDeserializer<Double> {
#Override
public Double deserialize(JsonParser parser, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
String doubleStr = parser.getText();
if (doubleStr.isEmpty() || doubleStr == null) {
return null;
}
return new Double(doubleStr);
}
}
and then in my bean:
#JsonDeserialize(using = DoubleDeserializer.class)
private Double partialPressureCO2;
Hope this helps.
The solution that worked for me was to make a custom JSON Deserializer that transformed null into Double.NaN. Adapting what I wrote to match my example code above it would look like this.
public class ThingDeserializer extends JsonDeserializer<Thing> {
#Override
public Thing deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
Thing thingy = new Thing();
JsonNode node = jp.getCodec().readTree(jp);
if (node.get("latitude").isNull()) {
thingy.setLatitude(Double.NaN);
} else {
thingy.setLatitude(node.get("latitude").asDouble());
}
if (node.get("longitude").isNull()) {
thingy.setLongitude(Double.NaN);
} else {
thingy.setLongitude(node.get("longitude").asDouble());
}
if (node.get("altitude").isNull()) {
thingy.setAltitude(Double.NaN);
} else {
thingy.setLatitude(node.get("altitude").asDouble());
}
return thingy;
}
then I registered the deserializer in the class Thing by adding the annotation above the class declaration.
#JsonDeserialize(using = ThingDeserializer.class)
public class Thing implements Serializable {
... class code here ...
}
Note I think a better answer would be to deserialize the Double class rather than the Thing class. By deserializing the Double you could generalize the conversion from null to NaN. This would also do away with pulling the specific fields from the node by field name. I could not figure out how to do it on a limited time budget so this is what worked for me. Also, the deserialization is actually being implicitly called by Jackson through a REST api so I am not sure how/if this changes things. I would love to see a solution that would accomplish this though.
Can I write a generic method to trim all strings within an complex object (object containing other objects)? Should java reflection api be used to achieve this?Thanks.
I have provided a sample below. However in reality there could be multiple objects within objects. Each object might contain a collection of String or collection of other objects which may contain String. Is there a way to trim the Strings - ones directly with the objects and ones within collection.
public class School{
private List<Course> courses;
private List<Student> students;
// Getters and Setters
}
public class Course{
private String name;
private String xxx;
private String yyy;
private List<String> zzzList;
}
public class Student{
private Map<String,String> xxx;
private List<Course> courseList;
}
Yes, reflection is the way. Basically, you need to:
get the class of the top level object (with [object].getClass())
get all the fields of the object (with clazz.getFields() - beware, it works only with public fields)
check if the field is String (either get field.getType() and check it's a string, or do a field.get(the object) and a instanceof String)
if it's the case, replace the string in the object with the trimmed one, using field.set([your object],[trimmed string])
if the field is an object but not a string, call your method recursively
That will do the trick.
---- just seen your update
Trimming strings in collection will be more tricky, since the strings are not exposed as public fields of the collection (List for example).
You will need something more clever, that will check if an object is an instance of List, or Map, or etc... (or a derived class!).
Main problem is also that java generics are done with erasing type at compile type. So you cannot know that your field is List[String] or List[Integer] or whatever. Every List[?] becomes List.
Still you can try to do it like that:
if field type is List
iterate through the list values
if a value is instanceof String, you have to remove it from the list and insert in place the trimmed version
if a value is an object, there you go again recursively with your method.
Not very interesting in real life samples, but more on a library side maybe.
Long way to go though!
Yes, you can do that with reflection, quite easily. Just check if the field is instanceof String.
The exact way to do it depends on your object structure.
/*********************************************************************************************
* Trim first level children of string type in this object
* #param obj which all string properties to be trimmed
*********************************************************************************************/
public static void trimAll(final Object obj)
throws LocalException
{
if (obj==null) return;
final Class c = obj.getClass();
final Method[] methods = c.getMethods();
final Class[] SETTER_ARGS = new Class[]{String.class};
final Object[] SETTER_VAL = new Object[1];
final String SET = "set";
final String GET = "get";
final String SPACE = "\u0020";
final String TAB = "\t";
for (final Method m:methods)
{
try
{
final String name=m.getName();
if (
name.length()>GET.length()
&& name.indexOf(GET)==0
&& m.getReturnType().equals(String.class)
&& m.getParameterTypes().length==0)
{
final String v = (String)m.invoke(obj);
if (v!=null && (v.contains(SPACE) || v.contains(TAB)) )
{
final Method setter=c.getMethod(SET+name.substring(3),SETTER_ARGS);
if (setter!=null)
{
SETTER_VAL[0]=v.trim();
setter.invoke(obj,SETTER_VAL);
}
}
}
}
catch (final Throwable e)
{
throw new LocalException(LocalException.EC_GENERAL_EXCEPTION,e);
}
}
}
We can also use Jackson to serialize and then deserialize the object. While deserializing we can use custom deserializer to trim all the String values.
Create a deserializer like this:
public class TrimStringToNullDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException {
String value = jsonParser.getValueAsString();
if (isNull(value)) {
return null;
}
value = value.trim();
if (value.length() == 0) {
value = null;
}
return value;
}
And then we can use Jackson to trim all values:
public class TrimStringToNullConfiguration {
private ObjectMapper objectMapper;
public Client trimToNull(Client inputClient) throws JsonProcessingException {
return getObjectMapper().readValue(getObjectMapper().writeValueAsString(inputClient), Client.class);
}
private ObjectMapper getObjectMapper() {
if (isNull(objectMapper)) {
objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(String.class, new TrimStringToNullDeserializer());
objectMapper.registerModule(module);
}
return objectMapper;
}
I have placed a working example over here.
private <T> T toTrim(T t) {
Field[] fields = t.getClass().getFields();
for (Field field : fields) {
try {
if (field.get(t) instanceof String) {
Object o = field.get(t);
String s = (String) o;
field.set(t, s.trim().toUpperCase());
}
} catch (IllegalAccessException e) {
log.info("Error converting field "+ field.getName() );
}
}
return t;
}
if (yourObject instanceof String){
yourObject = yourObject.trim();
}
Hope it helps :)
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.