I'm using the latest snapshot spring-data-rest-webmvc:2.0.0-SNAPSHOT.
When Jackson tries to serialize PersistentEntityResource to JSON, it goes into an infinite loop on the "persistentEntity" property. I understand the property is supposed to be ignored, but it's not.
public class PersistentEntityResource<T> extends Resource<T> {
#JsonIgnore private final PersistentEntity<?, ?> entity;
...
public PersistentEntity<?, ?> getPersistentEntity() {
return entity;
}
}
Maybe Jackson sees the getter that doesn't match the name of the field and decides to serialize? Or do I have some configuration wrong?
This seems to be an error reported here:
https://jira.springsource.org/browse/DATAREST-117?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
I solved the problem by making sure that I don't have another bean in the spring context called "objectMapper". It was a default Jackson ObjectMapper that was overwriting the custom one configured in RepositoryRestMvcConfiguration.
Related
I have a Spring Boot app using Jackson. I'm not using Jersey just regular Spring MVC. I have a Wrapper Request class:
public class WrapperRequest {
#NotNull
private final Object obj; // some object that corresponds with a JSON object
#JsonCreator
public WrapperRequest(#JsonProperty("wrapper") final Object obj) {
this.obj = obj;
}
public Object getObj() {return obj}
}
The JSON for this would look like:
{
"wrapper":{
//Object data
}
}
The #NotNull from javax isn't working as I excepted. The way I want it to work is that if the consumer sends in a JSON that have a typo like:
{
"wrapperr":{
//Object data
}
}
Jackson will not map my wrapper class because the key in the JSON doesnt match the JsonProperty i.e ("wrapper") (so Object will be null and then I will get NPE later on if I tried to interact with Object. Am I using Jackson wrong? i.e Jackson maps things that it knows about and the rest is null or am I not using the #NotNull annotation correctly?
Jackson is not aware of Bean Validation annotations like #NotNull. For the specific case of "null", you can mark the constructor parameter as #JsonProperty(required = true). More generally, you can use #NotNull on a property as you did and mark your MVC controller parameter with #Valid (which will not cause deserialization to fail but will cause Spring MVC to return a 400 if the validation fails).
Note that you may also be interested in the UNWRAP_ROOT_VALUE feature, which would allow you to eliminate the need for the wrapper class in this particular case.
I am building a REST service application with Spring and Jersey.
The "Response Content Type" can be either JSON or XML, which is why I use JAXB annotations on the fields of my POJO, which works great so far. No configuration was needed to get this to work, I just put the required .jars on the classpath.
Now, I want to add custom behaviour for (de-)serialization of Java 8 Date/Time objects and register the JavaTimeModule from Jackson.
Where would be the right place to register the Module? I know the code snippet is supposed to look something like this:
ObjectMapper tmpMapper = new ObjectMapper();
tmpMapper.registerModule(new JavaTimeModule());
But where do I actually put this code?
The place that comes to mind is my ResourceConfig class from Jersey. It looks like this:
#ApplicationPath("/rest")
public class ApplicationResourceConfig extends ResourceConfig {
public ApplicationResourceConfig() {
// Not needed because since jersey 2.9 JacksonFeature implements AutoDiscoverable
// register(JacksonFeature.class);
register(WadlFeature.class);
// Registers our own request listener for monitoring purposes
register(RestServiceApplicationEventListener.class);
// Registers the package that contains our REST resources
packages(PingResource.class.getPackage().getName());
}
What I did now was to add the following lines at the bottom of the constructor:
ObjectMapper tmpMapper = new ObjectMapper();
tmpMapper.registerModule(new JavaTimeModule());
JacksonJaxbJsonProvider tmpProvider = new JacksonJaxbJsonProvider();
tmpProvider.setMapper(tmpMapper);
register(tmpProvider);
The DateTime conversion now worked. However, some of the the JAXB behaviour changed which caused the deserialization of other beans to break. Here is what is not working anymore:
I have super class which defines a getter of a field:
public abstract class SuperClass {
#XmlElement(name = "yourField")
public abstract String getMyField();
}
(This maps the internal name of the field to different name which is exposed to the outside)
public class SubClass {
private String myField;
#Override
public String getMyField() {
return myField;
}
}
This mechanism used to work before and I could e.g. define a POST parameter of type SubClass calling the method with a JSON snippet that looks like this:
{
"yourField" : "hello world"
}
When trying this now I get the exception:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "yourField" (class com.myProject.SubClass), not marked as ignorable (1 known property: "myField"])
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#728b1889; line: 4, column: 14] (through reference chain: com.myProject.Subclass["yourField"])
Note however, that the basic JAXB features still work, only in this particular superclass/subclass example.
Could it be that explicitly registering a JacksonJaxbJsonProvider somehow altered the default behaviour on how to deal with JAXB annotated beans? Is there a way to maybe retrieve the existing JacksonJaxbJsonProvider (that must have been registered implicitly somehow) or the existing ObjectMapper object and register the JavaTimeModule to it instead of creating a new Mapper object and a new Provider object?
I am building an application providing a JAX-RS REST service using JPA (EclipseLink). When exposing User entities over JSON, I am using the #XmlTransient annotation on some fields (e.g. the password field) to hide them from the JSON representation. When sending a create or update (POST/PUT) operation, I would like to populate the missing fields again so JPA will correctly perform the operation.
My current approach is that I have a custom JsonDeserializer that is used to deserialize the User and to add the missing fields. For this I would like to inject (using #Inject) a UserFacadeREST bean which handles the JPA-stuff. However, this injection fails and the bean instance is null (which then of course causes a NullPointerException).
My UserFacadeREST bean is annoted as follows:
#Stateless
#LocalBean
#Path(UserFacadeREST.PATH)
public class UserFacadeREST extends AbstractFacade<User> {
//...
}
My UserDeserilizer (custom JsonDeserializer):
public class UserDeserializer extends JsonDeserializer<User> {
#Inject
private UserFacadeREST userFacade;
#Override
public User deserialize(JsonParser parser, DeserializationContext context) throws IOException,
JsonProcessingException {
JsonNode node = parser.getCodec().readTree(parser);
int userId = (Integer) ((IntNode) node.get("userID")).numberValue();
System.out.println(userId);
User user = userFacade.find(userId); // This line produces the NullPointerException
return user;
}
}
which I then use on my User entity with #JsonDeserialize:
#Entity
#Table(name = "User")
#XmlRootElement
#JsonDeserialize(using = UserDeserializer.class)
public class User implements Serializable {
// ...
}
I have included a bean.xml file in my WEB-INF folder with bean-discovery-mode set to all. What am I missing?
Jon Peterson pointed me to the right direction. I finally chose to implement the 'hackish' solution, in a way. Please note that there are basically 2 options here (if you know another one, please let me know!). Short version:
Hackish solution (the solution I chose): inject a bean programmatically using javax.enterprise.inject.spi.CDI.current().select(UserFacadeRest.class).get() as described in the accepted answer of the question mentioned by Jon or
Better (clean) solution (but also more elaborate): Redesign the logic to fill the missing fields after deserialization as suggested by Jon.
So for my question, the solution looks as follows:
1.
import javax.enterprise.inject.spi.CDI;
public class UserDeserializer extends JsonDeserializer<User> {
private final UserFacadeREST userFacade =
CDI.current().select(UserFacadeREST.class).get();
// Rest as before
}
2. In this case, in the deserialize method of my JsonDeserializer I would construct a User that just holds the userID. In every request method I would then have to examine all the users and replace them by the actual user by calling EntityManager.find(User.class, user.getUserID()). This means more effort in the business logic as you have to keep in mind that everytime you need to work on a User in a request method, you first have to do a query to get the 'full' User object. In the first solution, this query is hidden from the business logic and already happens in the JsonDeserializer.
public class UserDeserializer extends JsonDeserializer<User> {
#Override
public User deserialize(JsonParser parser, DeserializationContext context) throws IOException,
JsonProcessingException {
JsonNode node = parser.getCodec().readTree(parser);
int userId = (Integer) ((IntNode) node.get("userID")).numberValue();
return new User(userId); // Placeholder User object containing only the user ID, needs to be replaced in business logic
}
}
I'm not super familiar with CDI, but some quick Google'ing leads me to believe that bean-discovery-mode should either be all, annotated, or none (true not being a valid value). Reference
If that doesn't fix it, it might be the same issue that Spring would have: you have to declare your UserDeserializer as a bean for the dependency injection to be applied.
EDIT: Just found this other question that is basically the same issue you are having.
Ultimately, you probably need to just redesign the logic to call userFacade after deserialization.
I have the following situation
public class MyCustomForm {
private MyCustomType a;
private MyCustomType b;
}
#RestController
public class AController {
#RequestMapping(...)
public void myMethod(#RequestBody MyCustomForm form){
...
}
}
I want to send in a POST request the necessary data to fill the form. The problem is that MyCustomType is a complex data type and cannot be deserialized from JSON.
The first thing I tried was to write a PropertyEditor so that Spring will know how the make the deserialization from a string. This solution works if I use anything else beside #RequestBody (it works with #PathVariable for example).
I made some research and the reason why #RequestBody is not working is because this annotation generates a proxy which uses its own deserialization rules. Those rules do not interfere with custom PropertyEditors.
The next thing I tried was to use a custom Converter. This solution still didn't solved the issue.
Any other ideas?
I understood that the newest version of jackson (version 2) will know about the custom Converters or PropertyEditors but updating my jackson mapper is not really a solution in my case.
You can use #JsonDeserialize for your MyCustomType classes like
public class MyCustomForm {
#JsonDeserialize(using = MyCustomTypeDeserializer.class)
private MyCustomType a;
#JsonDeserialize(using = MyCustomTypeDeserializer.class)
private MyCustomType b;
}
Some references:
https://fasterxml.github.io/jackson-databind/javadoc/2.3.0/com/fasterxml/jackson/databind/annotation/JsonDeserialize.html
http://www.davismol.net/2015/06/05/jackson-using-jsonserialize-or-jsondeserialize-annotation-to-register-a-custom-serializer-or-deserializer/
http://www.baeldung.com/jackson-custom-serialization
I am trying to add validation to my model objects (which double as my form backing beans) using javax.validation annotations.
My model looks like this:
public Class TestObject {
private String myProp;
private InnerObject innerObject;
//getters and setters omitted
}
public Class InnerObject {
private BigDecimal myValue;
#NotNull
public BigDecimal getMyValue();
}
In my controller I have the method call like this:
public View calculate(#ModelAttribute("testObject") #Valid TestObject testObject, BindingResult result)
I also have the <mvc:annotation-driven/> in my spring-servlet.xml file.
Everytime I run the form with a null value it tells me there are 0 binding result errors.
I am using Java 1.6 with Hibernate-Validator-4.2.0 and Validation-API-1.0.0 on my classpath.
Can anyone help me and let me know what I am doing wrong? Been playing around with this for a while and can't get it to work.
Thanks
You are not referencing your InnerObject class. Your controller is taking a TestObject, but the field "innerObject" of the class TestObject is of type String.
Ok, getting a little further with this. I noticed that I was still using a custom validator in my controller like this:
binder.setValidator(new CustomValidator());
So I removed it and then I added #Valid above the getter on my inner object like:
#Valid
public InnerObject getInnerObject();
Now I can see that there are binding errors in my code. I have a new problem though. I have #NotNull on a BigDecimal property and I get the following error
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.validation.UnexpectedTypeException: No validator could be found for type: java.math.BigDecimal
From the documentation it looks like BigDecimal is supported. So not sure what is going on. Think I am getting closer though.
Ok to resolve the BigDecimal error I changed:
private BigDecimal myField;
to
private BigDecimal myFeild = BigDecimal.Zero;
It works properly now.
The only downside is that since these are my form backing objects a zero is displayed in the field initially instead of a blank.
I am not sure if there is any way around this?