Spring Boot: Convert complex json string response to object - json

I want to convert a complex json string–which I get from a GET request–to our database. I need to loop through all the objects and find some specific ones. The problem is that all objects are different in some way. They could look like these three examples, but there are many more:
{
"created": 1493209170473990,
"id": "fu:9843464EDF4D053072ACEAC2362EE0D8",
"type": "user-created"
},
{
"created": 1493209170883075,
"data": {
"process_type": "wallet-tx"
},
"id": "fu:6BE085BF29D7C8AF4C238615CA85F31A",
"process": "0CEB2F401E0FB9D9A44A124D0710B521",
"type": "process-created"
},
{
"created": 1495535185484487,
"data": {
"message": "electronic delivery"
},
"document": "25FBED0A80FEEBD6FF154D21D8E35D7E",
"id": "fu:3C17584381C0AFB4836F73057DB7DEAB",
"type": "info"
}
I need to find some objects with a specific type, but I cant get them out of a string. I get the request data with this call:
#RequestMapping(value="/events", method=RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)
public String getEvents() {
int created_after = 0;
final String url = server + "/rest/client/events/" + created_after;
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json; charset=utf-8");
headers.set("Auth-Token", token); // user_token
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
return response.getBody();
}
I use Angular in my frontend, which could easy convert the string to an Object, but then I have to pass this again to my backend to work with the data. I want to keep everything in the backend. Do you have any idea how to solve it?
If you need more information, please ask. Thanks
EDIT:
My JSON output looks like this:
[
{
"created": 1493209170473990,
"id": "fu:9843464EDF4D053072ACEAC2362EE0D8",
"type": "user-created"
},
{
"created": 1493209170653925,
"data": {
"verify_id": "12581C42DD2DF7D80F802C50ABD144F8"
},
"id": "fu:06111B0A9C5B760B9269044DA97D3D6F",
"type": "post-address-verification-confirmed"
},
{
"created": 1493209171320041,
"data": {
"after": {
"attempt": 1
}
},
"id": "fu:7B5B2AD57C1CE97BB642931C2C3C819D",
"process": "0CEB2F401E0FB9D9A44A124D0710B521",
"type": "process-updated"
},
...
]

The way I understand it, your objects have some common properties, as well as some optional ones. You can model the optional properties using #JsonAnyGetter and #JsonAnySetter:
class Data {
#JsonProperty
private Long created;
#JsonProperty
private String id;
#JsonProperty
private String type;
private Map<String, Object> optional = new HashMap<>();
public Data() { // empty public constructor is required
}
// getters/setters for all properties omitted for brevity
#JsonAnySetter
public void addOptional(String name, Object value) {
optional.put(name, value);
}
#JsonAnyGetter
public Object getOptional(String name) {
return optional.get(name);
}
}
Then you can deserialize your objects using
ObjectMapper objectMapper = new ObjectMapper();
Data data = objectMapper.readValue(j, Data.class);
or, if you've got an array of your Data objects as input,
Data[] data = objectMapper.readValue(j, Data[].class);
All properties except created, id and type will be placed in the optional map.

If you don't know what the structure of the JSON is going to be then you can serialize your JSON string to a Map which maps the field names in the json to their value.
This can be done using the Jackson ObjectMapper:
String jsonObject = <the string JSON response you obtained>;
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> jsonMap = objectMapper.readValue(jsonString,
new TypeReference<Map<String, Object>>(){});
If it's a list of JSON objects you expect then you can first map this to an array of JSON strings and then convert each one to a map:
ObjectMapper objectMapper = new ObjectMapper();
String[] jsonStrings = objectMapper.readValue(jsonString, String[]);
List<Map<String, Object>> jsonMaps = new ArrayList<>();
for (String json : jsonStrings) {
jsonMaps.add(objectMapper.readValue(json,
new TypeReference<Map<String, Object>>(){});
}

Related

How to post request { "nargout":1, "rhs":[1] } with retrofit in kotlin? [duplicate]

So, i have a List of my custom Object and I need a JSON like this:
{
"surveys": [{
"survey": {
"code": "05052017153632",
"date": "05/05/2017 15:36:32",
"device_id": 1,
"questions_attributes": [{
"kind": "string",
"label": "Você encontrou tudo o que procurava?",
"value": "Infelizmente, não"
}, {
"kind": "string",
"label": "Em qual departamento você não encontrou o produto?",
"value": "FERRAMENTAS, TAPETES"
}, {
"kind": "string",
"label": "Deseja que a Havan entre em contato com você?",
"value": "Não informado"
}, {
"kind": "string",
"label": "Nome",
"value": "Não informado"
}, {
"kind": "string",
"label": "E-mail",
"value": "Não informado"
}, {
"kind": "string",
"label": "Telefone",
"value": "Não informado"
}]
}
}]}
But I dont have any ideia how to do it using Gson.
I'm Using Retrofit 2 and need to pass this JSON into a body request.
Any ideias?
Yes you need to pass this JSON into body request.
Retrofit Interface:
public interface RetrofitInterface<R extends RetrofitClass> {
#Headers({"Content-Type: application/json", "Cache-Control: max-age=640000"})
#POST("v1/auth/")
public Call<ResponseBody> callLogin(#Query("key") String key, #Body LoginModel body);
#Headers({"Content-Type: application/json", "Cache-Control: max-age=640000"})
public static final Retrofit retrofit = new Retrofit.Builder()
.baseUrl(AppConstants.mBaseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
Api call Activity:
pass json object into body request use #Body param.
Here you can create gson model classes in http://www.jsonschema2pojo.org/
json to gson converter by using that json request format.
After that set values with that gson pojo classes and pass the json object to body request in retrofit.
For example:
LoginModel:
public class LoginModel {
#SerializedName("username")
private String username;
#SerializedName("password")
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
set Values with pojo class:
LoginModel model_obj = new LoginModel();
mModel_obj.setUsername(mUsername);
mModel_obj.setPassword(mPassword);
Api calling:
Call<ResponseBody> call = service.callLogin(AppConstants.mApiKey, model_obj);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
I saw this question that I made 1 year ago and I`m using another solution nowdays. If someone still needs a help and looking for this, here we go:
I have made a function to handle the JSON format that I want:
public String serialize(List<Object> objects, String arrKey, String
objKey) {
JsonArray ja = new JsonArray();
for (Object object: objects) {
Gson gson = new Gson();
JsonElement je = gson.toJsonTree(object);
JsonObject jo = new JsonObject();
jo.add(objKey, je);
ja.add(jo);
}
JsonObject objMain = new JsonObject();
objMain.add(arrKey,ja);
return objMain.toString();
}
And in my API Call I have this line:
String json = new CustomGsonAdapter().serialize(surveysList, "surveys","survey");
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
RequestBody is the trick. Now, just need to pass this RequestBody to retrofit call and thats it.
#POST("surveys")
Call<Void> setSurveys(#Body RequestBody json);
I dont know if this is the best way to archieve the problem, but for me it was.
Save time and avoid to create a class just to send to server.
#POST("users/new")
Call<User> createUser(#Body User user);
above code will be written in Api Service Interface.
and then you can call this from RestClient class(by Retrofit instance) by passing a JsonObject as Body.

Parse Json String using jackson Parser

I am able to parse json below:
{
"jobId": "xxx",
"jobName": "xxx",
"jobInput": "xxx"
}
final ObjectMapper mapper = new ObjectMapper();
Map<?, ?> map = mapper.readValue(jsonString, Map.class);
I need to parse the below json string using jackson parser in java.
{
"Test1": {
"jobId": "xxx",
"jobName": "xxx",
"jobInput": "xxx"
},
"Test2": {
"jobId": "xxx",
"jobName": "xxx",
"jobInput": "xxx"
}
}
With Jackson, you can do the following:
ObjectMapper mapper = new ObjectMapper();
TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
Map<String, Object> data = mapper.readValue(json, typeRef);
If you prefer to use a custom class to hold the values instead of a Map, use:
ObjectMapper mapper = new ObjectMapper();
Data data = mapper.readValue(json, Data.class);
public class Data {
#JsonProperty("Test1")
private Job test1;
#JsonProperty("Test2")
private Job test2;
// Default constructor, getters and setters
}
public class Job {
private String jobId;
private String jobName;
private String jobInput;
// Default constructor, getters and setters
}
How about just let Jackson parse a Map? Make the return type as Map<String, YourFirstDTO> and I think it will do.

Using javax.ws.rs.core.Response.readEntity to extract a list of strings from a JSON object

If my response is like:
{
"values": [ "1", "2" ]
}
How should I use readEntity to populate a List<String> with the values: 1, 2?
You can read the entity as a Map<String, List<String>>:
Map<String, List<String>> map =
response.readEntity(new GenericType<Map<String, List<String>>>() { });
List<String> values = map.get("values");
Or define your own POJO:
public class MyBean {
private List<String> values;
// Getters and setters
}
List<String> values = response.readEntity(MyBean.class).getValues();
You obviously must have a JSON provider such as Jackson registered.

Map JSON string array to List<String> using Jackson

I have following JSON returned from server.
String json = {
"values": ["name","city","dob","zip"]
};
I want to use ObjectMapper to return the List<String> values. Something like:
List<String> response = mapper.readValue(json, List.class)
I have tried several ways but none of them worked. Any help is appreciated.
Edit: I don't want additional wrapper objects. I want to straight away get the List<String> out.
The TypeFactory in Jackson allows to map directly to collections and other complex types:
String json = "[ \"abc\", \"def\" ]";
ObjectMapper mapper = new ObjectMapper();
List<String> list = mapper.readValue(json, TypeFactory.defaultInstance().constructCollectionType(List.class, String.class));
You could define a wrapper class as following:
public class Wrapper {
private List<String> values;
// Default constructor, getters and setters omitted
}
Then use:
Wrapper wrapper = mapper.readValue(json, Wrapper.class);
List<String> values = wrapper.getValues();
If you want to avoid a wrapper class, try the following:
JsonNode valuesNode = mapper.readTree(json).get("values");
List<String> values = new ArrayList<>();
for (JsonNode node : valuesNode) {
values.add(node.asText());
}
There is another way you might be interested in, partly similar to accepted answer but can be written as one-liner (line breaks should help with understanding).
Input JSON:
{
"values": ["name", "city", "dob", "zip"]
}
Code snippet:
String json = "{\"values\":[\"name\",\"city\",\"dob\",\"zip\"]}";
ObjectMapper mapper = new ObjectMapper();
List<String> list = Arrays.asList(
mapper.convertValue(
mapper.readTree(json).get("values"),
String[].class
)
);
list.forEach(System.out::println);
This code snippet outputs the following:
name
city
dob
zip
Please note that Arrays.asList() returns a list of fixed size because it is backed by a given array. To get a resizable list just wrap it like that:
List<String> resizableList = new ArrayList<>(
Arrays.asList(new String[] {"a", "b", "c"})
);
Of course this solution can be adapted to more complex cases, not just Strings.
For example, for a given POJO User:
class User {
private int id;
private String name;
public int getId() {
return id;
}
public String getName() {
return name;
}
#Override
public String toString() {
return String.format(
"[User = {id: %d, name: \"%s\"}]",
id,
name
);
}
}
and input JSON:
{
"values": [{
"id": 1,
"name": "Alice"
}, {
"id": 2,
"name": "Bob"
}
]
}
following code snippet:
ObjectMapper mapper = new ObjectMapper();
String json = "{\"values\":[{\"id\":1,\"name\":\"Alice\"},{\"id\":2,\"name\":\"Bob\"}]}";
List<User> list = Arrays.asList(
mapper.convertValue(
mapper.readTree(json).get("values"),
User[].class
)
);
list.forEach(System.out::println);
yelds the following output:
[User = {id: 1, name: "Alice"}]
[User = {id: 2, name: "Bob"}]

Gson (Json) parsing - POJO mapping - error (this is not a JSON Array)

For -
Config rfqObj = new Gson().fromJson(data, new TypeToken<Config>() {}.getType());
I'm getting the following exception -
The JsonDeserializer
main.java.com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter#30de3c87
failed to deserialized json object {} given the type
main.java.com.google.gson.ParameterizedTypeImpl#7c3d0336] with root
cause java.lang.IllegalStateException: This is not a JSON Array.
JSON data is -
{
"quantities": {
"142": "0",
"143": "20",
"144": "25"
},
"characteristics": {},
"details": {
"8": "HT Test Report",
"9": "Others",
"13": "nTest"
},
"materials": {},
"additionalProcesses": {},
"suppliers": {}
}
And here is the POJO -
public class Config {
Map<Long, String> quantities = new HashMap<Long, String>();
Map<Long, String> characteristics = new HashMap<Long, String>();
Map<Long, String> details = new HashMap<Long, String>();
Map<Long, String> materials = new HashMap<Long, String>();
Map<Long, String> additionalProcesses = new HashMap<Long, String>();
public Set<Suppliers> suppliers = new HashSet();
//this is for the nested class
public static class Suppliers {
// defining attributes of this class
public Long id;
public String name;
public Long contactId;
public String contactInfo;
public String contactMethod;
public String contactName;
public String message;
}
}
The reason you are getting the error is that your JSON text is an object with the following property:
"suppliers": {}
But because your Config object wants the property suppliers to be a set; Gson is expecting a JSON array, not a JSON object.
If you can somehow get your JSON text to include
"suppliers": []
you should be able to deserialize this into a Java set.