JSON respones from Jersey 1.x (1.17) with JAXB cannot be deserialized by Jackson - json

I have a jersey webservice running 1.17 and supports returning responses via both XML and JSON via the #Produces annotation. I am assuming it uses JAXB by default when returning JSON responses but I have no way to confirm it. As of now, my existing clients also use the same JAXB serializer/deserializer. I want to create a new client that uses Jackson without impacting the existing clients.
The JAXB JSON response is incompatible for Jackson for Maps. the JSON for a map using JAXB is of the form
"mapName":{"entry":[{"key":"key1","value":"value1"},{"key":"key2","value":"value2"}]}
and Jackson fails to parse this. Is there any way to make jackson parse this JSON?
Another Attempt: Switching Jersey to use Jackson
This isn't the preferred option but I tried setting "com.sun.jersey.api.json.POJOMappingFeature" to true to allow it to use Jackson for JSON Serialization/Deserialization however the service ends up returning 500s on response without logging any exceptions. the log4j logger level is set to TRACE. I enabled the ContainerRepsonseFilter to confirm 500s in the response and to my surprise, it logs the successful 2xx response. My guess is the problem occurs somewhere further down the stack but I don't know where.

I ended up with using MOXy which is able to parse the above json format.
#Provider
public class JsonMoxyConfigurationContextResolver implements ContextResolver {
private final MoxyJsonConfig config;
public JsonMoxyConfigurationContextResolver() {
final Map<String, String> namespacePrefixMapper = new HashMap<String, String>();
namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
config = new MoxyJsonConfig()
.setNamespacePrefixMapper(namespacePrefixMapper)
.setNamespaceSeparator(':');
}
#Override
public MoxyJsonConfig getContext(Class<?> objectType) {
return config;
}
}
and enabled it Jersey 2.x client using
cc.register(JsonMoxyConfigurationContextResolver.class);

Related

Extract and unmarshal JSON payload from response

I'm trying to write Citrus tests for a RESTful endpoint producing and consuming application/json content, and I'm not sure how to get my responses unmarshalled to a Java POJO (using Jackson or whatever (un)marshaller Citrus supports).
E.g. in REST-assured, I can simply write
UploadResponse response = when().post("/file").as(UploadResponse.class);
Is there an equivalent in Citrus?
I can only find examples using validate() or extractFromPayload(), which don't really cover my use case, since I don't want to operate on scalar members but embed the entire response object in the request object for the subsequent test step.
You can do something like this:
http().server(testServer)
.post("/file")
.validationCallback(new JsonMappingValidationCallback<UploadResponse>(UploadResponse.class) {
#Override
public void validate(UploadResponse payload, Map<String, Object> headers, TestContext context) {
// do something with payload object
}
});
The JsonMappingValidationCallback automatically searches for a JSON ObjectMapper in Spring bean application context. You can also provide an ObjectMapper instance as constructor arg to the JsonMappingValidationCallback.

JSONObject Alternative in Spring and Jackson

I need to pass a map back to the web application.
I'm used to encapsulating the map in a JSONObject
http://json.org/java/
But since I am using Spring and Jackson Haus.
is there an easier way to maintain the pojo? May I can just annotate the MAP ?
Jackson has com.fasterxml.jackson.core.JsonNode, and specific subtypes like ObjectNode.
These form so-called Tree Model, which is one of 3 ways to handle JSON with Jackson -- some other libraries (like org.json) only offer this way.
So you should be able to just use JsonNode instead; there is little point in using org.json library; it is slow, and has outdated API.
Alternatively you can just use java.util.Map, and return that. Jackson can handle standard Lists, Maps and other JDK types just fine.
If you need to manipulate the output, ie, you don't want to provide all the fields of the object you can use JSonArray:
#RequestMapping(value = "/api/users", method = RequestMethod.GET)
public
#ResponseBody
String listUsersJson(ModelMap model) throws JSONException {
JSONArray userArray = new JSONArray();
for (User user : userRepository.findAll()) {
JSONObject userJSON = new JSONObject();
userJSON.put("id", user.getId());
userJSON.put("firstName", user.getFirstName());
userJSON.put("lastName", user.getLastName());
userJSON.put("email", user.getEmail());
userArray.put(userJSON);
}
return userArray.toString();
}
Use the example from here
Otherwise if you add jackson to your dependencies and set the controller method anotatted with #ResponseBody the response will automatically mapped to JSON. Check here for a simple example.

How should I set a Feature on a Spring Message Converter's Object Mapper?

I'm using a 3rd party REST API which is returning 'NaN' in it's JSON response :( I can't change the response.
I'm using Spring MVC with RestTemplate and the built in Message Converters to deserialize the JSON to an Object.
I was wondering, if there is a smart way of setting the JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS property to allow for the NaN in the response.
Because I have other message converters which I do not need to configure myself I'm currently doing the following:
List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
for (HttpMessageConverter converter : converters) {
if (converter instanceof MappingJacksonHttpMessageConverter) {
ObjectMapper objectMapper = ((MappingJacksonHttpMessageConverter) converter).getObjectMapper();
objectMapper.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
}
}
This works but I don't like the fact that I'm a) iterating and b) doing the instanceof comparison just to set this property.
What would be a smarter/nicer way to do this?
Here are your options:
Instantiate and configure MappingJacksonHttpMessageConverter then set the message converters of your resttemplate to that instance. See RestTemplate.html#setMessageConverters(java.util.List). Note that this will remove all default message converters that are automatically instantiated when you instantiate a RestTemplate.
Use #JsonDeserialize. See http://dev.sghill.net/2012/04/how-do-i-write-jackson-json-serializer.html

Is it possible to get jersey to read json variables our of a request body without using a bean?

In jersey a Java bean can be auto-deserialized from within a request body but what if I want to read a parameter without creating a special type. Is it possible to do this using annotations.
My current code is:
public class RequestData {
String param;
}
...
public Response readData(RequestData data) {
data.getParam();
...
}
I want it to be something like:
public Response readData(#RequestParam("param") String param) {
...
}
If its not already clear the input JSON is:
{
"param":"some value"
}
The type of your input JSON is Map<String, String> so if you want to have undifferentiated input you could use that as your request parameter and read the values that you require.
Note that #RequestParam looks at the request parameters and not the body, so it's a different beast.
You do this by letting Jersey pass you String as is (as per annotations), and then data-bind it using Jackson ObjectMapper (thing Jersey uses internally for JSON binding):
Map<String,Object> map = objectMapper.readValue(param, Map.class);
to get access to ObjectMapper, you can use JAX-RS injection annotation (#Context I think?) in the resource class:
#Context
private ObjectMapper objectMapper;

How to get a JSON object and map it into object in spring 3.0 controller using POST method?

I want to add request mapping my controller which gets a Json of Array of an object.
The JSON will be sent from javascript using post.
Thanks,
Kfir
You can do this using Spring's #RequestBody annotation, e.g.
#ResponseBody
public Map<String, Object> handleJsonPost(#RequestBody Map<String, Object> requestJson) {
...
return responseJson;
}
This will also send the return value back as JSON.
You'll also need to include the Jackson library in your application's classpath, and add
<mvc:annotation-driven/>
to your context (see docs)