Using JsonPath in Java (8) to create/add nodes and arrays - java

I am trying to create or update a JSON with a new key:value nodes inside a JsonArray.
So far I am able to add simple values using a map of nodes, but I am unable to add an array.
The goal is to create a JSON string with the following values:
{
"curfew": [{
"enabled": true,
"lock_time": "00:00",
"unlock_time": "00:10"
},
{
"enabled": true,
"lock_time": "00:20",
"unlock_time": "00:30"
}]
}
Starting from a new and empty JSON and later adding more values (such as the second "curfew" array).
Map<String, Object> values = ImmutableMap.of(
"enabled", true,
"lock_time", "00:00",
"unlock_time", "00:10");
String emptyJson = "{}"; //empty json
DocumentContext doc = getDocument(emptyJson)
doc.set(JsonPath.compile("$.curfew"), values).jsonString();
So far I am getting this (NOT AN ARRAY)
{
"curfew": {
"enabled": true,
"lock_time": "00:00",
"unlock_time": "05:00"
}
}

Create a List<Map<String, Object>> and then add your map in the list
List<Map<String, Object>> list = new ArrayList<>();
list.add(values);
And set the list in doc
doc.set(JsonPath.compile("$.curfew"), list).jsonString();

Related

Java Jackson: get number of all subelements [duplicate]

This question already has answers here:
Java Stream API - count items of a nested list
(3 answers)
Closed 11 months ago.
I have the following piece of JSON code:
{
"films": [{
"id": "12345",
"title": "The Fellowship Of The Ring",
"year": 2001,
"cast": [{
"actor": "Billy Boyd",
"id": "770911234",
"character": "Peregrin Took"
},
{
"actor": "Ian McKellen",
"id": "162659743",
"character": "Gandalf"
}
]
},
{
"id": "67890",
"title": "Spiderman",
"year": 2021,
"cast": [{
"actor": "Tom Holland",
"id": "162660329",
"character": "Spiderman"
}]
}
]
}
I'm fairly new to using Jackson and now would like to know, what would be the best way to get the number of all actors of all film objects with Java? Would it be to parse it from the movies object or to make a separate parsing method?
I have created a JsonFilm POJO that contains the fields of a film object and a class JsonFilms with getter+setter:
public class JsonFilms {
#JsonProperty("films")
private List<JsonFilms > jsonFilmList = new ArrayList<>();
public List<JsonFilm> getFilmsList() {
return Collections.unmodifiableList(jsonFilmList );
}
public void setFilmsList(List<JsonFilm> jsonFilmList ) {
this.jsonFilmList = jsonFilmList;
}
}
With an ObjectMapper, I can retrieve all films:
File jsonFile = new File(filePath);
JsonFilms jsonFilmList = objectMapper.readValue(jsonFile, JsonFilms.class);
Well, you can create appropriate classes such as Film and Actor and parse your Json into appropriate structure of your classes, or you can do it quick and dirty and parse it into a Map<String, Object>. Your map will contain just one key "films" which will contain a List<Map<String, Object>>. You should iterate through that list and from each Map<String, Object> in that list extract a key "cast" that will contain a List<Map<String, Object>>. You summarize the lengths of all of those lists and that's your answer. In order to convert your JSON string into your Map<String, Object> you can use Jackson library, and in particular ObjectMapper class and its method readValue() or you can use an Open-source library MgntUtils that provides class JsonUtils which is a wrapper over Jackson library anyway. With that class parsing your Json String would look like this:
Map<String, Object> myMap = null;
try {
myMap = JsonUtils.readObjectFromJsonString(jsonString, Map.class);
}
} catch (IOException e) {
...
}
The Maven artifacts for MgntUtils library could be found here, and library as a jar along with Javadoc and source code could be found on Github here
If you just want the titles and actors' names, you can use ObjectMapper.readTree(). Since JsonNode implements Iterable, you can stream all value nodes using StreamSupport.stream(node.spliterator(), false).
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
Map<String, List<String>> filmActors = StreamSupport
.stream(root.get("films").spliterator(), false)
.collect(Collectors.toMap(
filmNode -> filmNode.get("title").asText(),
filmNode -> StreamSupport
.stream(filmNode.get("cast").spliterator(), false)
.map(castNode -> castNode.get("actor").asText())
.toList()));
filmActors.forEach((title, actors) -> System.out.println(title + ": "
+ actors.stream().collect(Collectors.joining(", "))));
Output:
The Fellowship Of The Ring: Billy Boyd, Ian McKellen
Spiderman: Tom Holland
If you're allowed to add new libraries then the JsonPath library could help. The simplest code would be:
import com.jayway.jsonpath.JsonPath;
import net.minidev.json.JSONArray;
...
JSONArray result = JsonPath.read(json, "$.films[*].cast[*].actor");
System.out.println("there are " + result.size() + " actors");
System.out.println("they are:");
result.forEach(System.out::println);

Java Spring custom JSON response

I have a data that looks like so:
{
"name":{
"first": "<firstName>",
"last": "<lastName>"
},
"info": "<info>",
"place":{
"location": "<location>",
},
"test": "<test>"
}
However, I want to customize the Response json and group the data by the location.
In other words I would like to have a response like this:
"location":[{
"name": <location>,
user: [{
"name": <firstName> + <lastName>,
"info": <info>,
"test": <test>
}]
}]
I have tried to the it into a map like so:
List<Data> data= newArrayList(repository.findAll(Sort.by(Sort.Direction.ASC, "location")));
Map<String, Object> result= new HashMap<>();
for (Data d : data) {
result.put("name", d.getLocation());
result.put("user", d.getFirstName());
...
and so on, but this does not work. What is the best approach to achieve my desired result?
As of now you have a single result map, which you are then overwriting in the loop, so it will contain data for a location only, the last one. What you probably wanted to do is creating a list of those maps, which requires creating a new map inside the loop, and collecting them in a list:
List<Map<String, Object>> result=new ArrayList<>();
for(Data d : data) {
Map<String, Object> item = new HashMap<>();
item.put("name", d.getLocation());
item.put("user", d.getFirstName());
...
result.add(item);
}

How I get only value ignoring the key

I have a json array in database. I want to get only value ignoring the key and send it to ajax call.
Json string I have while saving:
{
"cells":
[
{
"type": "devs.TooledModel",
"position":
{
"x": 200,
"y": 100
},
"size":
{
"width": 71,
"height": 100
},
".":
{
"magnet": false
}
}
]
}
I want to return the exact json array from database but I am getting a key appended because I am using a map in java servlet to retrieve json:
List<Map<String, Object>> result = new ArrayList<>();
while (rSet.next()) {
Map<String, Object> row = new HashMap<>();
row.put("JSON_Diagram", rSet.getString("JSON_INFO"));
result.add(row);
}
json received: JSON_Diagram: "cells%5B0%5D%5Btype%5D=devs.TooledModel&cells..
How do I remove the key JSON_Diagram and get only value ? Tried with Object value = result.get(0); but didn't work
You need to get a list of all the keys, loop over them and add them to your map and if you need only value add values only to list as shown in the example below:
Map<String,Object> row = new HashMap<String,Object>();
Iterator iter = rSet.keys();
while(iter.hasNext()){
String key = (String)iter.next();
String value = rSet.getString(key);
row.put(key,value);
result.add(value);
}
So once you have the map just do a a map.values.
[http://docs.oracle.com/javase/7/docs/api/java/util/Map.html#values()][1]
Then just use the resultant collection!

Converting Nested Json files to CSV in java

{
"Employee": [
{
"empMID": "mock:1",
"comments": [],
"col1": "something",
"contact": [{"address":"2400 waterview", "freetext":true}
],
"gender": "male"
},
{
"empMID": "mock:2",
"comments": [],
"col1": "something",
"contact": [{"address":"2200 waterview", "freetext":true}
],
"gender": "female"
}
],
"cola": false,
"colb": false
}
This is how my Json file looks .I m required to convert this json to a csv .(I m trying to convert a multi-dimesional data to 2d).I m using gson for my purpose.I cannot use gson.fromgson() function to object map with a template because it should be generic .
I know we can use CDL to convert jsonarray to csv format but It wont work in my case .
my csv format looks like
Employee*
empMID,comment.$,contact.address,contact.freetext,gender
mock:1,,2400 waterview,TRUE,male
mock:123,,2200 waterview,TRUE,female
colA#
TRUE
colB#
FALSE
I tried using google-GSON api to convert to this format .But I m not able to convert to this format .I have used * to represent its a json array and # to represent its a primitive type and contact.address to represent nested array inside another json array .I having problem relating this nested structure .I m able to traverse everything recursively like a column. Thanks in advance
public static void main(String[] args) throws IOException{
BufferedReader reader=null;
StringBuilder content=null;
String result=null;
reader = new BufferedReader(new FileReader("temp.json"));
String line = null;
content= new StringBuilder();
while ((line = reader.readLine()) != null) {
content.append(line);
}
reader.close();
result= content.toString();
JsonElement jelement = new JsonParser().parse(result);
printJsonRecursive(jelement);
}
public static void printJsonRecursive(JsonElement jelement){
if(jelement.isJsonPrimitive()){
System.out.println(jelement.getAsString());
return;
}
if(jelement.isJsonArray()){
JsonArray jarray= jelement.getAsJsonArray();
for(int i=0;i<jarray.size();i++){
JsonElement element= jarray.get(i);
printJsonRecursive(element);
}
return;
}
JsonObject jobject= jelement.getAsJsonObject();
Set<Entry<String, JsonElement>> set= jobject.entrySet();
for (Entry<String, JsonElement> s : set) {
printJsonRecursive(s.getValue());
}
}
}
You can achieve this thru reflection if you have a object mapped to the json.
use gson/jackson to convert json to java object
append fields using reflection by iterating the class and get any field you interested in.
append value with reflection by getting value from the target object.
More detail look at my blog post below:
vcfvct.wordpress.com/2015/06/30/converting-nested-json-files-to-csv-in-java-with-reflection/
You are not printing the key. This should fix it.
for (Entry<String, JsonElement> s : set) {
System.out.println(s.getKey()); //Added
printJsonRecursive(s.getValue());
}
You can take care of \ns from here.
EDIT
If you want to print the keys just once for repeating json objects, create a Java bean to hold the data and populate it during your recursion. Once the bean is complete, add a method there to print all the data in the format you want (printing keys only once and so on).
You can use the library json2flat for converting your JSON to CSV.
This library doesn't require any POJO's. It simply takes your JSON as string and returns a 2D representation of it in the format of List<Object[]>.
For example for the JSON:
{
"Employee": [
{
"empMID": "mock:1",
"comments": [],
"col1": "something",
"contact": [{"address":"2400 waterview", "freetext":true}
],
"gender": "male"
},
{
"empMID": "mock:2",
"comments": [],
"col1": "something",
"contact": [{"address":"2200 waterview", "freetext":true}
],
"gender": "female"
}
],
"cola": false,
"colb": false
}
It gives an output:
/cola,/colb,/Employee/empMID,/Employee/col1,/Employee/gender,/Employee/contact/address,/Employee/contact/freetext
,,"mock:1","something",,"2400 waterview",true
,,"mock:2","something",,"2200 waterview",true
false,false,,,,,
/**
* Get separated comlumns used a separator (comma, semi column, tab).
*
* #param headers The CSV headers
* #param map Map of key-value pairs contains the header and the value
*
* #return a string composed of columns separated by a specific separator.
*/
private static String getSeperatedColumns(Set<String> headers, Map<String, String> map, String separator) {
List<String> items = new ArrayList<String>();
for (String header : headers) {
String value = map.get(header) == null ? "" : map.get(header).replaceAll("[\\,\\;\\r\\n\\t\\s]+", " ");
items.add(value);
}
return StringUtils.join(items.toArray(), separator);
}

Parsing generic tag in JSON from GSON

I have a JSON which contains generic values. I tried it by using Maps but couldn't get the results. My problem is the generic tags starts from second level. Here is the JSON I am trying to parse through gson.
{
"success": true,
"status": 200,
"events": {
"Sep 2013": [
{
"artist_id": 1,
"created_at": "2013-05-18T15:21:00Z",
"duration": 2,
"end_time": "2013-09-19T22:00:00Z",
"event_desc": "",
"event_facebook_link": "",
"event_link": "https://www.smtickets.com/marketing/view/1316",
"feature_small": false,
"featured_status": false,
"id": 90,
In this JSON tag after "events" is generic i.e., "Sep 2013".
What I am trying right now is:
public Event event ;
public class Event {
public ArrayList<Map<String, String>> dates;
}
And I'm accessing it:
obj = gson.fromJson(reader, AllShowsActivityData.class);
Can anyone tell me that how can I make a class of dates. So Gson can serialize it. Or is there any other way to iterate first level tag and then I can declare it something like this
Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);
You can indeed use a Map, but you're not using it correctly. Note that what you have is a field "events", which is an object that contains a number of pairs string and array of objects:
{ "events": { "Sep 2013": [ {}, {}, ... ], ... } }
To parse your JSON you'd need a class structure like this (in pseudo-code):
class AllShowsActivityData
Map<String, List<Event>> events
class Event
int artist_id
String created_at //parsing as String to simplify...
int duration
...
And then your code to parse:
AllShowsActivityData obj = gson.fromJson(reader, AllShowsActivityData.class);
And finally, if what you want is to access the dates, you'll have all of them in:
Set<String> dates = obj.getEvents().keySet(); //"Sep 2013", ...
Note: in fact, if you only want those dates, you don't even need the class Event and you could use just Map<String, List<Object>> events...

Categories