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

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

Related

How to configure spring boot for using klaxon library

There is a klaxon library - JSON parser for kotlin
How to configure Spring Boot for using it to make a REST API in this way:
#RestController
class SampleController {
#RequestMapping("/test", method = [RequestMethod.POST])
fun test(#RequestBody body:JsonObject): JsonObject {
//work with body val (KLAXON object)
//return KLAXON object
}
}
#RequestBody body:JsonObject - is a Klaxon object, so we do not want to use standard Jackson2ObjectMapperBuilder for RequestBody. For simplicity we do not want to use it for Response body too.
Post body is some kind of dynamic data, so I want to use a Low level API in lib, not a Object binding API.
No, at the moment it's not possible.
Reference

No Suitable MessageConverter

I have a spring boot application and am testing integration test. My REST service produces JSON and I can confirm it when testing it in postman.
But when I make a getForObject call by restTemplate:
#Test
public void testGetObject() {
Pet pet = restTemplate.getForObject("http://localhost:9000/pets/10000", User.class, Collections.emptyMap());
assertThat(pet.getName(), is("Bobby"));
}
It fails with following error:
Could not extract response: no suitable HttpMessageConverter found for response type [class petstore.entity.User] and content type [text/html;charset=utf-8]
I read lots of posts in stackoverflow, and having that restTempalte itself has MappingJackson2HttpMessageConverter as one of default converters which has JSON as default media type then I should not get this error.
Is there anything I am missing here?
Well, the message is pretty indicative - you're getting text/html as a response type, make your endpoint return application/json. If you're using Spring MVC then you can do it by adding the produces parameter to the annotation:
#RequestMapping(value = "/pets/{id}", method = RequestMethod.GET, produces = "application/json")
Or, if you're using Jersey, add this annotation to your method:
#Produces("application/json")

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.

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

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);

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.