How to unmarshall json lists using Spring Boot RestTemplate - java

I have to parse a REST response in json and it has a lot of nested lists with many objects.
The response contains an item called "ObjectList" which has a list and inside, two elements, "ObjectA" and "ObjectB". I don't know how to parse the response to objects using Jackson annotations.
The json looks like this:
"ObjectList": [
{
"ObjectA": {
"property1": false,
"property2": true
},
"ObjectB": {
"property1": 66,
"property2": true
},
{
"ObjectA": {
"property1": false,
"property2": true
},
"ObjectB": {
"property1": 66,
"property2": true
}
}
]
}
My code looks like this
ResponseEntity<Response> response = restTemplate.exchange(URL, HttpMethod.GET, request, Response.class);
Response response = response.getBody();
Response is:
#JsonIgnoreProperties(ignoreUnknown = true)
public class TimesheetListResponse {
#JsonProperty("ObjectA")
private List<ObjectA> objectAList;
#JsonProperty("ObjectB")
private List<ObjectB> objectBList;
That does not work at all, and I'm confused about how to map this.

According to your requirement the model structure may look like below. Within the objectList map in Response object, you need to add HashMap with keys as "ObjectA"/"ObjectB" string and value as instance of ObjectA/ObjectB. I have taken value type of Map as Object, so that any object type A/B can fit in there. Add corresponding #JsonXXX annotations.
public class Response {
private List<Map<String,Object>> objectList;
//Getters & Setters
}
public class ObjectB {
String propB1;
String propB2;
}
public class ObjectA {
String propA;
String propA1;
}

I also would consider the entry in the list as another wrapper object that can either ObjectA or ObjectB. I.e.
#JsonIgnoreProperties(ignoreUnknown = true)
public final class Parent {
#JsonProperty("ObjectList")
private List<ChildWrapper> objectList = new ArrayList<>();
}
#JsonIgnoreProperties(ignoreUnknown = true)
public final class ChildWrapper {
#JsonProperty("ObjectA")
private Child ObjectA;
#JsonProperty("ObjectB")
private Child ObjectB;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public final class Child {
#JsonProperty("property1")
private int property1;
#JsonProperty("property2")
private boolean property2;
}

It seems that the mapping was fine, I only had to initialize the Arraylist. The main issue was that the endpoint was returning empty because of a parameter that I forgot.

Related

How to ignore JSON properties similar to #JsonIgnore in Deserialization

I have a JSON object similar below:
[
{
"objA": {
"propA": "AAAA",
"propB": "BBBB",
"objB": {
"objC": {
"propC": "CCCC",
"propD": "DDDD"
}
},
"objD": [
"asa"
],
"propE": "AW",
"propF": "533",
"propG": "ABW",
"propH": "ARU",
"objE": {
"objF": {
"propI": "SASDS",
"propJ": "54DEFF"
}
}
}
}
]
When I deserialize this JSON into a List, I would like to do for part of this object, for example: I would like to ignore objB, objC, objD, objE and objF.
To do that I has been used the #JsonIgnore annotation. So I did something like that:
public class MyClass {
// objects and properties not ignorabled
private ClassA objA;
private String propE;
private String propF;
private String propG;;
private String propH;
// objects ignorabled in deserialization
#JsonProperty("objB")
#JsonIgnore
private Object objB;
#JsonProperty("objD")
#JsonIgnore
private Object objD;
#JsonProperty("objE")
#JsonIgnore
private Object objE;
/** gets and setters here **/
Follow below the piece of code that deserialize my JSON
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(url, new TypeReference<List<MyClass>>(){});
This code is working. this code is ignoring the objects from JSON, but I believe there are some another way to do that instead of use #JsonIgnore to each object or property in my entity.
Do you know how can I do that better?
Do you want to avoid using # notations ?
If not, have you tried using a filter like #JsonFilter("myFilter") in Jackson ? As described here: https://www.baeldung.com/jackson-ignore-properties-on-serialization
See also https://www.tutorialspoint.com/jackson_annotations/jackson_annotations_jsonfilter.htm
So you would have to write:
#JsonFilter("myFilter")
public class MyClass { ... }
in your class. Then do something like:
SimpleBeanPropertyFilter objBFilter = SimpleBeanPropertyFilter
.serializeAllExcept("objB");
SimpleBeanPropertyFilter objDFilter = SimpleBeanPropertyFilter
.serializeAllExcept("objD");
SimpleBeanPropertyFilter objEFilter = SimpleBeanPropertyFilter
.serializeAllExcept("objE");
FilterProvider filters = new SimpleFilterProvider()
.addFilter("objBFilter", theFilter)
.addFilter("objDFilter", theFilter)
.addFilter("objEFilter", theFilter);

Mapping GraphQL response to class in Java/Spring

I'm having some trouble mapping a GraphQL response to a class and I'm probably missing something obvious so I need a second pair of eyes on my code I think.
I have a response that looks like this:
{
"data": {
"Area": [
{
"id": "1"
},
{
"id": "2"
}
]
}
}
My consumer and client looks like this:
public class Consumer {
private final WebClient webClient;
private static final String QUERY = "query { Area { id }}";
public Consumer(WebClient webClient) {
this.webClient = webClient;
}
public AreaResponse.AreaData getAreas() {
var request = new GraphQLRequest(QUERY, null);
var res = webClient.post()
.bodyValue(request)
.retrieve()
.bodyToMono(AreaResponse.class)
.block();
return res.getData();
}
}
And finally my response dto:
#Data
public class AreaResponse {
private AreaData data;
#Data
public class AreaData{
private Collection<Area> Area;
#Data
public class Area{
private String id;
}
}
}
If I map the response to String.class I get the same data as in Postman so there's nothing wrong with the query or the endpoint. But when I try to map to AreaResponse the Area object is null. I am not sure if the hierarchy in my response class is correct or if I should move the Collection a step down?
I think the problem comes from the use of upper-case Area in the response. By default, Jackson will try to map all of the stuff to a lower-case version of the class name (or lower-camelcase).
One way to fix it:
#JsonProperty("Area")
private Collection<Area> Area;
P.S. nested/ inner classes can be static, so public static class AreaData etc.

Jackson: Deserialize JSON array from JSON object into Java List

I have been stumbled by this for a while. I have a Spring application and would like to parse the following JSON:
{
"metadata": {...}
"response": {
"objects": [
{
"name": "someName",
"properties": [<array_of_properties>]
},
...
]
}
}
into a list of the following Java objects:
public class MyClass {
String name;
List<CustomProperties> customProperties;
}
Meaning, I want to extract only the objects array and parse only that. I have tried using a custom deserializer and that works, but I had to do:
#JsonDeserialize(using=MyDeserializer.class)
public class MyClassList extends ArrayList<MyClass>{}
and then:
ObjectMapper objectMapper = new ObjectMapper();
List<MyClass> list = objectMapper.readValue(json, MyClassList.class)
Is there anyway to avoid extending ArrayList, since currently I am doing that in order to be able to access the .class property.
you can define your json structure with a couple of classes
public class MyJson {
private MyResponse response;
...
}
public class MyResponse {
private List<MyClass> objects;
...
}
public class MyClass {
String name;
List<CustomProperty> customProperties;
...
}
than you can use Jackson to parse the json string to MyJson class, no special #JsonDeserialize is needed
ObjectMapper objectMapper = new ObjectMapper();
MyJson myJson = objectMapper.readValue(json, MyJson.class);
List<MyClass> list = myJson.getResponse().getObjects();
Keep in mind, this code is only a draft, all classes should have setters (and getters) and some null checks are required
You can do something like this. I feel this would be cleaner
#JsonIgnoreProperties(ignoreUnknown = true)
class Wrapper{
private Response response;
//setters, getters
}
#JsonIgnoreProperties(ignoreUnknown = true)
class Response{
private List<MyClass> objects;
//setters, getters
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyClass {
String name;
List<CustomProperties> customProperties;
//setters, getters
}
ObjectMapper objectMapper = new ObjectMapper();
Wrapper wrapper = objectMapper.readValue(json, Wrapper.class)
You can extrat objects and consequently CustomProperties by traversing the list. You can declare only fields which you are interested in and ignore others by #JsonIgnoreProperties(ignoreUnknown = true)(for example i have not included metadata)

Parsing json string to java with complex datastructure(jackson)

I am trying to convert the below json string to java object, but i am getting empty object.
Under prop2 object, there can be any number of key value pairs(where key is a string and value is a array )
{
"Level1": {
"prop1": "",
"prop2": {
"one": [{
"ip": "1.2.3.4",
"port": "100"
}],
"ten": [{
"ip": "10.20.20.10",
"port": "200"
}]
}
}
}
I have this class structure, however i am getting ipAndPorts map as empty.
#JsonIgnoreProperties(ignoreUnknown = true)
static class Root {
#JsonProperty("Level1")
private Level1 level1;
}
#JsonIgnoreProperties(ignoreUnknown = true)
static class Level1 {
#JsonProperty("prop2")
private Prop2 prop2;
}
#JsonIgnoreProperties(ignoreUnknown = true)
static class Prop2 {
private Map<String, List<IpAndPort>> ipAndPorts = Collections.emptyMap();
}
#JsonIgnoreProperties(ignoreUnknown = true)
static class IpAndPort {
#JsonProperty("port")
private String port;
}
How should my java class look like, to represent "prop2" correctly?
For the record: The problem was solved by using
#JsonIgnoreProperties(ignoreUnknown = true)
static class Level1 {
#JsonProperty("prop2")
private Map<String, List<IpAndPort>> ipAndPorts = Collections.emptyMap();
}
directly without the Prop2 class. Otherwise Jackson would expect a JSON property called ipAndPorts under prop2 JSON object.
I would sudgest that you would first create your Java class the way want it to look like, then use Jackson to serialize it to JSON. you will see what is the structure of resultant JSON and see if and how you will need to modify your class.

Dynamic serialization/deserialization

I use Spring MVC and Jackson to drive the API of a application that I work in. I am faced with the following situation, we need serialize the Person class below in two different ways...
#Entity
Order{
String id;
String name;
String address;
List<Items> items;
}
#Entity
Item{
String id;
String description:
}
The two situations reposes on the serialization or not of the content of the "items" field in accord with the service that was called.
For example, the service http://localhost/order, results without the "items" field.
{
"id": "1",
"name" : "Bill",
"address" : "any address",
}
In the other hands, the second way is http://localhost/order/[id_order]/item/[ids_items], results with the field "items" that was give on the parameter.
{
"id": "1",
"name" : "Bil",
"address" : "any",
"items" : [{
"id" : "33",
"description" : "Item 33"
}]
}
#JsonView
You can use #JsonView to filter fields depending on the context of serialization. It is supported by Spring MVC.
First define your views:
public class View {
interface Default { }
interface Detailed extends Default { }
}
Then annotate your fields using the desired view:
#Entity
public class Order {
#JsonView(View.Default.class)
private String id;
#JsonView(View.Default.class)
private String name;
#JsonView(View.Default.class)
private String address;
#JsonView(View.Detailed.class)
private List<Items> items;
// Getters and setters
}
Finally annotate your controller methods to use a view when serializing the response:
#JsonView(View.Default.class)
#RequestMapping(value = "/order", method = RequestMethod.GET)
public ResponseEntity<Order> getOrder() {
...
}
#JsonView(View.Detailed.class)
#RequestMapping(value = "/order-with-items", method = RequestMethod.GET)
public ResponseEntity<SampleResults> getOrderWithItems() {
...
}
In order to make it work, you may need to disable the default view inclusion in your ObjectMapper:
mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
With jackson you can modify the result json string on the fly. For example:
// create a new order
Order order = new Order("id", "name", "addr");
ObjectMapper mapper = new ObjectMapper();
// create a json string with the order
JsonNode node = mapper.valueToTree(order);
//the content of the node at this moment is:
//{"id":"id","name":"name","address":"addr"}
// create an ArrayList with the Items
ArrayList<Item> items = new ArrayList<Item>();
items.add(new Item("id1", "desc1"));
items.add(new Item("id2", "desc2"));
// transform the ArrayList to a json string and add it
// the the previous node with the Order
((ObjectNode)node).put("items", mapper.valueToTree(items));
String jsonString = node.toString();
System.out.println(jsonString);
The final output is:
{"id":"id","name":"name","address":"addr","items":[{"id":"id1","description":"desc1"},{"id":"id2","description":"desc2"}]}
For more information visit the official documentation page: https://github.com/FasterXML/jackson-databind/

Categories

Resources