Map JSON string array to List<String> using Jackson - json

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"}]

Related

Serialize Feign Json Response to object

I've the following Json response coming from a Feign client:
{
"maxResults": 1,
"total": 5,
"isLast": false,
"values": [
{
"id": 37,
"self": "https://your-domain.atlassian.net/rest/agile/1.0/sprint/23",
"state": "active",
"name": "sprint 1",
"goal": "sprint 1 goal"
}
]
}
The feign client:
#FeignClient(name = "jira")
public interface JiraFeignClient {
#GetMapping("/rest/agile/1.0/board/{boardId}/sprint?state=active&maxResults=1")
ActiveSprintResponse getActiveSprint(#PathVariable String boardId);
}
I'd like to define the ActiveSprintResponse class in order to have the information related to the "values" property (I'm only interested in those) of the json response but I don't understand how can I easily represent it.
I would have no problems for the properties "maxResults", "total" etc... but how can easily unpack "values"? I can assume I will always have only one element in the value array.
I've tried defining it like that but it clearly does not work:
public class ActiveSprintResponse {
private final String id;
private final String self;
private final String name;
private final String goal;
public ActiveSprintResponse(String id, String self, String name, String goal) {
this.id = id;
this.self = self;
this.name = name;
this.goal = goal;
}
}
You need to define a class that represents the root JSON object. You can define a property for the values of type List then:
public class ActiveSprintResponseList {
private List<ActiveSprintResponse> values;
// (Other fields omitted for simplicity)
public void setValues(List<ActiveSprintResponse> values) {
this.values = values;
}
public List<ActiveSprintResponse> getValues() {
return values;
}
}
you then need to declare that class as return type:
#FeignClient(name = "jira")
public interface JiraFeignClient {
#GetMapping("/rest/agile/1.0/board/{boardId}/sprint?state=active&maxResults=1")
ActiveSprintResponseList getActiveSprint(#PathVariable String boardId);
}
and use it on the calling side:
ActiveSprintResponseList response = client.getActiveSprint(..);
List<ActiveSprintResponse> values = response.getValues();
// work with values

Spring Boot: Convert complex json string response to object

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>>(){});
}

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.

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.

Create JsonObject from #ResponseBody

#RequestMapping(value = "/dropDown", method = RequestMethod.GET)
public #ResponseBody
DropDown getList(Map<String, Object> map, HttpServletRequest request,
HttpServletResponse response) {
DropDown dropDown = new DropDown();
List<Map<String, Object>> rows = new ArrayList<Map<String, Object>>();
List<MapTable2> list = contactService.mapProcess();
for (MapTable2 table : list) {
Map<String, Object> dataRow = new HashMap<String, Object>(1);
dataRow.put("text", table.getProcess());
dataRow.put("value", table.getId());
dataRow.put("selected", false);
dataRow.put("description", table.getProcess());
dataRow.put("imageSrc", "image.jpg");
rows.add(dataRow);
}
dropDown.setRows(rows);
return dropDown;
}
I need to create following one
var ddData = [
{
text: "Facebook",
value: 1,
selected: false,
description: "Description with Facebook",
imageSrc: "http://dl.dropbox.com/u/40036711/Images/facebook-icon-32.png"
},
{
text: "Twitter",
value: 2,
selected: false,
description: "Description with Twitter",
imageSrc: "http://dl.dropbox.com/u/40036711/Images/twitter-icon-32.png"
}]
I know the issue with my above java coding , I'm not aware to create json array like above.
please check it and help me to correct it.
MapTable2 has ProcessId & ProcessName
public class MapTable2 {
private int id;
private String process;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProcess() {
return process;
}
public void setProcess(String process) {
this.process = process;
}
}
#theon is right.
Since you are using #Responsebody you can let Spring do the JSON conversion for you. Create a class that matches the objects in the JSON array:
public class SomeObject {
public String getText() { //... }
public int getValue() { //... }
public boolean getSelected { // ... }
public String getDescription { // ... }
public String getImageSrc { // ... }
}
Populate the objects and return it as a list from your controller:
#RequestMapping(value = "/dropDown", method = RequestMethod.GET)
#ResponseBody
public List<SomeObject> getList(Map<String, Object> map, HttpServletRequest request, HttpServletResponse response) {
// Get the objects, return them in a list
}
Add the <mvc:annotation-driven /> or the #EnableWebMvc to your application config unless you have not already done so. Make sure that Jackson is available on your classpath and then Spring will automatically serialize your objects to JSON (if the request has Content-Type: application/json. Alternatively, the produces attribute can be added to the #RequestMapping annotation to always return JSON).
Well Use this library. It is very light weight (16KB) and does exactly what you need.
So in your case, you will be using JSONObject which internally extends HashMap
and do
JSONObject o = new JSONObject();
o.put("text","whatever text");
o.put("value",1);
o.put("selected",false);
//and so on
JSONArray arr = new JSONArray();
arr.add(o);
The above will give you this:
[
{
text: "Facebook",
value: 1,
selected: false,
description: "Description with Facebook",
imageSrc: "http://dl.dropbox.com/u/40036711/Images/facebook-icon-32.png"
}
]
To add more objects, add more JSONObjects in a loop to JSONArray
So as per your given code, just replace dataRow with 'JSONObject' and rows with JSONArray and thats it. In the end, to retrieve the string, do rows.toString().