Hnadle Any JSON definition in Spring Rest controller from a request - json

I'm handling A JSON sent in POST Request body using:
Controller
#PostMapping
public Library newLibrary(#RequestBody Library newLibrary) {
return libraryRepository.saveAndFlush(newLibrary);
}
Library model
#Column(name = "Name", nullable = false)
private String name;
JSON in Request Body
{
"name" : "testLibrary"
}
But my application won't receive definite JSON Structure in a call, for example, I receive something like this -
{
"names" : ["testLibrary","testLibrary2"],
"anyKey" : "val",
"anykey2" : {"keys":"vals"}
}
So I need to know how can I handle this situation in a single Post mapping function.

You can use #JsonAnySetter annotation from jackson library.
Inside your model:
private Map<String, Object> details;
#JsonAnySetter
public void setDetail(String key, Object value) {
if (null == details) {
this.details = new HashMap<>();
}
this.details.put(key, value);
}
And it will put extra(not defined into model) fields into the map.
Hope this helps!

Related

Remove Optional from JSON response if is not present

I am building my simple rest server with Spring boot. My problem is the following:
In one of the objects that I have to get through API, there is an Optional field:
public class MyObject {
.....
public String description;
public Optional<String> description2;
public MyObject (Object obj) {
description = obj.getDescription();
description2= Optional.ofNullable(obj.getDescription2());
}
......
}
When obj.getDescription2() is null, and retrieve it, in JSON format I got:
{
"description": "Lorem ipsum",
"description2": {
"present": false
}
}
What I have to do, to remove description2 from response if it's not present?
Thanks
you can use #JsonValue and #JsonCreator on methods that will output the data as you wish. for example:
#JsonValue
public String getDescription2() {
return desription2.orElse(null);
}

Is there a way to detect unmapped json properties when #JsonIgnoreProperties(ignoreUnknown = true) is set?

I need to detect which json fields are not mapped to the data model after PUT or POST requests.
For example:
If I post this:
{
"firstName": "test",
"lastName": "test 2",
"age": 25
}
and my model only have firstName and lastName, I want to list all unmapped fields, which in this example is "age" field.
Yes, that is possible using Jackson's annotation #JsonAnySetter
#JsonIgnoreProperties(ignoreUnknown = true)
public class DTO {
private String first;
private String last;
private Map<String, Object> unknown = new HashMap<>();
// getters/setters omitted
#JsonAnySetter
public void set(String name, Object value) {
unknown.put(name, value);
}
public Map<String, Object> getUnknown() {
return unknown;
}
}
Simple test:
#Test
public void testUnknown() throws Exception {
String json = "{\"first\":\"John\", \"last\":\"Doe\", \"age\":\"29\"}";
DTO dto = new ObjectMapper().readValue(json, DTO.class);
assertEquals(1, dto.getUnknown().size());
assertEquals("29", dto.getUnknown().get("age"));
}
If it's just about learning which properties are unmapped you may want to consider using this library: https://github.com/whiskeysierra/jackson-module-unknown-property
It logs unmapped properties for all mapped classes without a need to modify class itself.

How to map JSON with nullable array properties to POJO using WildFly RestEasy

I have JSON
{
"name": "volo",
"friends": ["joe", "alex"]
}
and Java POJO
class Person {
private String name;
private Set<String> friends;
//constructor, getters, setters
}
POST method:
#POST
#Consumes("application/json")
public Response createPerson(Person person) {
//logic
}
This is work nice when POST or PUT request are coming and JSON is parsed to POJO, but when
"friends": null
WildFly RestEasy cannot parse JSON to POJO and Response with error is returned
com.fasterxml.jackson.databind.JsonMappingException: N/A (through reference chain: dk.systematic.beacon.workspace.WorkspaceInfo["friends"])
Does anybody knows how to fix this with some annotation or additional setup?
Problem was in setter method
Before fix:
public void setFriends(Set<String> friends) {
this.friends = new HashSet<>(friends)
}
To fix just need to add validation
public void setFriends(Set<String> friends) {
if (friends != null) {
this.friends = new HashSet<>(friends)
}
}
Hopefully this will help to avoid same mistakes for others.

How to use Jackson Annotations to process JSON with random object names

I have the following JSON:
{
"animals": {
"113110": {
"id": 113110,
"name": "Dog",
.....
},
"121853": {
"id": 121853,
"name": "Cat",
.....
}
}
}
Ideally, the JSON should be as follows and implementing Jackson annotations will be trivial:
{
"animals": [
{
"id": 113110,
"name": "Dog",
.....
},
{
"id": 121853,
"name": "Cat",
.....
}
]
}
However, is there a way to use Jackson to abstract the object names so I can work with the original JSON, if anybody gets my meaning?
EDIT:
I do not know how to create my POJO. I could create an Animal class, with objects 113110 and 121853, but as these objects will always vary, how do I use Jackson annotations in my Animal class so that I can deserialize the JSON?
Thanks all, but I couldn't really understand the rest of the answers ( I don't really want to delve into Jackson, I just want to convert it to a POJO), so I found an alternative solution.
I left out a key bit of information: The JSON I posted is part of a much larger JSON object.
I ended up using Jackson's #AnySetter as I noticed that any "un-parsable" JSON data related to "animals" could be retrieved in additionalProperties defined as follows in its parent class:
public class AnimalParent {
#JsonIgnore
private Animal animal;
#JsonIgnore
private Map<String, Object> additionalProperties =
new HashMap<String, Object>();
public Animal getAnimal() {
return this.animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
Then in my main method where I parse the parent JSON, I have the following after the parsing is completed to parse the animals.
// start parsing parent JSON
...
// end parsing parent JSON
// parse animal
ObjectMapper mapper = new ObjectMapper();
if (animalParent.getAdditionalProperties() != null) {
for (Map.Entry<String, Object> item : animalParent
.getAdditionalProperties().entrySet()) {
Animal animal = mapper.convertValue(item.getValue(), Animal.class);
animalParent.setAnimal(animal);
}
It is always a bit messy when your JSON is "dynamic" as per the OPs example. The main approaches are
parse the JSON to some kind of dynamic Map-structure
parse the JSON to a tree-structure (i.e. JsonNode)
use a custom deserializer to parse the JSON and map it to a POJO
There are downsides to all of these approaches. The Map-approach offers no type safety and does not offer much functionality when it comes to traversing the object structure.
The JsonNode approach offers some nice type-methods and also some traversal methods. IMO this is a cleaner approach than the Map-approach.
The POJO-approach is type safe but a custom deserializer is required which is generally not pretty...
So, maybe the following "hybrid" approach can be of use.
// Setup the mapper
final ObjectMapper mapper = new ObjectMapper();
// Parse the json to a tree (JsonNode). This is IMO nicer than the
// Map since it exposes some nice methods for managing the
// underlying data
final JsonNode json = mapper.readTree(jsonString);
// Alt 1, use JsonNode directly
for (final JsonNode animal : json.path("animals")) {
final int id = animal.get("id").asInt();
final String name = animal.get("name").asText();
// Do stuff with name and id...
}
If the JsonNode approach feels a bit too raw then it is possible to convert the JsonNode object to a POJO without the use of a deserializer. If you assume the following POJO:
public class Animal {
private final int id;
private final String name;
#JsonCreator
public Animal(#JsonProperty("id") final int id, #JsonProperty("name") final String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
Then, this code can be used for converting to POJOs:
final ObjectMapper mapper = new ObjectMapper();
final JsonNode json = mapper.readTree(jsonString);
// Alt 2, convert to a Pojo
for (final JsonNode animal : json.path("animals")) {
final Animal a = mapper.treeToValue(animal, Animal.class);
// Handle the animal instance...
}
Finally, if the POJO still contains dynamic data you can use the following approach to handle that. In your POJO, declare the following:
private final Map<String, Object> dynamic = new HashMap<>();
#JsonAnySetter
private void set(String name, Object value) {
dynamic.put(name, value);
}
Note that it is not a must for the method to be public (i.e. it can be hidden from the outside world). This way you'll get hold of all the unknown/dynamic JSON elements.
Personally, any time I'm dealing with weird JSON that doesn't map easily to POJOs, I just do custom serialization.
I would probably make the POJOs look something like this:
public class Animal
{
String id;
String name;
}
public class JsonThing
{
List<Animal> animals;
}
Then I would implement a custom parser using the Jackson stream API. Here's a quick stub of a JsonDeserializer<JsonThing>:
public Stuff deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
.... // Start by creating a JsonThing instance and init the list.
while (jp.nextToken() != JsonToken.END_OBJECT)
{
jp.nextToken();
switch (jp.getCurrentName())
{
case "animals":
jp.nextToken(); // Skip to {
jp.nextToken(); // Skip id field
Animal a = jp.readValuesAs(Animal.class);
// Add to list
}
}
..... // Return JsonThing
}
If the keys are not known in advance then use Map instead of POJO.
Have a look at Example 1 and Example 2
You can try any one.
sample code: (using Jackson Library)
TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
ObjectMapper mapper = new ObjectMapper();
try {
Map<String, Object> data = mapper.readValue(jsonString, typeRef);
} catch (Exception e) {
System.out.println("There might be some issue with the JSON string");
}
sample code: using GSON Library
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = new Gson().fromJson(jsonString, type);
Perhaps it's just a question of combining simple Maps with pojos? Like:
public class Wrapper {
public Map<Long, Animal> animals;
}
public class Animal {
public long id;
public String name;
}
and that's it; although ids there match, maybe there is no need to try to model that dependency.

Spring #ResponseBody produces an invalid JSON for primitive types

I have a code from a REST API which uses #ResponseBody to return the result, and a MappingJacksonHttpMessageConverter to return it in a JSON format.
It all works well for complex objects.
For primitives like int, boolean and string I get a JSON which does not start with { or [.
This is not a valid JSON.
I was wondering what is the proper way to return just a simple type like that?
Should I encapsulate it in an object such as { Result : true } ?
Thanks
Code sample:
#RequestMapping(
value = "/login",
method = RequestMethod.POST)
#ResponseBody
public boolean Login(String username, String password) {
return authenticationService.authenticate(username, password);
}
This will return just true or false which is an invalid JSON. It should either be encapsulated in an object or an array (if I understand correctly).
It does just return true, or false. And you are correct that is not json.
It can't be json because its not an object, it is simply a primitive, so its fine as is - it will be assigned to a javascript variable in your success handler.
If you return a list of Booleans you get an array :
[true,false,true]
If you must have fully formed json don't return a primitive use a hashmap or custom wrapper object.
public
#ResponseBody
Map<String, Boolean> getTrue() {
Map<String, Boolean> map = new HashMap<String, Boolean>(1){{put("result", Boolean.TRUE);}};
return map;
}
Returning a hashmap is probably the simplest and best way to get the json you require :
{"result":true}
I've found convenient to use
public class ObjWrapper<T> implements Serializable {
private T obj;
public ObjWrapper(T obj) {
this.obj = obj;
}
public T getObj() {
return obj;
}
}
then in controller:
#RequestMapping("/getString") #ResponseBody
public ObjWrapper<String> getString() { ...
and on client (jquery)
$.getJson("getString", {}, function (data) {
alert(data.obj);
})
same with lists:
public class ListWrapper<T> implements Serializable {
private List<T> content;
public ListWrapper(T... objects) {
content = Arrays.asList(objects);
}
public ListWrapper(List<T> content) {
this.content = content;
}
public List<T> getContent() {
return content;
}
}