Jackson ignores JsonProperty annotation in deserialization - java

I've got a bit of a conundrum. I'm trying to deserialize a json message into a pojo using the builder pattern and I'm getting the following error:
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "Information"
(class com.verification.client.models.response.Response$Builder), not marked as ignorable (3 known properties: "status", "products", "information"])
This is very confusing to me as I've clearly marked the field in the pojo with a JsonProperty annotation:
#JsonDeserialize(builder = Response.Builder.class)
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Response {
#JsonProperty("Status")
private final Optional<Status> status;
#JsonProperty("Products")
private final Optional<List<ResponseProduct>> products;
#JsonProperty("Information") //Here's where the field is defined
private final Optional<List<ResponseInformation>> information;
private Response(final Builder b){
this.status = b.status;
this.products = b.products;
this.information = b.information;
}
public Optional<Status> getStatus() {
return status;
}
public Optional<List<ResponseProduct>> getProducts() {
return products;
}
public Optional<List<ResponseInformation>> getInformation() {
return information;
}
#JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
public static class Builder{
private Optional<Status> status;
private Optional<List<ResponseProduct>> products = Optional.empty();
private Optional<List<ResponseInformation>> information = Optional.empty();
public Builder(){}
public Builder status(final Status status){
this.status = Optional.of(status);
return this;
}
public Builder products(final List<ResponseProduct> products){
this.products = Optional.of(products);
return this;
}
public Builder information(final List<ResponseInformation> information){
this.information = Optional.of(information);
return this;
}
public Response build(){
return new Response(this);
}
}
}
I have a feeling it's something small, but at this point I am at a loss for why my code is behaving this way.
P.S.
Here's the json I'm deserializing
{
"Information": [{
"InformationType": "error-details",
"Code": "internal_application_error",
"Description": "Error: Internal",
"DetailDescription": []
}]
}

Solved this a while back, answering for posterity.
The issue I was having is that the build methods were not being correctly interpreted, but by defining #JsonSetter annotations on the methods of the static build class Jackson was able to correctly interpret the input and build the object.

Related

Creating complex JSON payload from Java Pojo Jackson

I want to create below JSON payload
{
"maxResults":3,
"counter":0,
"customerParameters":{
"filters":[
{
"name":"customerId",
"operator":"=",
"value":["hello"]
}
]
},
"dealerParameters":[
{
"name":"club"
},
{
"name":"token"
}
]
}
Coded so far:
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"maxResults",
"counter",
"customerParameters",
"dealerParameters"
})
public class CustomerModel {
#JsonProperty("maxResults")
private Integer maxResults;
#JsonProperty("counter")
private Integer counter;
#JsonProperty("customerParameters")
private CustomerParameters customerParameters;
#JsonProperty("dealerParameters")
private List<DealerParameter> dealerParameters = null;
#JsonProperty("customerParameters")
public CustomerParameters getCustomerParameters() {
return customerParameters;
}
#JsonProperty("customerParameters")
public void setCustomerParameters(CustomerParameters customerParameters) {
this.customerParameters = customerParameters;
}
#JsonProperty("dealerParameters")
public List<DealerParameter> getDealerParameters() {
return dealerParameters;
}
#JsonProperty("dealerParameters")
public void setDealerParameters(List<DealerParameter> dealerParameters) {
this.dealerParameters = dealerParameters;
}
// Getter/Setter for other params
}
CustomerParameters.java
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"filters"
})
public class CustomerParameters {
#JsonProperty("filters")
private List<Filter> filters = null;
// Setter and Getter for filters parameter
}
DealerParameters.java
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"name"
})
public class DealerParameter {
#JsonProperty("name")
private String name;
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
}
Filter.java
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"name",
"operator",
"value"
})
public class Filter {
#JsonProperty("name")
private String name;
#JsonProperty("operator")
private String operator;
#JsonProperty("value")
private List<String> value = null;
#JsonProperty("value")
public List<String> getValue() {
return value;
}
#JsonProperty("value")
public void setValue(List<String> value) {
this.value = value;
}
// Setter and Getter for other properties
}
Missing Part:
#Controller
public class TestContoller {
RestTemplate restTemplate;
Should I instantiate each pojo class with new operator as below and set all required parameters ? or any other approach of creating JSON payload?
CustomerModel customerModel= new CustomerModel();
customerModel.setMaxResults(1);
Filter filter= new Filter();
filter.setName("customerID");
filter.setOperator("-");
filter.setValue(Arrays.asList("club"));
CustomerParameters customerParameters = new CustomerParameters();
customerParameters.setFilters(Arrays.asList(filter));
customerModel.setCustomerParameters(customerParameters);
For DealerParameter class, I want to create multiple objects with same key different value(see the json payload I mentioned above). Below code creates only one object "name":"dealerId"
DealerParameter dealerParameter = new DealerParameter();
dealerParameter.setName("dealerId");
customerModel.setDealerParameters(dealerParameter);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValueAsString(customerModel);
restTemplate.exchange(todo); // restful service call
}
you are already using "ObjectMapper", And ObjectMapper has readValue() method. By using readValue() method you can populate all data at a time like below:--
ObjectMapper objectMapper = new ObjectMapper();
//populating data from json string to POJO
CustomerModel customerModel = objectMapper.readValue(<json String>,CustomerModel.class);
System.out.println(objectMapper.writeValueAsString(customerModel); // print all data

Spring Boot enum JSON serializer

Below is the object that I want to convert to JSON;
public class TestDto{
private ResponseType responseType;
private Long id;
private String name;
}
The ResponseType below is an enum;
public enum ResponseType{
TEST1("test message 1"), TEST2("test message 2"), TEST3("test message 3");
private String message;
}
Below is the JSON which I want to create:
{"code":"TEST1", "message":"test message 1", "id":1, "name":"name"}
and code in the JSON response points the name of the enum and the message in the JSON response points the message field of the enum.
Is there any way to do it?
Easiest way to do this is to add derived getters/setters to TestDto, and suppress JSON serialization of the responseType field.
class TestDto {
private ResponseType responseType;
private Long id;
private String name;
#JsonIgnore // Suppress JSON serialization
public ResponseType getResponseType() {
return this.responseType;
}
public void setResponseType(ResponseType responseType) {
this.responseType = responseType;
}
public String getCode() { // Derived getter for "code" property
return this.responseType.name();
}
public void setCode(String code) { // Derived setter for "code" property
this.responseType = (code == null ? null : ResponseType.valueOf(code));
}
public String getMessage() { // Derived getter for "message" property
return this.responseType.getMessage();
}
#Deprecated // Shouldn't be called by Java code, since it's a dummy stub method
#SuppressWarnings("unused")
public void setMessage(String message) { // Derived setter for "message" property
// Ignore value
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
Two-way test
TestDto testDto = new TestDto();
testDto.setResponseType(ResponseType.TEST1);
testDto.setId(1L);
testDto.setName("name");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(testDto);
System.out.println(json);
TestDto testDto2 = mapper.readValue(json, TestDto.class);
System.out.println(testDto2.getResponseType());
System.out.println(testDto2.getId());
System.out.println(testDto2.getName());
Output
{"id":1,"name":"name","message":"test message 1","code":"TEST1"}
TEST1
1
name
I have updated my previous answer as it wasn't correct. You can use #JsonFormat(shape = JsonFormat.Shape.OBJECT) to indicate that the enum should be serialized like an object (based on the getters).
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum ResponseType{
TEST1("test message 1"), TEST2("test message 2"), TEST3("test message 3");
private String message;
ResponseType(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public String getCode() {
return this.toString();
}
}
After that, you must also use #JsonUnwrapped on the Enum field to avoid having it's fields serialized as an object.
public static class TestDto {
#JsonUnwrapped private ResponseType responseType;
private Long id;
private String name;
}
Running the following code
TestDto testDto = new TestDto(ResponseType.TEST1, 1234356L, "First Response");
result = mapper.writeValueAsString(testDto);
System.out.println(result);
I get the result {"message":"test message 1","code":"TEST1","id":1234356,"name":"First Response"}

Deserializing an enum with Jackson (#JsonValue) [duplicate]

I'm using JAVA 1.6 and Jackson 1.9.9 I've got an enum
public enum Event {
FORGOT_PASSWORD("forgot password");
private final String value;
private Event(final String description) {
this.value = description;
}
#JsonValue
final String value() {
return this.value;
}
}
I've added a #JsonValue, this seems to do the job it serializes the object into:
{"event":"forgot password"}
but when I try to deserialize I get a
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.globalrelay.gas.appsjson.authportal.Event from String value 'forgot password': value not one of declared Enum instance names
What am I missing here?
The serializer / deserializer solution pointed out by #xbakesx is an excellent one if you wish to completely decouple your enum class from its JSON representation.
Alternatively, if you prefer a self-contained solution, an implementation based on #JsonCreator and #JsonValue annotations would be more convenient.
So leveraging on the example by #Stanley the following is a complete self-contained solution (Java 6, Jackson 1.9):
public enum DeviceScheduleFormat {
Weekday,
EvenOdd,
Interval;
private static Map<String, DeviceScheduleFormat> namesMap = new HashMap<String, DeviceScheduleFormat>(3);
static {
namesMap.put("weekday", Weekday);
namesMap.put("even-odd", EvenOdd);
namesMap.put("interval", Interval);
}
#JsonCreator
public static DeviceScheduleFormat forValue(String value) {
return namesMap.get(StringUtils.lowerCase(value));
}
#JsonValue
public String toValue() {
for (Entry<String, DeviceScheduleFormat> entry : namesMap.entrySet()) {
if (entry.getValue() == this)
return entry.getKey();
}
return null; // or fail
}
}
Note that as of this commit in June 2015 (Jackson 2.6.2 and above) you can now simply write:
public enum Event {
#JsonProperty("forgot password")
FORGOT_PASSWORD;
}
The behavior is documented here: https://fasterxml.github.io/jackson-annotations/javadoc/2.11/com/fasterxml/jackson/annotation/JsonProperty.html
Starting with Jackson 2.6 this annotation may also be used to change serialization of Enum like so:
public enum MyEnum {
#JsonProperty("theFirstValue") THE_FIRST_VALUE,
#JsonProperty("another_value") ANOTHER_VALUE;
}
as an alternative to using JsonValue annotation.
You should create a static factory method which takes single argument and annotate it with #JsonCreator (available since Jackson 1.2)
#JsonCreator
public static Event forValue(String value) { ... }
Read more about JsonCreator annotation here.
Actual Answer:
The default deserializer for enums uses .name() to deserialize, so it's not using the #JsonValue. So as #OldCurmudgeon pointed out, you'd need to pass in {"event": "FORGOT_PASSWORD"} to match the .name() value.
An other option (assuming you want the write and read json values to be the same)...
More Info:
There is (yet) another way to manage the serialization and deserialization process with Jackson. You can specify these annotations to use your own custom serializer and deserializer:
#JsonSerialize(using = MySerializer.class)
#JsonDeserialize(using = MyDeserializer.class)
public final class MyClass {
...
}
Then you have to write MySerializer and MyDeserializer which look like this:
MySerializer
public final class MySerializer extends JsonSerializer<MyClass>
{
#Override
public void serialize(final MyClass yourClassHere, final JsonGenerator gen, final SerializerProvider serializer) throws IOException, JsonProcessingException
{
// here you'd write data to the stream with gen.write...() methods
}
}
MyDeserializer
public final class MyDeserializer extends org.codehaus.jackson.map.JsonDeserializer<MyClass>
{
#Override
public MyClass deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException
{
// then you'd do something like parser.getInt() or whatever to pull data off the parser
return null;
}
}
Last little bit, particularly for doing this to an enum JsonEnum that serializes with the method getYourValue(), your serializer and deserializer might look like this:
public void serialize(final JsonEnum enumValue, final JsonGenerator gen, final SerializerProvider serializer) throws IOException, JsonProcessingException
{
gen.writeString(enumValue.getYourValue());
}
public JsonEnum deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException
{
final String jsonValue = parser.getText();
for (final JsonEnum enumValue : JsonEnum.values())
{
if (enumValue.getYourValue().equals(jsonValue))
{
return enumValue;
}
}
return null;
}
I've found a very nice and concise solution, especially useful when you cannot modify enum classes as it was in my case. Then you should provide a custom ObjectMapper with a certain feature enabled. Those features are available since Jackson 1.6. So you only need to write toString() method in your enum.
public class CustomObjectMapper extends ObjectMapper {
#PostConstruct
public void customConfiguration() {
// Uses Enum.toString() for serialization of an Enum
this.enable(WRITE_ENUMS_USING_TO_STRING);
// Uses Enum.toString() for deserialization of an Enum
this.enable(READ_ENUMS_USING_TO_STRING);
}
}
There are more enum-related features available, see here:
https://github.com/FasterXML/jackson-databind/wiki/Serialization-Features
https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features
Try this.
public enum Event {
FORGOT_PASSWORD("forgot password");
private final String value;
private Event(final String description) {
this.value = description;
}
private Event() {
this.value = this.name();
}
#JsonValue
final String value() {
return this.value;
}
}
I like the accepted answer. However, I would improve it a little (considering that there is now Java higher than version 6 available).
Example:
public enum Operation {
EQUAL("eq"),
NOT_EQUAL("ne"),
LESS_THAN("lt"),
GREATER_THAN("gt");
private final String value;
Operation(String value) {
this.value = value;
}
#JsonValue
public String getValue() {
return value;
}
#JsonCreator
public static Operation forValue(String value) {
return Arrays.stream(Operation.values())
.filter(op -> op.getValue().equals(value))
.findFirst()
.orElseThrow(); // depending on requirements: can be .orElse(null);
}
}
You can customize the deserialization for any attribute.
Declare your deserialize class using the annotationJsonDeserialize (import com.fasterxml.jackson.databind.annotation.JsonDeserialize) for the attribute that will be processed. If this is an Enum:
#JsonDeserialize(using = MyEnumDeserialize.class)
private MyEnum myEnum;
This way your class will be used to deserialize the attribute. This is a full example:
public class MyEnumDeserialize extends JsonDeserializer<MyEnum> {
#Override
public MyEnum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
MyEnum type = null;
try{
if(node.get("attr") != null){
type = MyEnum.get(Long.parseLong(node.get("attr").asText()));
if (type != null) {
return type;
}
}
}catch(Exception e){
type = null;
}
return type;
}
}
Here is another example that uses string values instead of a map.
public enum Operator {
EQUAL(new String[]{"=","==","==="}),
NOT_EQUAL(new String[]{"!=","<>"}),
LESS_THAN(new String[]{"<"}),
LESS_THAN_EQUAL(new String[]{"<="}),
GREATER_THAN(new String[]{">"}),
GREATER_THAN_EQUAL(new String[]{">="}),
EXISTS(new String[]{"not null", "exists"}),
NOT_EXISTS(new String[]{"is null", "not exists"}),
MATCH(new String[]{"match"});
private String[] value;
Operator(String[] value) {
this.value = value;
}
#JsonValue
public String toStringOperator(){
return value[0];
}
#JsonCreator
public static Operator fromStringOperator(String stringOperator) {
if(stringOperator != null) {
for(Operator operator : Operator.values()) {
for(String operatorString : operator.value) {
if (stringOperator.equalsIgnoreCase(operatorString)) {
return operator;
}
}
}
}
return null;
}
}
There are various approaches that you can take to accomplish deserialization of a JSON object to an enum. My favorite style is to make an inner class:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.NotEmpty;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import static com.fasterxml.jackson.annotation.JsonFormat.Shape.OBJECT;
#JsonFormat(shape = OBJECT)
public enum FinancialAccountSubAccountType {
MAIN("Main"),
MAIN_DISCOUNT("Main Discount");
private final static Map<String, FinancialAccountSubAccountType> ENUM_NAME_MAP;
static {
ENUM_NAME_MAP = Arrays.stream(FinancialAccountSubAccountType.values())
.collect(Collectors.toMap(
Enum::name,
Function.identity()));
}
private final String displayName;
FinancialAccountSubAccountType(String displayName) {
this.displayName = displayName;
}
#JsonCreator
public static FinancialAccountSubAccountType fromJson(Request request) {
return ENUM_NAME_MAP.get(request.getCode());
}
#JsonProperty("name")
public String getDisplayName() {
return displayName;
}
private static class Request {
#NotEmpty(message = "Financial account sub-account type code is required")
private final String code;
private final String displayName;
#JsonCreator
private Request(#JsonProperty("code") String code,
#JsonProperty("name") String displayName) {
this.code = code;
this.displayName = displayName;
}
public String getCode() {
return code;
}
#JsonProperty("name")
public String getDisplayName() {
return displayName;
}
}
}
In the context of an enum, using #JsonValue now (since 2.0) works for serialization and deserialization.
According to the jackson-annotations javadoc for #JsonValue:
NOTE: when use for Java enums, one additional feature is that value returned by annotated method is also considered to be the value to deserialize from, not just JSON String to serialize as. This is possible since set of Enum values is constant and it is possible to define mapping, but can not be done in general for POJO types; as such, this is not used for POJO deserialization.
So having the Event enum annotated just as above works (for both serialization and deserialization) with jackson 2.0+.
Besides using #JsonSerialize #JsonDeserialize, you can also use SerializationFeature and DeserializationFeature (jackson binding) in the object mapper.
Such as DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, which give default enum type if the one provided is not defined in the enum class.
In my case, this is what resolved:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum PeriodEnum {
DAILY(1),
WEEKLY(2),
;
private final int id;
PeriodEnum(int id) {
this.id = id;
}
public int getId() {
return id;
}
public String getName() {
return this.name();
}
#JsonCreator
public static PeriodEnum fromJson(#JsonProperty("name") String name) {
return valueOf(name);
}
}
Serializes and deserializes the following json:
{
"id": 2,
"name": "WEEKLY"
}
I hope it helps!
Here, 'value' acts as a deserialiser and 'namespace' acts as a serialiser. Hence, you can pass in value "Student Absent" to API while saving, and in DB it will be saved as "STUDENT_ABSENT". On the other hand, while retrieving data in your class, your API will return "Student Absent"
import com.fasterxml.jackson.annotation.JsonProperty;
public enum AttendanceEnums {
STUDENT_PRESENT,
#JsonProperty(value = "Student Absent", namespace = "Student Absent")
STUDENT_ABSENT;
}
I had been looking for a solution to enum serialization and I finally made a solution.
https://github.com/sirgilligan/EnumerationSerialization
https://digerati-illuminatus.blogspot.com/2022/10/java-enum-generic-serializer-and.html
It uses a new annotation and two new classes, EnumerationSerializer and EnumerationDeserializer. You can subclass the EnumerationDeserializer and make a class that sets the enum Class (typical approach) or you can annotate the enum and you don't have to have a subclass of EnumerationDeserializer.
#JsonSerialize(using = EnumerationSerializer.class)
#JsonDeserialize(using = EnumerationDeserializer.class)
#EnumJson(serializeProjection = Projection.NAME, deserializationClass = RGB.class)
enum RGB {
RED,
GREEN,
BLUE
}
Notice how the implementation of ContextualDeserializer pulls the class from the annotation.
https://github.com/sirgilligan/EnumerationSerialization/blob/main/src/main/java/org/example/EnumerationDeserializer.java
There is a lot of good code in this that might give insights.
For your specific question you could do this:
#JsonSerialize(using = EnumerationSerializer.class)
#JsonDeserialize(using = EnumerationDeserializer.class)
#EnumJson(serializeProjection = Projection.NAME, deserializationClass = Event.class)
public enum Event {
FORGOT_PASSWORD("forgot password");
//This annotation is optional because the code looks for value or alias.
#EnumJson(serializeProjection = Projection.VALUE)
private final String value;
private Event(final String description) {
this.value = description;
}
}
Or you could do this:
#JsonSerialize(using = EnumerationSerializer.class)
#JsonDeserialize(using = EnumerationDeserializer.class)
#EnumJson(serializeProjection = Projection.NAME, deserializationClass = Event.class)
public enum Event {
FORGOT_PASSWORD("forgot password");
private final String value;
private Event(final String description) {
this.value = description;
}
}
That's all you have to do.
Then if you have a class that "has a" event you can annotate each occurance to serialize the way you want.
class EventHolder {
#EnumJson(serializeProjection = Projection.NAME)
Event someEvent;
#EnumJson(serializeProjection = Projection.ORDINAL)
Event someOtherEvent;
#EnumJson(serializeProjection = Projection.VALUE)
Event yetAnotherEvent;
}
The simplest way I found is using #JsonFormat.Shape.OBJECT annotation for the enum.
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum MyEnum{
....
}
I did it like this :
// Your JSON
{"event":"forgot password"}
// Your class to map
public class LoggingDto {
#JsonProperty(value = "event")
private FooEnum logType;
}
//Your enum
public enum FooEnum {
DATA_LOG ("Dummy 1"),
DATA2_LOG ("Dummy 2"),
DATA3_LOG ("forgot password"),
DATA4_LOG ("Dummy 4"),
DATA5_LOG ("Dummy 5"),
UNKNOWN ("");
private String fullName;
FooEnum(String fullName) {
this.fullName = fullName;
}
public String getFullName() {
return fullName;
}
#JsonCreator
public static FooEnum getLogTypeFromFullName(String fullName) {
for (FooEnum logType : FooEnum.values()) {
if (logType.fullName.equals(fullName)) {
return logType;
}
}
return UNKNOWN;
}
}
So the value of the property "logType" for class LoggingDto will be DATA3_LOG
This post is old, but if it can help someone, use JsonFormat.Shape.STRING
#JsonFormat(shape = JsonFormat.Shape.STRING)
public enum SomeEnum{
#JsonProperty("SOME_PROPERTY")
someProperty,
...
}
Code results is like this
{"someenum":"SOME_PROPERTY"}
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum LoginOptionType {
PHONE(1, "Phone"), MAIL(2, "mail"), PERSONAL_EMAIL(3, "Personal email");
private static List<LoginOptionType> all;
static {
all = new ArrayList<LoginOptionType>() {
{
add(LoginOptionType.PHONE);
add(LoginOptionType.MAIL);
add(LoginOptionType.PERSONAL_EMAIL);
}
};
}
private final Integer viewValue;
private final String name;
LoginOptionType(Integer viewValue, String name) {
this.viewValue = viewValue;
this.name = name;
}
public Integer getViewValue() {
return viewValue;
}
public String getName() {
return name;
}
public static List<LoginOptionType> getAll() {
return all;
}
}
Response
[
{
"viewValue": 1,
"name": "Phone"
},
{
"viewValue": 2,
"name": "mail"
},
{
"viewValue": 3,
"name": "Personal email"
}
]

Jackson: deserialize with Builder along with standard setters/getters?

Is it possible with Jackson to deserialize json with Builder pattern as well as with default setter and getter approach?
My object is created with Builder that covers only required (final) fields, but I have non-final fields with some values as well that need to be deserialized with setters.
Here is the sample that throws an exception in an attempt to deserialize it with:
new ObjectMapper().readValue(json, Foo.class);
json - json representation serialized with default Jackson serializer, like:
objectMapper.writeValueAsString(foo);
class
#Getter
#Setter
#ToString
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonDeserialize(builder = Foo.Builder.class)
public class Foo {
private final String key;
private final Long user;
private final String action;
private final String material;
private final String currency;
private Foo(String key, Long user, String action, String material, String currency) {
this.key = key;
this.user = user;
this.action = action;
this.material = material;
this.currency = currency;
}
public static class Builder {
private String key;
private Long user;
private String action;
private String material;
private String currency;
#JsonProperty("key")
public Foo.Builder withKey(String key) {
this.key = key;
return this;
}
#JsonProperty("user")
public Foo.Builder withUser(Long user) {
this.user = user;
return this;
}
#JsonProperty("action")
public Foo.Builder withAction(String action) {
this.action = action;
return this;
}
/// other 'with' setters....
}
#JsonProperty("state")
private int state;
#JsonProperty("stat")
private String stat;
#JsonProperty("step")
private String step;
}
The exception it throws like :
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "state" (class com.Foo$Builder), not marked as
ignorable (5 known properties: "key", "user", "action", "material",
"currency",])
If not possible what workaround is the cheapest?
Two things that are suspicious:
You are willing to use the builder inside the Foo class. In that case you should correct the specification
(SessionData.Builder.class is not correct in that case).
You are indeed trying to use an external builder. In this case you should remove or at least mark as ignorable the inner builder, this seems to be the reason of the excetpion you are getting.
In both cases you should make sure the final method to get the Foo instance is called build() otherwise you should annotate the builder with a #JsonPOJOBuilder(buildMethodName = "nameOfMethod", withPrefix = "set").

Spring Boot Data REST #JsonValue POJO serializes as POJO with content field

I am using spring boot 1.5.1.RELEASE, with jackson as MessageConverter.
One of my POJO is looking something like following:
#Getter
#AllArgsConstructor
public class Pojo1 {
#Id
private final String id;
private final NestedValue payload;
}
public class NestedValue {
private final String value;
public NestedValue(String value) {
this.value = value;
}
#JsonCreator
public static NestedValue(String value) {
return new NestedValue(value);
}
#JsonValue
public String getValue() {
return value;
}
}
#RepositoryRestResource(collectionResourceRel = 'pojos', path='pojos')
public interface Pojo1Repository extends MongoRepository<Pojo, String> {
}
I am expecting above to be serialized as this:
{
"payload": "value projected by getValue()"
}
Instead, I am getting as below:
{
"payload": {
"content": "value projected by getValue()"
}
}
If I test it with default ObjectMapper, POJO is serialized as I expected.
Is there a hidden jackson feature that is configured by Spring???

Categories