How does generic works for nested Lists - java

I have a meeting centre class, each meeting centre contains 1..n meeting rooms and each room has 0..n reservations.
I want to iterate through the meeting centres, for everyone create a sublist with all its reservations and then add the sublist to the list with all reservations from all meeting centres
public List<ArrayList<Reservation>> findAllReservations() {
List<ArrayList<Reservation>> allReservations = new ArrayList<>();
for (MeetingCentre mc : this.getMeetingCentres()) {
ArrayList<Reservation> currentMCReservations = new ArrayList<>();
for (MeetingRoom mr : mc.getMeetingRooms()){
if (mr.getReservations().size() > 0){
currentMCReservations.addAll(mr.getReservations());
}
}
if (currentMCReservations.size() > 0) {
allReservations.add(currentMCReservations);
}
}
return allReservations;
}
I want to export this data into JSON, and when I try to work with the allReservations List, the generic get "lost" and I get an exception "Object cannot be converted into Reservation"

Best simple solution is to use gson.
Gson gson = new Gson();
String jsonList = gson.toJson(list_name); // converts list to json
System.out.println(jsonList);

Do you need a List of ArrayLists?
public String getAllReservationsJSON() {
List<Reservation> allReservations = new ArrayList<>();
for (MeetingCentre mc : this.getMeetingCentres()) {
ArrayList<Reservation> currentMCReservations = new ArrayList<>();
for (MeetingRoom mr : mc.getMeetingRooms()){
if (mr.getReservations().size() > 0){
currentMCReservations.addAll(mr.getReservations());
}
}
if (currentMCReservations.size() > 0) {
allReservations.addAll(currentMCReservations);
}
}
Gson gson = new Gson();
return gson.toJson(allReservations);
}

Related

JSON getting nested in a POJO

I have a POJO class as:
public class D{
private JSONObject profileData;
public JSONObject getProfileData ()
{
return profileData;
}
public void setProfileData (JSONObject profileData)
{
this.profileData = profileData;
}
}
Now I populate this class like:
for (int i =0; i<identities.size();i++){
D d = new D();
d.setProfileData(profileData);
dList.add(d);
}
I create JSON object for profileData from GSON using a HashMap:
profileDataInJson = new JSONObject(gson.toJson(map1));
Where the signature of profileDataInJson is: JSONObject profileDataInJson = null;
Now the resultant JSON is like:
"profileData":{"map":{"ioCinema":"firstValue","ioSIMAvailable":"firstKey","Name":"onePair"}}
Wherein I get an unwanted object called map inserted in my main profileData object.
However when I print this inside the loop I get
{`"ioCinema":"firstValue","ioSIMAvailable":"firstKey","Name":"onePair"}`
Whish is exactly what I want inside profileData object, without nesting the map object.
How do I solve this?
"I am already aware that I can achieve this by converting the type of profileData in D class from JSONObject to String, which will induce escape characters - However I am looking for a generic solution"
EDIT:
map1 is constructed in two ways, depending on user input and both ways are as follows:
if (args.length >= 4 && args[1].equalsIgnoreCase("onePair")) {
map1 = new HashMap<>();
String key1 = args[2];
String value1 = args[3];
map1.put(key1, value1);
profileDataInJson = new JSONObject(gson.toJson(map1));
}
And:
if (args.length >= 1 && args[0].equalsIgnoreCase("update")) {
if (args.length >= 2)
profileData.setName(args[1] != null ? args[1] : "");
if (args.length >= 3)
profileData.setSIMAvailable(args[2] != null ? args[2] : "");
profileDataInJson = new JSONObject(profileData);
}
Signature: ProfileData profileData = new ProfileData();
The thing which puzzles me is when I try to traverse profileData and try to fetch the json object by name "map" I get a nullPointer exception
You don't need to use Gson to convert hashmap to a json object.
Simply use:
profileDataInJson = new JSONObject(map);
Add custom serializer to Gson, so that Gson serialize the org JSON as expected by you.
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(JSONObject.class, new JsonSerializer<JSONObject>() {
#Override
public JsonElement serialize(final JSONObject src, final Type typeOfSrc,
final JsonSerializationContext context) {
return new JsonParser().parse(src.toString()).getAsJsonObject();
}
});
gsonBuilder.create().toJson(map1);
This will return {"ioCinema":"firstValue","ioSIMAvailable":"firstKey","Name":"onePair"}

String[] from ArrayList<Class>

I recently started working on an app that does a request to a server and gets a json response.
The "thing" functioned beautifully until i had to implement new stuff in the list and now i have a hard time to fix it.
Any help is very appreciated:
class RemoteConfig
{
// names and type must match what we get from the remote
String[] username;
ArrayList<accDetails> in_groups;
String[] in_groups_sorted;
class accDetails
{
int group_id;
String group_label;
Boolean _is_system;
}
This is just a part of how the class starts, and here is how the json reponse looks like:
{
"username":[
"mike"
],
"in_groups":[
{
"group_id":2,
"group_label":"All users",
"_is_system":true
},
{
"group_id":4372,
"group_label":"Privileged User",
"_is_system":false
},
{
"group_id":4979,
"group_label":"Supervisor",
"_is_system":false
}
]
}
The problem that i encounter now, is that i have no idea on how to split the in_groups array list and get into String[] in_groups_sorted the value of Group_label if the _is_system value is false.
Any help is highly appreciated.
Thank you,
Mike
After checking the responses, the cleanest and simplest was the one provided by Abbe:
public String[] groupSettings()
{
String[] levels = new String[] {};
if (remoteConfig != null && remoteConfig.in_groups != null){
for (accDetails ad: remoteConfig.in_groups)
{
if (!ad._is_system) {
levels = ArrayUtils.addAll(levels, ad.group_label); ;
}
}
}
return levels;
}
From your question, I suppose the JSON is already parsed and stored in the in_groups field of RemoteConfig class. And you just need to filter the information you need to populate the in_group_sorted field.
Add the following to the RemoteConfig class:
public initGroupSorted() {
// Temporary list, since we don't know the size once filtered
List<String> labels = new ArrayList<>();
for (accDetails ad : in_groups) {
if (ad._is_system) {
groups.add(ad.group_label);
}
}
in_group_sorted = labels.toArray(new String[labels.size()]);
}
if you donĀ“t want to change the way you parse your JSON, you could always do this:
Let accDetails implement Comparable and then use Collections.sort passing in_groups.
if you really want the String[] you could always iterate over in_groups, add to in_groups_sorted and then using Arrays.sort
Mike, let me give you something that should get you going. From your question i got the feeling that your problem was on how to parse the JSON, so before you go write your own parser, consider the following piece of code that i just wrote:
public void createObjects(String rawJSON) {
try {
JSONObject object = new JSONObject(rawJSON);
JSONArray username = object.getJSONArray("username");
JSONArray inGroups = object.getJSONArray("in_groups");
RemoteConfig config = new RemoteConfig();
config.in_groups = new ArrayList<>();
config.username = username.getString(0);
for (int i = 0; i < inGroups.length(); i++) {
JSONObject group = inGroups.getJSONObject(i);
if (!group.getBoolean("_is_system")) {
accDetails details = new accDetails();
details.group_id = group.getInt("group_id");
details.group_label = group.getString("group_label");
details._is_system = false;
config.in_groups.add(details);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
Here is a Java 8 Solution using Stream's filter,sorted, and map methods:
//ArrayList<accDetails> in_groups is already populated
Stream<accDetails> tempStream= in_groups.stream().filter(p -> p._is_system == false);
tempStream= tempStream.sorted((accDetails o1, accDetails o2) -> o1.group_label.compareTo(o2.group_label));
String[] in_groups_sorted = tempStream.map(s -> s.group_label).toArray(String[]::new);
Separated the calls for visibility, but they can be a one liner:
String[] in_groups_sorted = in_groups.stream().filter(p -> p._is_system == false).sorted((accDetails o1, accDetails o2) -> o1.group_label.compareTo(o2.group_label)).map(s -> s.group_label).toArray(String[]::new);

Convert JSON object with duplicate keys to JSON array

I have a JSON string that I get from a database which contains repeated keys. I want to remove the repeated keys by combining their values into an array.
For example
Input
{
"a":"b",
"c":"d",
"c":"e",
"f":"g"
}
Output
{
"a":"b",
"c":["d","e"],
"f":"g"
}
The actual data is a large file that may be nested. I will not know ahead of time what or how many pairs there are.
I need to use Java for this. org.json throws an exception because of the repeated keys, gson can parse the string but each repeated key overwrites the last one. I need to keep all the data.
If possible, I'd like to do this without editing any library code
As of today the org.json library version 20170516 provides accumulate() method that stores the duplicate key entries into JSONArray
JSONObject jsonObject = new JSONObject();
jsonObject.accumulate("a", "b");
jsonObject.accumulate("c", "d");
jsonObject.accumulate("c", "e");
jsonObject.accumulate("f", "g");
System.out.println(jsonObject);
Output:
{
"a":"b",
"c":["d","e"],
"f":"g"
}
I want to remove the repeated keys by combining their values into an array.
Think other than JSON parsing library. It's very simple Java Program using String.split() method that convert Json String into Map<String, List<String>> without using any library.
Sample code:
String jsonString = ...
// remove enclosing braces and double quotes
jsonString = jsonString.substring(2, jsonString.length() - 2);
Map<String, List<String>> map = new HashMap<String, List<String>>();
for (String values : jsonString.split("\",\"")) {
String[] keyValue = values.split("\":\"");
String key = keyValue[0];
String value = keyValue[1];
if (!map.containsKey(key)) {
map.put(key, new ArrayList<String>());
}
map.get(key).add(value);
}
output:
{
"f": ["g"],
"c": ["d","e"],
"a": ["b"]
}
In order to accomplish what you want, you need to create some sort of custom class since JSON cannot technically have 2 values at one key. Below is an example:
public class SomeClass {
Map<String, List<Object>> values = new HashMap<String, List<Object>>();
public void add(String key, Object o) {
List<Object> value = new ArrayList<Object>();
if (values.containsKey(key)) {
value = values.get(key);
}
value.add(o);
values.put(key, value);
}
public JSONObject toJson() throws JSONException {
JSONObject json = new JSONObject();
JSONArray tempArray = null;
for (Entry<String, List<Object>> en : values.entrySet()) {
tempArray = new JSONArray();
for (Object o : en.getValue()) {
tempArray.add(o);
}
json.put(en.getKey(), tempArray);
}
return json;
}
}
You can then retrieve the values from the database, call the .add(String key, Object o) function with the column name from the database, and the value (as the Object param). Then call .toJson() when you are finished.
Thanks to Mike Elofson and Braj for helping me in the right direction. I only wanted to have the keys with multiple values become arrays so I had to modify the code a bit. Eventually I want it to work for nested JSON as well, as it currently assumes it is flat. However, the following code works for what I need it for at the moment.
public static String repeatedKeysToArrays(String jsonIn) throws JSONException
{
//This assumes that the json is flat
String jsonString = jsonIn.substring(2, jsonIn.length() - 2);
JSONObject obj = new JSONObject();
for (String values : jsonString.split("\",\"")) {
String[] keyValue = values.split("\":\"");
String key = keyValue[0];
String value = "";
if (keyValue.length>1) value = keyValue[1];
if (!obj.has(key)) {
obj.put(key, value);
} else {
Object Oold = obj.get(key);
ArrayList<String> newlist = new ArrayList<String>();
//Try to cast as JSONArray. Otherwise, assume it is a String
if (Oold.getClass().equals(JSONArray.class)) {
JSONArray old = (JSONArray)Oold;
//Build replacement value
for (int i=0; i<old.length(); i++) {
newlist.add( old.getString(i) );
}
}
else if (Oold.getClass().equals(String.class)) newlist = new ArrayList<String>(Arrays.asList(new String[] {(String)Oold}));
newlist.add(value);
JSONArray newarr = new JSONArray( newlist );
obj.put(key,newarr);
}
}
return obj.toString();
}

How to sort GSON Array based on a key?

Consider the following is my Array
[
{"id":10,"name":"name10","valid":true},
{"id":12,"name":"name12","valid":false},
{"id":11,"name":"name11","valid":false},
{"id":9,"name":"name9","valid":true}
]
Created a JsonArray out of it, like following code does:
//Create a JSON Parser using GSON library
objJsonParser = new JsonParser();
String strArrayText = [{"id":9,"name":"name9","valid":true}, ...]
JsonArray jsonArrayOfJsonObjects = objJsonParser.parse(strArrayText).getAsJsonArray();
Now, I am trying to sort jsonArrayOfJsonObjects based on name field.
Desired Output:
[
{"id":9,"name":"name9","valid":true},
{"id":10,"name":"name10","valid":false},
{"id":11,"name":"name11","valid":false},
{"id":12,"name":"name12","valid":true}
]
Could anyone help to sort this out with best apporach with respect to Java & Gson?
Your inputs are greatly appreciated.
First of all, the proper way to parse your JSON is to create a class to encapsulate your data, such as:
public class MyClass {
private Integer id;
private String name;
private Boolean valid;
//getters & setters
}
And then:
Type listType = new TypeToken<List<MyClass>>() {}.getType();
List<MyClass> myList = new Gson().fromJson(strArrayText, listType);
Now you have a List and you want to sort it by the value of the attribute id, so you can use Collections as explained here:
public class MyComparator implements Comparator<MyClass> {
#Override
public int compare(MyClass o1, MyClass o2) {
return o1.getId().compareTo(o2.getId());
}
}
And finally:
Collections.sort(myList, new MyComparator());
Try this library method as a simple JSON-level alternative to model classes creation:
/**
* Sort JSON-array by a given key name (numbers or text expected).
*
* #param jsonArray JSON-array to sort.
* #param keyNameToSort Key name to sort by. Expected are integer type
* (sorted ascending) or string type (sorted
* alphabetically).
*/
public static JsonArray
sortJsonArrayByKey(
JsonArray jsonArray,
String keyNameToSort) {
JsonArray sortedJsonArray = new JsonArray();
JsonObject jsonObject = null;
int jsonElementIndex;
TreeMap<Integer, JsonObject> integerSortedObjects = new TreeMap<>();
TreeMap<String, JsonObject> stringSortedObjects = new TreeMap<>();
for (
jsonElementIndex = 0;
jsonElementIndex < jsonArray.size();
jsonElementIndex++) {
try {
// A JSON-Object from JSON-array:
jsonObject =
jsonArray
.get(
jsonElementIndex)
.getAsJsonObject();
} catch (Exception notAnObject) {
}
for (Entry<String, JsonElement> entry : jsonObject.entrySet()) {
// Look for the given key in the JSON-object:
if (
entry.getKey()
.equals(keyNameToSort)) {
try {
// If key is of integer type:
integerSortedObjects.put(
entry
.getValue()
.getAsInt(),
jsonObject);
} catch (Exception notAnInt) {
try {
// If key is of string type:
stringSortedObjects.put(
entry
.getValue()
.getAsString(),
jsonObject);
} catch (Exception neitherIntNorString) {
}
}
}
}
}
// Add sorted by number values first:
for (Integer key : integerSortedObjects.keySet()) {
sortedJsonArray.add(
integerSortedObjects.get(
key));
}
// Add sorted by string values second:
for (String key : stringSortedObjects.keySet()) {
sortedJsonArray.add(
stringSortedObjects.get(
key));
}
return sortedJsonArray;
}
you can use Gson library https://sites.google.com/site/gson/gson-user-guide
to get the Array(the class should implement comparable) and sort with arrays.sort();
Thanks

Looping iterator until it's empty

I want to get all the objects from the list and put them into a Map grouped by creation date, which means the map is like this: Map<String, List<MyObject>>. The MyObject object has a field that stores its creation date.
I've thought of doing a nested while loop that looks like this:
public Map<String, List<Expense>> getExpensesSorted(SortType type){
Map<String, List<Expense>> map = new HashMap<String, List<Expense>>();
List<Expense> expenses = getAllExpenses(budgetId).getExpenses()
.getList();
if (type.equals(SortType.DAY)) {
Iterator<Expense> expIter = expenses.iterator();
while (expIter.hasNext()) {
List<Expense> list = new ArrayList<Expense>();
Expense exp = (Expense) expIter.next();
list.add(exp);
String day = exp.getDate().format("YYYY-MM-DD");
expIter.remove();
while (expIter.hasNext()) {
Expense exp2 = (Expense) expIter.next();
if (exp2.getDate().format("YYYY-MM-DD").equals(day)) {
list.add(exp2);
expIter.remove();
}
}
map.put(day, expenses);
}
} else if (type.equals(SortType.WEEK)) {
...
} else if (type.equals(SortType.TYPE)) {
...
} else if (type.equals(SortType.CATEGORY)) {
...
}
return map;
}
But this is wrong, it only gets all the ones that have the same day as the first element, so my map ends up having only one element.
I seriously don't know how to solve this...
Thanks in advance for any help.
Map<Data, List<MyObject>> result = new HashMap<Data, List<MyObject>>;
for (List<MyObject> list : myMap.values()) {
for (MyObject myObject : list) {
Date date = myObject.getDate();
List<MyObject> newList = result.get(date);
if (newList == null) {
newList = new Arraylist<MyObject>;
result.put(date. newList);
}
newList.add(myObject);
}
}
Something like this should do the job. I didn't compile it though.
while (iter.hasNext()) {
MyObject obj = (MyObject) iter.next();
String day = obj.getDate().format("YYYY-MM-DD");
if(!map.containsKey(day)) {
map.put(day, new ArrayList<MyObject>());
}
List<MyObject> list = map.get(day);
list.add(obj);
map.put(day, list);
}

Categories