Jackson does not accept #XlmElement uppercase keys - java

I try to deserialize some classes for use in OpenAPI.
Like for example:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "MyClass", propOrder = {
"config",
"value"
})
public class MyClass
{
#XmlElement(name = "CONFIG")
protected MyConfig config;
#XmlElement(name = "VALUE")
protected MyValue value;
public MyConfig getCONFIG()
{
return config;
}
public MyValue getVALUE()
{
return value;
}
}
I am using these dependencies:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jersey2-jaxrs</artifactId>
</dependency>
In my Swagger-UI i see that it generated this json:
{
"value": {
[...]
},
"config": {
[...]
}
}
But my REST-Interface only accepts the object if the properties are in UPPERCASE.
This is my object Mapper:
om = new ObjectMapper();
om.registerModule(new JaxbAnnotationModule());
What am i doing wrong here?

You could use MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES.
Feature that will allow for more forgiving deserialization of incoming JSON. If enabled, the bean properties will be matched using their lower-case equivalents, meaning that any case-combination (incoming and matching names are canonicalized by lower-casing) should work.
So:
om.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);

Related

Exclude colums (fields) using Jackson CsvMapper to serialize POJO

I have a Java class and i want to serialize it into CSV using jackson.
In addition i want to exclude from the csv a single field base on a external property.
I have tried using all features provides by jackson like Feature.IGNORE_UNKNOWN=true or #JsonIgnoreProperties(ignoreUnknown = true) on my data class, csvMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) but none of them works. I still receive the exception column not found in schema when not all columns are declared in the schema. Code works fine in the other case.
Here i upload my implementation
public class MyClass{
#JsonProperty("A")
private string a;
#JsonProperty("B")
private string b;
#JsonProperty("C")
private string c;
}
CsvMapper mapper = new CsvMapper();
mapper.configure(Feature.IGNORE_UNKNOWN, true);
CsvSchema csvSchema;
if (disable = true) {
schema = CsvSchema.builder()
.addColumn("A")
.addColumn("C")
.build()
} else {
schema = CsvSchema.builder()
.addColumn("A")
.addColumn("B")
.addColumn("C")
.build()
}
ObjectWriter ow = mapper.writer(schema);
String csv = ow.writeValueAsString(list);
use #JsonIgnore on top of property.
Ex:
import com.fasterxml.jackson.annotation.JsonIgnore;
#Validated
#Data
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Builder
public class MyResponseModel {
...
#JsonIgnore
private String createBy;
...
}
Using Feature.IGNORE_UNKNOWN=true or #JsonIgnoreProperties(ignoreUnknown = true) will help when you want to deserialize JSON into your Java class.
For example, this will be useful when you're implementing the REST API and in the POST method dedicated to creating new objects, you want to ignore all invalid/incorrect fields sent to you by the user and process only the valid ones instead of returning the 400/500 error.
In your case, you just need to put the #JsonIgnore annotation on the field in your Java class, which you want to exclude during the serialization. This is an example of excluding the «a» property:
public class MyClass {
#JsonIgnore
#JsonProperty("A")
private String a;
#JsonProperty("B")
private String b;
#JsonProperty("C")
private String c;
}
This should also be useful in cases when you want to exclude some private and sensitive information from application logs, like passwords, PII, PHI, etc.
Updated:
This is the working solution for your case:
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class MyClass {
#JsonProperty("A")
private String a;
#JsonProperty("B")
private String b;
#JsonProperty("C")
private String c;
}
public String test() throws JsonProcessingException {
boolean disable = false;
CsvMapper mapper = new CsvMapper();
mapper.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true);
CsvSchema csvSchema;
List<MyClass> list = List.of(MyClass.builder()
.a("a1")
.b("b1")
.c("c1")
.build()
);
if (disable) {
csvSchema = CsvSchema.builder()
.addColumn("A")
.addColumn("C")
.build();
} else {
csvSchema = CsvSchema.builder()
.addColumn("A")
.addColumn("B")
.addColumn("C")
.build();
}
ObjectWriter ow = mapper.writer(csvSchema);
String csv = ow.writeValueAsString(list);
return csv;
}
Dependency, which I've been using for tests and verification of the above code:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>

How to fix MessageBodyProviderNotFoundException with Jersey Client using Jackson?

I have set up a REST client consuming JSON with Jersey. Firstly according to Baeldung's tutorial with MOXy and secondly according to Vaadin's tutorial with Jackson.
The JSON response from the REST service looks as follows:
{
"DisplayName": "Sixpack, Joe",
"UserID": "joe.sixpack",
"StdLog": "Some text"
}
So I have set up a simple JAXB bean ...
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class DisplayName {
private String displayName;
private String userID;
private String stdLog;
public DisplayName() {
}
public DisplayName(String DisplayName, String UserID, String StdLog) {
this.displayName = DisplayName;
this.userID = UserID;
this.stdLog = StdLog;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getUserID() {
return userID;
}
public void setUserID(String userID) {
this.userID = userID;
}
public String getStdLog() {
return stdLog;
}
public void setStdLog(String stdLog) {
this.stdLog = stdLog;
}
}
... added the Maven dependencies ...
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.22.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
... and implemented a REST client:
Client client = ClientBuilder.newClient();
URI uri = UriBuilder.fromUri("http://server:2000").path("/path/to/service/" + UriComponent.encode(input_parameter, UriComponent.Type.QUERY_PARAM_SPACE_ENCODED)).build();
WebTarget target = client.target(uri);
DisplayName responseDN = target.request(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).get(DisplayName.class);
However, all the POJO fields are always null when using the MOXy media converter regardless of the annotations and constructors (e.g. no-args constructor) used according to similar issues (e.g. JAX-RS Response Object displaying Object fields as NULL values, Java REST service accepts POJO, but fields are always null).
So I want to use Jackson and adapted the JAXB class ...
import com.fasterxml.jackson.annotation.*;
#JsonInclude(JsonInclude.Include.ALWAYS)
#JsonPropertyOrder({"DisplayName","UserID","StdLog"})
public class DisplayName {
#JsonProperty("DisplayName")
private String displayName;
#JsonProperty("UserID")
private String userID;
#JsonProperty("StdLog")
private String stdLog;
public DisplayName() {
}
#JsonCreator
public DisplayName(#JsonProperty("DisplayName") String DisplayName, #JsonProperty("UserID") String UserID, #JsonProperty("StdLog") String StdLog) {
this.displayName = DisplayName;
this.userID = UserID;
this.stdLog = StdLog;
}
public String getDisplayName() {
return displayName;
}
#JsonProperty("DisplayName")
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getUserID() {
return userID;
}
#JsonProperty("UserID")
public void setUserID(String userID) {
this.userID = userID;
}
public String getStdLog() {
return stdLog;
}
#JsonProperty("StdLog")
public void setStdLog(String stdLog) {
this.stdLog = stdLog;
}
}
... and removed the MOXy dependency and added a dependency for JAXB:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.25.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
However, now I run into the error
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json
and none of the solutions I found did help (e.g. MessageBodyReader not found for media type=application/json, Glassfish :MessageBodyProviderNotFoundException in Jersy Client).
Why? And what is the solution?
Shortly after I had posted the question, I found the explanation and solution in the Jersey User Guide which says:
In order to use Jackson as your JSON (JAXB/POJO) provider you need to register JacksonFeature and a ContextResolver for ObjectMapper, if needed, in your Configurable (client/server).
Since MOXy is the default media library and registered automatically with the WebClient, it will be used unless another media library is registered. Hence, one has to register the Jackson media converter ...
Client client = ClientBuilder.newClient().register(JacksonFeature.class);
... and add the respective Maven dependency:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.25.1</version>
</dependency>
I'm just surprised that neither the tutorials nor the other answers to similar questions mentioned it.

How to custom serialize & deserialize enums using FasterXML? [duplicate]

I have an Enum desrcibed below:
public enum OrderType {
UNKNOWN(0, "Undefined"),
TYPEA(1, "Type A"),
TYPEB(2, "Type B"),
TYPEC(3, "Type C");
private Integer id;
private String name;
private WorkOrderType(Integer id, String name) {
this.id = id;
this.name = name;
}
//Setters, getters....
}
I return enum array with my controller ( new OrderType[] {UNKNOWN,TYPEA,TYPEB,TYPEC};), and Spring serializes it into the following json string:
["UNKNOWN", "TYPEA", "TYPEB", "TYPEC"]
What is the best approach to force Jackson to serialize enums just like POJOs? E.g.:
[
{"id": 1, "name": "Undefined"},
{"id": 2, "name": "Type A"},
{"id": 3, "name": "Type B"},
{"id": 4, "name": "Type C"}
]
I played with different annotations but couldn't manage to get such result.
Finally I found solution myself.
I had to annotate enum with #JsonSerialize(using = OrderTypeSerializer.class) and implement custom serializer:
public class OrderTypeSerializer extends JsonSerializer<OrderType> {
#Override
public void serialize(OrderType value, JsonGenerator generator,
SerializerProvider provider) throws IOException,
JsonProcessingException {
generator.writeStartObject();
generator.writeFieldName("id");
generator.writeNumber(value.getId());
generator.writeFieldName("name");
generator.writeString(value.getName());
generator.writeEndObject();
}
}
#JsonFormat(shape= JsonFormat.Shape.OBJECT)
public enum SomeEnum
available since https://github.com/FasterXML/jackson-databind/issues/24
just tested it works with version 2.1.2
answer to TheZuck:
I tried your example, got Json:
{"events":[{"type":"ADMIN"}]}
My code:
#RequestMapping(value = "/getEvent") #ResponseBody
public EventContainer getEvent() {
EventContainer cont = new EventContainer();
cont.setEvents(Event.values());
return cont;
}
class EventContainer implements Serializable {
private Event[] events;
public Event[] getEvents() {
return events;
}
public void setEvents(Event[] events) {
this.events = events;
}
}
and dependencies are:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
<exclusions>
<exclusion>
<artifactId>jackson-annotations</artifactId>
<groupId>com.fasterxml.jackson.core</groupId>
</exclusion>
<exclusion>
<artifactId>jackson-core</artifactId>
<groupId>com.fasterxml.jackson.core</groupId>
</exclusion>
</exclusions>
</dependency>
<jackson.version>2.1.2</jackson.version>
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.
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
Here is my solution. I want transform enum to {id: ..., name: ...} form.
With Jackson 1.x:
pom.xml:
<properties>
<jackson.version>1.9.13</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
Rule.java:
import org.codehaus.jackson.map.annotate.JsonSerialize;
import my.NamedEnumJsonSerializer;
import my.NamedEnum;
#Entity
#Table(name = "RULE")
public class Rule {
#Column(name = "STATUS", nullable = false, updatable = true)
#Enumerated(EnumType.STRING)
#JsonSerialize(using = NamedEnumJsonSerializer.class)
private Status status;
public Status getStatus() { return status; }
public void setStatus(Status status) { this.status = status; }
public static enum Status implements NamedEnum {
OPEN("open rule"),
CLOSED("closed rule"),
WORKING("rule in work");
private String name;
Status(String name) { this.name = name; }
public String getName() { return this.name; }
};
}
NamedEnum.java:
package my;
public interface NamedEnum {
String name();
String getName();
}
NamedEnumJsonSerializer.java:
package my;
import my.NamedEnum;
import java.io.IOException;
import java.util.*;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
public class NamedEnumJsonSerializer extends JsonSerializer<NamedEnum> {
#Override
public void serialize(NamedEnum value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
Map<String, String> map = new HashMap<>();
map.put("id", value.name());
map.put("name", value.getName());
jgen.writeObject(map);
}
}
With Jackson 2.x:
pom.xml:
<properties>
<jackson.version>2.3.3</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
Rule.java:
import com.fasterxml.jackson.annotation.JsonFormat;
#Entity
#Table(name = "RULE")
public class Rule {
#Column(name = "STATUS", nullable = false, updatable = true)
#Enumerated(EnumType.STRING)
private Status status;
public Status getStatus() { return status; }
public void setStatus(Status status) { this.status = status; }
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public static enum Status {
OPEN("open rule"),
CLOSED("closed rule"),
WORKING("rule in work");
private String name;
Status(String name) { this.name = name; }
public String getName() { return this.name; }
public String getId() { return this.name(); }
};
}
Rule.Status.CLOSED translated to {id: "CLOSED", name: "closed rule"}.
An easy way to serialize Enum is using #JsonFormat annotation. #JsonFormat can configure the serialization of a Enum in three ways.
#JsonFormat.Shape.STRING
public Enum OrderType {...}
uses OrderType::name as the serialization method. Serialization of OrderType.TypeA is “TYPEA”
#JsonFormat.Shape.NUMBER
Public Enum OrderTYpe{...}
uses OrderType::ordinal as the serialization method. Serialization of OrderType.TypeA is 1
#JsonFormat.Shape.OBJECT
Public Enum OrderType{...}
treats OrderType as a POJO. Serialization of OrderType.TypeA is {"id":1,"name":"Type A"}
JsonFormat.Shape.OBJECT is what you need in your case.
A little more complicated way is your solution, specifying a serializer for the Enum.
Check out this reference:
https://fasterxml.github.io/jackson-annotations/javadoc/2.2.0/com/fasterxml/jackson/annotation/JsonFormat.html
Use #JsonCreator annotation, create method getType(), is serialize with toString or object working
{"ATIVO"}
or
{"type": "ATIVO", "descricao": "Ativo"}
...
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum SituacaoUsuario {
ATIVO("Ativo"),
PENDENTE_VALIDACAO("Pendente de Validação"),
INATIVO("Inativo"),
BLOQUEADO("Bloqueado"),
/**
* Usuarios cadastrados pelos clientes que não possuem acesso a aplicacao,
* caso venham a se cadastrar este status deve ser alterado
*/
NAO_REGISTRADO("Não Registrado");
private SituacaoUsuario(String descricao) {
this.descricao = descricao;
}
private String descricao;
public String getDescricao() {
return descricao;
}
// TODO - Adicionar metodos dinamicamente
public String getType() {
return this.toString();
}
public String getPropertieKey() {
StringBuilder sb = new StringBuilder("enum.");
sb.append(this.getClass().getName()).append(".");
sb.append(toString());
return sb.toString().toLowerCase();
}
#JsonCreator
public static SituacaoUsuario fromObject(JsonNode node) {
String type = null;
if (node.getNodeType().equals(JsonNodeType.STRING)) {
type = node.asText();
} else {
if (!node.has("type")) {
throw new IllegalArgumentException();
}
type = node.get("type").asText();
}
return valueOf(type);
}
}
In Spring Boot 2, the easiest way is to declare in your application.properties:
spring.jackson.serialization.WRITE_ENUMS_USING_TO_STRING=true
spring.jackson.deserialization.READ_ENUMS_USING_TO_STRING=true
and define the toString() method of your enums.

Jackson ignores #Ignore annotations

I am trying to let Jackson ignore some properties of a DTO, however I can't seem to be able to.
I have a fairly big project with many dependencies (Lombok, Spring, GWT, Gemfire and many more),
and I can't change these dependencies (maybe I can change versions, but it's not my call).
I have prepared a test case, here it is:
this is my test dto class, it has a map that is only useful
server side. A copy of this dto is serialized to be sent to gwt
to display (the implementation is not complete, only the relevant parts
are shown).
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreType;
import lombok.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(of = "id", callSuper = true)
public class MyClass extends MyAbstractClass {
#Getter
#Setter
#Builder
public static class AValueClass {
int someInt;
String SomeString;
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#JsonIgnoreType
public static class MyJsonIgnoreKeyClass {
protected Integer anInt;
protected String aString;
}
#JsonIgnore
#Getter(AccessLevel.NONE) #Setter(AccessLevel.NONE)
private transient Map<MyJsonIgnoreKeyClass, List<AValueClass>> aMapThatJacksonShouldIgnore = new HashMap<>();
public void addToMap(MyJsonIgnoreKeyClass key, AValueClass value) {
List<AValueClass> valueList = aMapThatJacksonShouldIgnore.get(key);
if(valueList == null) {
valueList = new ArrayList<>();
}
valueList.add(value);
aMapThatJacksonShouldIgnore.put(key,valueList);
}
public boolean noMap() {
return aMapThatJacksonShouldIgnore == null || aMapThatJacksonShouldIgnore.keySet().isEmpty();
}
public void nullifyMap() {
aMapThatJacksonShouldIgnore = null;
}
// other methods operating on maps omitted
}
the test model inherits some fields from a superclass
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
#Setter
#Getter
#EqualsAndHashCode(of = "id")
public class MyAbstractClass {
protected String id;
protected Date aDay;
}
here are the unit tests I've prepared
public class MyClassJacksonTest {
ObjectMapper om;
#Before
public void setUp() throws Exception {
om = new ObjectMapper().registerModule(new Jdk8Module());
om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
}
#Test
public void testWithMapValues() throws Exception {
MyClass testClass = new MyClass();
testClass.setADay(new Date());
testClass.setId(UUID.randomUUID().toString());
testClass.addToMap(
new MyClass.MyJsonIgnoreKeyClass(1,"test"),
new MyClass.AValueClass(1,"test"));
StringWriter writer = new StringWriter();
om.writeValue(writer,testClass);
writer.flush();
String there = writer.toString();
MyClass andBackAgain = om.readValue(there, MyClass.class);
assertTrue(andBackAgain.noMap());
}
#Test
public void testWithEmptyMaps() throws Exception {
MyClass testClass = new MyClass();
testClass.setADay(new Date());
testClass.setId(UUID.randomUUID().toString());
StringWriter writer = new StringWriter();
om.writeValue(writer,testClass);
writer.flush();
String there = writer.toString();
MyClass andBackAgain = om.readValue(there, MyClass.class);
assertTrue(andBackAgain.noMap());
}
#Test
public void testWithNullMaps() throws Exception {
MyClass testClass = new MyClass();
testClass.setADay(new Date());
testClass.setId(UUID.randomUUID().toString());
testClass.nullifyMap();
StringWriter writer = new StringWriter();
om.writeValue(writer,testClass);
writer.flush();
String there = writer.toString();
MyClass andBackAgain = om.readValue(there, MyClass.class);
assertTrue(andBackAgain.noMap());
}
}
All of the tests are failing with
com.fasterxml.jackson.databind.JsonMappingException: Can not find a (Map) Key deserializer for type [simple type, class MyClass$MyJsonIgnoreKeyClass]
So the questions are :
Why Jackson tries to find a deserializer for the keys of a map that can't be accessed (since there are no getter and setter) and that is annotated with #JsonIgnore?
More importantly, how can I tell it not to search for the deserializers?
These are the relevant dependencies on my pom, if it can be of any help :
<properties>
<!-- ... -->
<jackson.version>2.7.4</jackson.version>
</properties>
<dependencies>
<!-- other dependencies omitted -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jsonSchema</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr353</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>${jackson.version}</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>stax2-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
It turns out that this is a case of bad interaction between Lombok and Jackson.
The Lombok annotation #AllArgsConstructor generates a constructor that is annotated with #ConstructorProperties, which in turn lists all the properties that are declared in the class.
This is then used by Jackson when the default deserializer is to be used.
In this case, the absence of setters and getters and the presence of #JsonIgnore annotations is not taken into account.
The solution is simply to specify the #AllArgsConstructor with the attribute suppressConstructorProperties set to true :
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor(suppressConstructorProperties = true)
#EqualsAndHashCode(of = "id", callSuper = true)
public class MyClass extends MyAbstractClass {
// everything else is unchanged
Tricky one, indeed. What I think is happening is that you are generating a public all arguments constructor with Lombok. When deserializing, that is the one that Jackson will try to use. If you change your annotation for MyClass to
#AllArgsConstructor(access = AccessLevel.PRIVATE)
... it should work fine. Good luck!

How do I make Spring call a validator on a REST service?

I have studied many, many answers and I still can't see what I'm doing wrong.
I have a REST service controller which takes a bean as a parameter and I would like to validate that the fields on the bean are properly set.
The following is (a portion of) my configuration object.
#Configuration
#ComponentScan({...})
#EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
#Bean
public Validator validator() {
LocalValidatorFactoryBean validatorBean = new LocalValidatorFactoryBean();
return validatorBean;
}
#Override
public Validator getValidator() {
return validator();
}
}
I can't tell if those are necessary, but I can tell they're never called, so maybe they're in the wrong place?
The following is (a portion of) my pom.xml:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>5.1.1.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
<scope>provided</scope>
</dependency>
I don't think el is necessary for what I'm doing, but I included it in case it made a difference.
Here is (a portion of) my bean class that needs to be validated:
public class ContactInfo {
#NotNull
#Size(min=1, max=50)
private String phone;
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
And finally, this is (a portion of) the Rest Controller:
#RestController
#RequestMapping("/contact")
public class ContactController {
private static final String USER_ID_ATTRIBUTE = "userId";
#Autowired
private Validator validator;
#Autowired
#Qualifier("contactService")
private ContactService service;
#RequestMapping("/submit")
public void submitContact(
#Valid #RequestBody ContactInfo formData, BindingResult result) throws Exception {
assert(validator != null);
if(result.hasErrors()) {
throw new Exception("I don't like it one bit.");
}
service.submitContact(formData);
}
}
In the debugger, I can see that the validator passed in is an OptionalValidatorFactoryBean, and result.hasErrors() is always false.
The JSON passed in to the controller includes this:
{ "phone":"" }
I think that should trigger a validation error because it doesn't meet the minimum size.

Categories