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);
Related
I have Json response like :
[
{
"first_name": "fname1",
"last_name": "lname1"
},
{
"first_name": "fname2",
"last_name": "lname2",
"city_name": "paris"
},
{
"first_name": "fname2",
"last_name": "lname2",
"city_name": "paris",
"Address": "1st Ave"
}
.
.
.
]
and my fields in JsonObject is dynamic so i can't use a class with predefined fields , so i've decided to use Map to parse the Json response as below :
Collection<List<Map<String,String>>> list_Objects = null;
Reader reader = new InputStreamReader(is);
Gson gson = new Gson();
Type collectionType = new TypeToken<Collection<List<Map<String,String>>>>(){}.getType();
list_objects = gson.fromJson(reader, collectionType);
but it throws me this error :
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 1
im not expert in Json parsing so please tell me where is my mistake or if there is another way to implement such behavior i would be very appreciated.
I think the problem is with your Collection wrapper on the List one.
You just need to define the list_Objects as List<Map<String, String>>.
I just tried the following with Jackson's ObjectMapper (which should work the same as GSON) and it worked ok:
List<Map<String, String>> list_objects = objectMapper.readValue(jsonString, new TypeReference<List<Map<String, String>>>(){});
The result is a List of LinkedHashMap objects.
For your GSON example, you just need to remove the Collection<>, or the List<> if you prefer.
If I'm reading this correctly, you're defining your list twice.
Collection<List<Map<String, String>>> would map to a structure like
[ [ {"bla": "bla"}, {"bla": "bla"} ], [ {"foo": "bar"}, ... ], ... ]
(because List is a kind of Collection). So, try using List<Map<String, String>> and see if that works...
{
"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);
}
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...
I have the following Json file and I want to create list of array ,i don't know the keys name (like person ,lastname etc ) it can be
many entities like employes users etc but the structure of the file must be exactly the same ,how can i do that ?
the file is
{
"Person": [
{
"name": "Peter",
"lastname": "ta",
"age": 43,
"sex": "male"
},
{
"name": "Zara",
"lastname": "treg",
"age": 25,
"sex": "female"
}
]
}
what I need is to create list of array like this
person ,name,peter ,lastname,ta,age,43,sex,male
person ,name,zara ,lastname,treg,age,23,sex,female
....
I started with the following code to get the file but since i dont know the name of the keys I dont know how to proceed.
JSONObject jsonObject= (JSONObject) parser.parse(new FileReader("C:\\General\\jsonperson.txt"));
You can use
String[] keyNames= JSONObject.getNames(jsonObject);
to get the names of the keys.
Javadoc
Using this, you can get the values using getJSONObject
Looping these you can construct the array you are looking for.
Check Example 4 on this page: https://code.google.com/p/json-simple/wiki/DecodingExamples
Specifically, this part:
Map json = (Map)parser.parse(jsonText, containerFactory);
Iterator iter = json.entrySet().iterator();
System.out.println("==iterate result==");
while(iter.hasNext()){
Map.Entry entry = (Map.Entry)iter.next();
System.out.println(entry.getKey() + "=>" + entry.getValue());
}
System.out.println("==toJSONString()==");
System.out.println(JSONValue.toJSONString(json));
That's how you might iterate over the entries of a JSONObject. By the way, with this library, if it's the one I think you're using, a JSONObject is just a java.util.Map, and you can use all of its methods - that's why it works for this example to cast the parse result to a Map.
All of the JSON <-> Java object mappings for this lib: https://code.google.com/p/json-simple/wiki/MappingBetweenJSONAndJavaEntities
You can also try the below code:
public void performExecute() throws IOException {
JsonFactory jsonFactory = new JsonFactory();
ObjectMapper objectMapper = new ObjectMapper(jsonFactory);
File file = new File("json.txt");
TypeReference<HashMap<String, Object>> typeReference = new TypeReference<HashMap<String, Object>>() {};
HashMap<String, Object> jsonMap = objectMapper.readValue(file, typeReference);
System.out.println("JSON DATA: " + jsonMap);
}
Make sure the Jackson library is in your class path & you imports the following classes while using the code:
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
Other than Locale.getISOCountries() that is, because I'm already getting some strange errors with that. What else is the best way to get the 2-letter country codes as well as the full country name?
See code snippets :
String[] countryCodes = Locale.getISOCountries();
for (String countryCode : countryCodes) {
Locale obj = new Locale("", countryCode);
System.out.println("Country Code = " + obj.getCountry()
+ ", Country Name = " + obj.getDisplayCountry());
}
Refer to this country list in Java for more examples.
For a separate project, I took the country code data from the ISO site.
Beware of the following:
The names are in all caps. You will probably want to tweak it so it's not.
The names are not all in simple ASCII.
The names are not entirely political neutral (it is probably impossible for any purported list of countries to be). E.g., "Taiwan, Province of China" is a name. A good starting point to learn about the issues is this blog post.
Create a Map out of this page http://www.theodora.com/country_digraphs.html
Save it to a file (I suggest XMLEncoder/XMLDecoder class)
Create a wrapping class that loads this Map from the file (I'd use a lazily initialized singleton) and allows access to the get(...) methods.
Repeat (or use a bi-directional map) these steps for each column of the table on the afore mentioned webpage.
Fancy-Time: Throw in some code to wrap the entries in a Reference object (SoftReference?) so that the Map won't throw MemoryErrors
You can use from json bellow like
Json parsing..
String jsonString =JSON_DATA;
ObjectMapper mapper = new ObjectMapper();
try {
JsonNode rootNode = mapper.readTree(jsonString);
for (JsonNode node : rootNode) {
String countrycode = node.path("code").asText();
String dialnumber = node.path("dial_code").asText();
String countryname = node.path("name").asText();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Json String here
public static String JSON_DATA="
[
{
"name": "Afghanistan",
"dial_code": "+93",
"code": "AF"
},
{
"name": "Aland Islands",
"dial_code": "+358",
"code": "AX"
},
{
"name": "Albania",
"dial_code": "+355",
"code": "AL"
},
{
"name": "Algeria",
"dial_code": "+213",
"code": "DZ"
},
{
"name": "AmericanSamoa",
"dial_code": "+1684",
"code": "AS"
}]";
Or You can download full json from link : https://gist.github.com/Goles/3196253