Strange behaviour in jackson ObjectMapper in Spring MVC application with #JsonIdentityInfo - java

I'm a bit confused about those things I currently see in our Spring MVC application and I hope someone can tell me what we're making wrong. Since google doesn't have an answer for me I'm sure we're making wrong.
We've a simple Spring 4.1.5.RELEASE application. Nothing special to mention about it. There is a MappingJackson2HttpMessageConverter configured to deliver JSON to the clients in RestControllers via Jackson ObjectMapper.
All entities which get serialized via jackson have the following annotation on tpe level
#JsonIdentityInfo(generator = IntSequenceGenerator.class, property = "#jsonObjectId")
Now the strange part:
There is a spring integration test which does the following (objectMapper gets autowired from the web application context, it is the same instance which converts the request paylod)
objectMapper.writeValueAsString(dummy) // generate json from dummy object
mockMvc.perform(post(...)) // make a post request with JSON payload generated
evaluate reponse
Executing the test once is okay. If I copy that test case and execute it twice then I get a 400 from the server back saying IllegalArgumentExecption -> Already had POJO for id ... Problem can be reproduced in real world application so it seems to be no test problem.
I've figured out, that on serialization process the IntSequenceGenerator which is in charge of generating the "jsonObjectId" properties gets created every time an object needs to be serialized. Ok so far. But the corrresponding counterpart, in our case the default used SimpleObjectIdResolver from jackson is always the same instance. So the second server request (the second deserialization process) leads to that exception in SimpleObjectIdResolver.
I would be very thankful if anybody could give me a hint, what we're making wrong ...

Forgot to mention: Jackson-Core 2.4.1 was used. We now upgraded to version 2.7.0 of jackson and the SimpleObjectIdResolver now gets recreated for every deserialization. This seemed to be a bug in jackson before 2.7.0.
Edit: I was able to reduce the issue to the following small test:
public class DummyTest {
private Dummy dummy = new Dummy();
#Before
public void setUp() {
dummy.setFoo("Foo");
}
#Test
public void testApp() throws Exception {
ObjectMapper mapper = new ObjectMapper();
String dummyAsJsonString = mapper.writeValueAsString(dummy);
mapper.readValue(dummyAsJsonString, Dummy.class);
mapper.readValue(dummyAsJsonString, Dummy.class);
}
}
where Dummy.class is defined as
#JsonIdentityInfo(generator = IntSequenceGenerator.class, property = "#jsonObjectId")
public class Dummy {
private String foo;
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}
You will get an IllegalStateException when executing it with jackson 2.4.1

Related

How to POST a Java POJO / Json to a Play Framework v2.2.1 Controller Http POST Method

I am writing an API using Java play framework.
I would like to post a Json via a rest client like Postman, while testing the Play controller.
Version of Java Play = 2.2.1
Controller method:
public static Result submitDetails(DetailsVO detailsVO) throws Exception
Routes config
POST /submit controllers.application.ProcessDetails.submitDetails(detailsVO:DetailsVO)
Below is my POJO
import java.io.Serializable;
import java.util.List;
public class DetailsVO implements Serializable {
public DetailsVO(){}
private String firstName;
private String middleName;
private List<Details> detailsList;
//Setter and getter for first name and last name
public List<Details> getDetailsList() {
return detailsList;
}
public void setDetailsList(List<Details> detailsList) {
this.detailsList = detailsList;
}
}
After adding the routes entry, I am getting this error "Cannot resolve reference"
Please, could some advice on how to define a post method that takes a Java POJO in the routes?
I dont think it is possible to use your VO directly in play for java. You need to use the play request type. And then from the request you get the body as JSON. You can use any JSON parser for this. Play has one, but you can use whatever you like. Your route would look like this:
POST /submit #controllers.HomeController.submitDetails(request: Request)
Remove the static from your controller function, and let it take the Http.Request instead of the VO.
public Result submitDetails(Http.Request request) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode json = request.body().asJson();
DetailsVO dvo = objectMapper.convertValue(json, DetailsVO.class);
return ok(json.toString());
}
For this example i used the Jackson JSON parser. For this particular solution to work you also need your fields to be public, because the parser does not work for the private fields. I would suggest using the play documentation on their website. It can be found here: https://www.playframework.com/documentation/2.8.x/JavaHome
I would suggest trying scala when you're trying out the play framework, it makes for much cleaner code with this framework.

Strings in OSGI Enroute DTOs are 'compressed'

My current task is to build an application using OSGI Enroute (http://enroute.osgi.org/) and Angular (though we elected to use Angular2/4 instead of the bundled AngularJS).
So far so good. I have a REST Java application which is responding to various requests from the Angular front-end but I'm currently running into an issue. In order to make development easier I am serving the Angular code on port 4200 and the back-end is listening on port 8080. CORS is working so I am able to send and receive requests while building the code. This may or may not be related to the issue.
The issue is when responding with a DTO with String content in excess of 21 characters the value is getting 'compressed.' I noticed this when attempting to use the value I received (a UUID) as a key for a subsequent GET request. Checking the DTO class I have confirmed that the toString() method does indeed call a private compress method where it will take any string longer than 21 characters and return something akin to this nine...last nine which tends to make it difficult to re-obtain a UUID from ... {"uuid":"95b90155-...ee5c02200", "name":"My Object"}...
So ... given something like this:
import org.osgi.dto.DTO;
public final class MyDTO extends DTO
{
public String uuid;
public String name;
}
and a REST application like this:
#RequireBootstrapWebResource(resource="css/bootstrap.css")
#RequireWebserverExtender
#RequireConfigurerExtender
#Component(name="web", propery={"debug=true"})
public final class MyApplication implements REST
{
private boolean debug = false;
public MyDTO getMe(RESTRequest request)
{
MyDTO dto = new MyDTO();
dto.name = "My Object";
dto.uuid = UUID.randomUUID().toString();
return dto;
}
#SuppressWarnings("unused")
#Activate
void activate(ComponentContext component, BundleContext bundle,
Map<String, Object> config)
{
if ("true".equals(config.get("debug"))
{
debug = true;
}
}
}
what am I missing in order to avoid this value 'compression' in my JSON responses?
Things I have tried
(The one that works) overriding the toString() method provided by DTO. This works but doesn't seem like it is the best solution. I would then have to override the toString() for anything that might have a string value in excess of 21 characters. The documentation indicates that the intent is for debugging, which likely means I'm not returning the proper type?
Setting the request's _response()'s content type to application/json: the result I see in the Chrome Web console is still a compressed string
I wrote the DTO.toString methods. It is clearly documented that the format of the output is not specified and that it is for use as a debugging tool and not for serialization. This is is why the impl "compresses" strings.
If you need to serialize a DTO, you need to use code for that purpose. See https://github.com/osgi/osgi.enroute/blob/master/osgi.enroute.base.api/src/osgi/enroute/dto/api/DTOs.java for an API that can convert DTOs to a format like JSON.

Prevent Jackson unmarshaller from throwing exception on first datatype mismatch

java newb here. Probably this has been covered somewhere before, but I just can't seem to find it.
RESTish service on Java 8 + Spring 4 + Jackson 2. What I want to do, is unmarshal JSON into an object. It works just fine with minimal setup:
public class A {
private int id;
private double val;
getters/setters
}
Then I have my restcontroller:
#RestController
#RequestMapping(value = "/item/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public class MyController {
public A test(#Valid #RequestBody A a) {
return a;
}
}
If I post valid JSON
{"id": 1, "val":5.55}
all is dandy. If I post
{"id": cat, "val":5.55}
by default I get response of 400. I can define #ExceptionHandler to deal with HttpMessageNotReadableException, so it would return some sort of descriptive JSON back, pointing out the mistake.
Problem is, that if I get more than one mistake in JSON, such as:
{"id": cat, "val":"dog"}
unmarshaller will throw exception on first node and stop parsing, requiring the client to fix issues one-by-one and reposting them, just to get next error response. It might not be an issue in this particular case, but it becomes very uncanny when dealing with larger datasets. So I would like to somehow convince lord Jackson to parse whole thing before throwing exception.
Suggestions?
You can use custom deserializer
Or you can validate incoming JSON data inside a REST service!!
As explained here

ExceptionMapper not invoked if receiving invalid JSon

I am using Jersey 2.5.1 with Jackson 2.2. for building JSON Rest web services. I kind of got it up and running also with 2 ExceptionMappers but for a "not-json" request the application is not throwing any exception!
ExceptionMapper
Will be invoked if e.g. a NullPointerException is thrown
ExceptionMapper
Will be invoked if there s a problem with the JSon Mapping
My Problem:
1. Request Body: {} works
2. Request Body: {} with an application side NullPointer invoked the first exception mapper
3. Request Body: "jibberish" does not invoke anything (not caught by any ExceptionMapper) cause no Exception is thrown. Unfortunately the response body is sth like : Unrecognized field "xxx" (class com.sample.MyDto), not marked as ignorable (9 known properties...
....> but I want to customize the error msg since I am always returning a JSon object.
Ok, I solved it. What finally made me think the right way was, that sometimes it worked so it had to be some overwrite class loading issue.
In my ApplicationClass I registered Providers based on packages like this
#ApplicationPath("resources")
public class JerseyApplication extends ResourceConfig {
public JerseyRestApplication() {
packages("com.sample", "com.fasterxml.jackson.jaxrs.json");
}
}
If you look sharp you might see the error! I am putting "com.fasterxml.jackson.jaxrs.json" on the search path because I want to use the JacksonJsonProvider.class. What I did not know is, that in that package there are also some JSonMappers for JSonParseException etc. and those do not log :)
So sometimes the application was loading mine first and sometimes the one from jackson.
So I am going to stick with the following now:
#ApplicationPath("resources")
public class JerseyRestApplication extends ResourceConfig {
public JerseyApplication() {
register(JacksonJsonProvider.class); // Do it manually
packages("com.sample");
}
}
Hope that helps sb :D
I also found sometime coustom ExceptionMapper not work, and it's not always work or not work.
so i debug the jersey's source code .
class: org.glassfish.jersey.server.ServerRuntime, methed:mapException
...
final long timestamp = tracingLogger.timestamp(ServerTraceEvent.EXCEPTION_MAPPING);
**ExceptionMapper mapper = runtime.exceptionMappers.findMapping(throwable);**
if (mapper != null) {
request.getRequestEventBuilder().setExceptionMapper(mapper);
...
if the mapper is null, the coustom ExceptionMapper will be not work.
class: org.glassfish.jersey.internal.ExceptionMapperFactory methed: ExceptionMapperFactory
the Exception Mapper :(there two Exception mapping one same Exception: java.lang.Exception)
org.glassfish.jersey.server.mvc.internal.ErrorTemplateExceptionMapper#6473fc2,class
java.lang.Exception
...
com.baidu.ssp.web.ws.exception.BaseExceptionMapper#7a84639c,class
java.lang.Exception
it's because in MvcFeature:
#Override
public boolean configure(final FeatureContext context) {
final Configuration config = context.getConfiguration();
if (!config.isRegistered(ErrorTemplateExceptionMapper.class)) {
context.register(ErrorTemplateExceptionMapper.class);
context.register(new MvcBinder());
return true;
}
return false;
}
the ErrorTemplateExceptionMapper is also add to ExceptionMapper.
so i change my custom MapperException's genericity type :
ExceptionMapper
to
ExceptionMapper
may be my resolution is not fit for you ,
the main problem is the ExceptionMapper.

How do I disable fail_on_empty_beans in Jackson?

Using jackson 2.1, how do I disable the fail_on_empty beans that the error message seems to want me to disable?
I'm assuming this is just the simplest thing in the world, but hell it is late and I haven't been able to find a simple tutorial or anything particularly obvious from the api. SerializationFactory? Why would they make it so unintuitive and then make the error message seem so straightforward?
Although I do like the error message, I mean, it is better than an NPE.
I'm assuming there is a way to do this using annotations - but I'm not keen on using them at all for the simplistic work I'm doing!
You can do this per class or globally, I believe.
For per class, try #JsonSerialize above class declaration.
For a mapper, here's one example:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
// do various things, perhaps:
String someJsonString = mapper.writeValueAsString(someClassInstance);
SomeClass someClassInstance = mapper.readValue(someJsonString, SomeClass.class)
The StackOverflow link below also has an example for a Spring project.
For REST with Jersey, I don't remember off the top off my head, but I believe it's similar.
Couple of links I dug up: (edited 1st link due to Codehaus shutting down).
https://web.archive.org/web/20150513164332/https://jira.codehaus.org/browse/JACKSON-201
Jackson serializationConfig
If you are using Spring Boot, you can set the following property in application.properties file.
spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false
If you wish to get JSON object without any extra fields - please add this annotation to your class, it worked perfect for me.
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
You can also add in your application.properties file this row, but it will add an extra field to your JSON.
spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false
You can also get the same issue if your class doesn't contain any public methods/properties. I normally have dedicated DTOs for API requests and responses, declared public, but forgot in one case to make the methods public as well - which caused the "empty" bean in the first place.
You can also probably annotate the class with #JsonIgnoreProperties(ignoreUnknown=true) to ignore the fields undefined in the class
In Jersey Rest Services just use the JacksonFeatures annotation ...
#JacksonFeatures(serializationDisable = {SerializationFeature.FAIL_ON_EMPTY_BEANS})
public Response getSomething() {
Object entity = doSomething();
return Response.ok(entity).build();
}
T o fix this issue configure your JsonDataFormat class like below
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
which is almost equivalent to,
mapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
I don't fully understand the reason behind this exception, but for Spring Boot projects adding the following to the properties file works a treat
application.yml
spring:
jackson:
serialization:
FAIL_ON_EMPTY_BEANS: false
application.properties
spring.jackson.serialization.FAIL_ON_EMPTY_BEANS = false
In my case, I missed to write #JsonProperty annotation in one of the fields which was causing this error.
If you use org.codehaus.jackson.map.ObjectMapper, then pls. use the following lines
mapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
Adding a solution here for a different problem, but one that manifests with the same error... Take care when constructing json on the fly (as api responses or whatever) to escape literal double quotes in your string members. You may be consuming your own malformed json.
ObjectMapper mapper = new ObjectMapper();
Hi,
When I use mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
My json object values come '' blank in angular page mean in response
Solved with the help of only below settings
mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker().
withFieldVisibility(JsonAutoDetect.Visibility.ANY).withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
If it is a Spring App, just paste the code in config class
#Bean
public ObjectMapper getJacksonObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
objectMapper.configure(
com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return objectMapper;
}
I get the same error when I return InvoiceDTO as a response to the client. The problem was when I used the bean (Customer) in the DTO (InvoiceDTO). So, to solve I changed the bean (Customer) to DTO (CustomerDTO) to disable serialization and it is worked fine.
In my case I didnt need to disable it , rather I had to put this code on top of my class : (and this solved my issue)
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)//this is what was added
#Value //this was there already
#Builder//this was there already
public class NameOfClass {
//some code in here.
}

Categories

Resources