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)
Related
I have a Json response that looks like this:
[
{ "name":"A" },
{ "name":"B" }
]
I have Java classes representing a single ResponseDto and contains a List of Person:
public class GetPersonsResponseDto {
public List<Person> persons;
}
public class Person {
public String name;
}
I would like to deserialize the by JSON using ObjectMapper but without use of a custom Deserializer and without collection type (no Persons[].class or TypeReference<List<Person>>(){}). What I really want is
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(in, GetPersonsResponseDto.class);
But I get:
jackson.map.JsonMappingException:
Can not deserialize instance of com.project.my.GetPersonsResponseDto out of START_ARRAY token
I tried several Annotations but without success.
Actually it is quite simple to serialize your models to the target json, but might be tricky to deserialize.
So, the solution for deserialization in this case could be using #JsonCreator annotation from com.fasterxml.jackson.annotation package above constructor:
#Data
public class GetPersonsReponseDto {
public List<Person> persons;
#JsonCreator // use this annotation for deserialization on constructor
public GetPersonsReponseDto(List<Person> persons) {
this.persons = persons;
}
public GetPersonsReponseDto() {
}
}
However, it might not work with some versions of jackson.
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);
In my front end I send this JSON:
"ids": [ 123421, 15643, 51243],
"user": {
"name": "John",
"email": "john#sovfw.com.br"
}
To my Spring Endpoint below:
#PostMapping(value = "/sendToOficial")
public ResponseEntity<?> sendToOficial(#RequestBody Map<String, Object> payload) {
ObjectMapper mapper = new ObjectMapper();
List<Long> pointsIds = mapper.convertValue( payload.get("pointsIds"), List.class );
UsuarioDTO autorAlteracao = mapper.convertValue(payload.get("user"), UsuarioDTO.class);
for (Long idPoint : pointsIds) { ... }
But I'm getting a Cast Exception in the for saying that it can't cast Integer to Long.
I can't receive the "ids" numbers as Integer, I want to receive as Long.
Please, how could I do this?
First, define POJOs for mapping your request object:
public class RequestObj implements Serializable{
private List<Long> ids;
private UsuarioDTO user;
/* getters and setters here */
}
public class UsuarioDTO implements Serializable{
private String name;
private String email;
/* getters and setters here */
}
And then modify your endpoint:
#PostMapping(value = "/sendToOficial")
public ResponseEntity<?> sendToOficial(#RequestBody RequestObj payload) {
In this way you also do not need to use an ObjectMapper. Just call payload.getIds().
Consider also that in this way if payload changes you'll need only to change RequestObj definition, while using ObjectMapper would force you to update also your endpoint in an important way. It's better and safer to separate payload representation from control logic.
In jackson-databind-2.6.x and onward versions you can configure the ObjectMapper to serialize low typed int values (values that fit in 32 bits) as long values using the DeserializationFeature#USE_LONG_FOR_INTS configuration feature:
#PostMapping(value = "/sendToOficial")
public ResponseEntity<?> sendToOficial(#RequestBody Map<String, Object> payload) {
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature .USE_LONG_FOR_INTS, true);
List<Long> pointsIds = mapper.convertValue( payload.get("pointsIds"), List.class );
UsuarioDTO autorAlteracao = mapper.convertValue(payload.get("user"), UsuarioDTO.class);
for (Long idPoint : pointsIds) { // ... }
}
If you just want your mapper to read into a List<Long>, use this trick for obtaining full generics type information by sub-classing.
Example
ObjectMapper mapper = new ObjectMapper();
List<Long>listOfLong=mapper.readValue("[ 123421, 15643, 51243]" ,
new TypeReference<List<Long>>() {
});
System.out.println(listOfLong);
Prints
[123421, 15643, 51243]
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.
I couldn't find an example how to map the following json:
{
"id":1,
"name":"hugodesmarques",
"age":30,
}
To the following java object using jackson:
public class EntityDto {
private Map<String, Object> content;
}
Notice the dto is just a wrapper. What I'm trying to achieve is to have an object EntityDto with a Map{name=>"hugodesmarques", age=>30, id=>1}.
I want to avoid having to map each json field to an object map.
A step back
First of all, the JSON you posted in you question is invalid: there's a comma after 30 and it shouldn't be there. Fix your JSON otherwise Jackson won't parse it:
{
"id": 1,
"name": "hugodesmarques",
"age": 30
}
Parsing the JSON with Jackson
Add a constructor annotated with #JsonCreator to the EntityDto class, as following:
public class EntityDto {
private Map<String, Object> content;
#JsonCreator
public EntityDto(Map<String, Object> content) {
this.content = content;
}
// Getters and setters omitted
}
Then parse the JSON using ObjectMapper:
String json = "{\"id\":1,\"name\":\"hugodesmarques\",\"age\":30}";
ObjectMapper mapper = new ObjectMapper();
EntityDto entityDto = mapper.readValue(json, EntityDto.class);
Structure of class must be like structure of JSON:
public class EntityDto {
int id;
String name;
int age;
}
Jackson can read JSON as a HashMap:
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.readValue("{\"id\":1, \"name\": \"One\"}", HashMap.class);
EntityDto dto = new EntityDto();
dto.setContent(map);