I am getting response like this from server,
[
{
"id":"b2",
"type":"ball"
},
{
"id":"a1",
"type":"apple",
},
{
"id":"d4",
"type":"dog",
},
{
"id":"c3",
"type":"cat",
}
]
but I need to sort the above response data based on alphabets wise, based on "type". And then display the list, I am using model to parse the data .
What data structure are you parsing the JSON into? If, for example, you have an Item class similar to:
class Item {
Item(String id, String type) { ... }
String getType() { ... }
String getId() { ... }
}
And you parse that JSON into some kind of List<Item>, then the following should work for sorting by type (API level 24+), see Comparator.comparing:
List<Item> items = ...; // however you parse the JSON
Comparator<Item> byType = Comparator.comparing(Item::getType);
Collection.sort(items, byType); // items will now be sorted
For a more general approach that will work with older API levels, you can define the byType comparator like this:
Comparator<Item> byType = new Comparator<Item> {
public int compare(Item a, Item b) {
return a.getType().compareTo(b.getType());
}
};
Can't you make your class Comparable and use :
type.compareTo()
In your own compareTo method in oder to be able to sort them by type?
Json is an object is an unordered set of name/value pairs.
See http://json.org.
Convert to Object to sort.
Convert it to an Object and implement Comparable<> Interface. The use:
CompareTo()
Related
My sample request
{
"requestModel":{
"CUSTID": "100"
},
"returnParameters":[
{
"name":"NETWORK/NETID",
"datatype":"String",
"order":"asc",
"sequence":1
},
{
"name":"INFODATA/NAME",
"datatype":"String",
"order":"asc",
"sequence":1
},
{
"name":"SOURCE/SYSTEM",
"datatype":"int",
"order":"asc",
"sequence":2
},
]
}
Sample Response
Below is my dynamically generated Map format of json response[Response parameters will be different each time based on the request params],
"responseModel":{
"documents": [
{
"NETWORK":[
{"NETID":"1234"},
{"ACT":"300"}
],
"SOURCE": {
"SYSTEM":"50"
},
"INFODATA":{
"NAME":"PHIL"
}
},
{
"NETWORK":[
{"NETID":"1234"},
{"ACT":"300"}
],
"SOURCE": {
"SYSTEM":"100"
},
"INFODATA":{
"NAME":"PHIL"
}
}
]
}
Problem Statement
I need to do multi level sorting based on the "returnParameters" in the request which is dynamic...
"order" indicates ascending (or) descending and sequence indicates the the priority for ordering like (group by in sql query)
Code
Map<String,Object> documentList = new HashMap<String,Object>();
JSONObject jsonObject= new JSONObject(response.getContent());
response.getContent() -> is nothing but it contains the above json response in Map format.
Now I converting the map to list of json object
JSONArray jsonArray= (JSONArray)jsonObject.get("documents");
ArrayList<JSONObject> list = new ArrayList<>();
for(int i=0;i<jsonArray.length();i++){
list.add((JSONObject) jsonArray.get(i));
}
Collections.sort(list, new ResponseSorter());
public class ResponseSorter implements Comparator<JSONObject> {
#Override
public int compare(JSONObject o1,JSONObject o2){
String s1= (String)((JSONObject) o1.get("NETWORK")).get("NETID");
String s2= (String)((JSONObject) o2.get("NETWORK")).get("NETID");
int i1=Integer.parseInt(s1);
int i2=Integer.parseInt(s2);
return i1-i2;
}
}
I'm stuck here to proceed further. Created one for Integer comparator, .Should I create for each dataType? also
I need to dynamically construct the composite comparator by parsing the "retunrParameters" , below sample is hard coded, how to create dynamically??
(String)((JSONObject) o1.get("NETWORK")).get("NETID"); -> this should be dynamically framed , since "returnParameters" are also dynamic in nature.[NETWORK & NETID may not be come in another request],so my comparator should be capable enough to frame the keys in runtime
Would anyone able to assist me to create composite comparator in runtime for sorting?
NOTE:- Java Pojo cannot be created as the response is dynamic nature
In your case a simple comparator that's provided with the sort parameters might be easier to understand than a bunch of nested comparators.
Basically you'd do something like this:
class ReturnParameterComparator implements Comparator<JSONObject> {
private List<ReturnParameter> params; //set via constructor
public int compare( JSONObject left, JSONObject right) {
int result = 0;
for( ReturnParameter p : params ) {
//how exactly you get those values depends on the actual structure of your data and parameters
String leftValueStr = left.get( p );
String rightValueStr = right.get( p );
switch( p.datatype ) {
case "String":
result = String.compare( leftValueStr, rightValueStr );
break;
case "int":
//convert and then compare - I'll leave the rest for you
}
//invert the result if the order is descending
if( "desc".equals(p.order ) {
result += -1;
}
//the values are not equal so return the order, otherwise continue with the next parameter
if( result != 0 ) {
return result;
}
}
//at this point all values are to be considered equal, otherwise we'd have returned already (from the loop body)
return 0;
}
}
Note that this is just a stub to get you started. You'll need to add quite a few things:
how to correctly use the parameters to extract the values from the json objects
how to convert the data based on the type
how to handle nulls, missing or incompatible data (e.g. if a value should be sorted as "int" but it can't be parsed)
Adding all those would be way too much for the scope of this question and depends on your data and requirements anyway.
EDITED after additional questions in comments and additional info in description
You have a couple of steps you need to do here to get to the solution:
You want to have the sorting be dynamic based on the value of the property sequence in the request. So you need to parse the names of those returnParameters and put them in order. Below I map them to a List where each String[] has the name and order (asc/desc). The list will be ordered using the value of sequence:
List<String[]> sortParams = params.stream() // params is a List<JSONObject>
.filter(json -> json.containsKey("sequence")) // filter those that have "sequence" attribute
.sorted( sequence ) // sorting using Comparator called sequence
.map(jsonObj -> new String[]{jsonObj.get("name").toString(), jsonObj.get("order").toString()} )
.collect(Collectors.toList());
Before this you'll map the objects in the returnParameters array in the request to a List first.Then the stream is processed by 1. filtering the JSONObjects to only keep those that have prop sequence, 2. sorting the JSONObjects using comparator below. 3. from each JSONObject get "name" & "order" and put them in a String[], 4. generate a list with those Arrays. This list will be ordered in the order of attributes with priority 1 first, then priority 2, etc, so it will be ordered in the same way you want the JSONObjects ordered in the end.
Comparator<JSONObject> sequence = Comparator.comparingInt(
jsonObj -> Integer.valueOf( jsonObj.get("sequence").toString() )
);
So for your example, sortParams would look like: List( String[]{"NETWORK/NETID", "asc"}, String[]{""INFODATA/NAME", "asc"}, String[]{"SOURCE/SYSTEM", "asc"} )
Then you need to write a method that takes two params: a JSONObject and a String (the path to the property) and returns the value of that property. Originally I advised you to use JSONAware interface and then figure out the sub-class, but let's forget about that for now.
I am not going to write this method for you. Just keep in mind that .get(key) method of JSON.Simple always yields an Object. Write a method with this signature:
public String findSortValue(JSONObject doc, String path){
// split the path
// find the parent
// cast it (parent was returned as an Object of type Object)
// find the child
return value;
}
Write a generic individual comparator (that compares values of just one sort attribute at a time) and figures out if it's an Int, Date or regular String. I would write this as a regular method so it'll be easier to combine everything later on. Since you had so many questions about this I've made an example:
int individualComparator(String s1, String s2){
int compResult = 0;
try{
int numeric1 = Integer.parseInt(s1);
int numeric2 = Integer.parseInt(s2);
compResult = numeric1 - numeric2; // if this point was reached both values could be parsed
} catch (NumberFormatException nfe){
// if the catch block is reached they weren't numeric
try{
DateTime date1 = DateTime.parse(s1);
DateTime date2 = DateTime.parse(s2);
compResult = date1.compareTo(date2); // compareTo method of joda.time, the library I'm using
} catch (IllegalArgumentException iae){
//if this catch block is reached they weren't dates either
compResult = s1.compareTo(s2);
}
}
return compResult;
};
Write an overall Comparator that combines everything
Comparator<JSONObject> overAllComparator = (jsonObj1, jsonObj2) -> {
List<String[]> sortValuesList = sortParams.stream()
.map(path -> new String[]{ findValueByName(jsonObj1, path), findValueByName(jsonObj2, path) } )
.collect(Collectors.toList());
//assuming we always have 3 attributes to sort on
int comp1 = individualComparator(sortValuesList.get(0)[0], sortValuesList.get(0)[1]);
int comp2 = individualComparator(sortValuesList.get(1)[0], sortValuesList.get(1)[1]);
int comp3 = individualComparator(sortValuesList.get(2)[0], sortValuesList.get(2)[1]);
int result = 0;
if (comp1 != 0){
result = comp1;
} else if (comp2 != 0){
result = comp2;
} else{
result = comp3;
}
return result;
};
This Comparator is written lambda-style, for more info https://www.mkyong.com/java8/java-8-lambda-comparator-example/ .
First it takes the ordered list of sortParams we made in step 1 and for each returns an array where position 0 has the value for jsonObj1, and position 1 has the value for jsonObj2 and collects it in sortValuesList. Then for each attribute to sort on, it get the result of the individualComparatormethod. Then it goes down the line and returns as result of the overall comparison the first one that doesn't result in 0 (when a comparator results in 0 both values are equal).
The only thing that's missing now is the asc/desc value from the request. You can add that by chainingint comp1 = individualComparator(sortValuesList.get(0)[0], sortValuesList.get(0)[1]); with a simple method that takes an int & a String and multiplies the int by -1 if the String equals "desc". (Remember that in sortParams we added the value for order on position 1 of the array).
Because the first list we made, sortParams was ordered based on the priority indicated in the request, and we always did evertything in the order of this list, the result is a multi-sort in this order. It is generic & will be determined dynamically by the contents of returnParams in the request. You can apply it to your list of JSONObjects by using Collections.sort()
My suggestion: learn about:
Comparator.comparing which allows you to build your comparator by specifying the key extractor
Comparator.thanComparing which allows you to chain multiple comparators. The comparators later in the chain are called only if predecessors say the objects are equal
A tutorial if you need one: https://www.baeldung.com/java-8-comparator-comparing
I receive a List<org.apache.avro.generic.GenericRecord> with the data contents as shown below (JSON notation used for clarity). How can I best hold these record types using Java?
Record 1:
[
{
"serial_no" : "x",
"data1" : "d"
},
{
"serial_no" : "y",
"data2" : "d2"
},
............................MANY MORE
]
Record 2:
[
{
"id":"x",
"type":"A"
},
{
"id" : "x",
"type" : "B"
},
{
"id" : "y",
"type" : "A",
},
{
"id" : "y",
"type" : "B"
}
]
As you see here, each serial number has two records in record2. serial_no in record1 is same as id in record2.
My Goal is:
Fatsest way to find these two records.
Solution I think:
Create a map like
map.put("x", [map.put("A",List), map.put("B",List)]);
But I feel like, its a complex structure. Because map contains list of maps[each map is Map<String,List<Map<String,String>>>].
Any suggestions?
EDIT
Each entries in records are avro GenericRecord
It looks as if you are trying to parse JSON using Java. Why not use a specific library for that?
Like the basic http://www.json.org/java/ or Google's https://github.com/google/gson
Otherwise, I do not think that the complex structure you are proposing is especially slow. You might want to design your own object class to hold the data if you think it is more efficient or easier to get to the data.
EDIT
Based on your question I assumed JSON was the format you received it in, sorry.
I would just create a wrapper for GenericRecord, or subclass it. Then add the methods that you need to extract the data, or make it Comparable for sorting.
Something along the lines of
public class MyRecord extends GenericRecord implements Comparable<MyRecord>
{
// Determine the type
public int getType()
{
if ( this.get( "id") != null )
return 2;
return 1;
}
// Add methods that allow you to retrieve the serial field from any of the two record types
public String getId()
{
if ( this.get( "id") != null )
return (String)this.get("id");
return (String)this.get("serial_no");
}
// add comparator methods that will allow you to sort the list, compare based on Id, etc
#Override
public int compareTo(MyRecord another)
{
// Just a simple example
return this.getId().compareTo( another.getId() );
}
}
Define classes for repeated entries:
class SerialNoData {
String serialNo;
Object data;
}
and
class IdType {
String id;
String type;
}
; once parsed put the instances into arrays or Lists to get the desired format.
How complex the map is doesn't really make a difference for the speed. Depending on the type of Map you use getting a list of records will be constant time (with a reasonably small overhead). Finding something in the sublists will then be O(n), since you need to iterate through the list and look at all the Maps.
Define following classes
class Serial{
String serial-no;
String data;
List<IdType> idTypes;
}
class IdType{
String id;
String type;
}
After that you can use jackson or any kind of JSON processing library.
I am using Jersey as a client to parse JSON into Java objects,
The problem is that I am using a service that returns different types of responses that should be mapped to a different Java object each time, so I need a way to step into the parsing process and make an arbitrary decision to tell Jersey what is the exact type of object to parse each time.
EDIT:
For example if I have Java Classes A, B, and C and the Json Response as follows:
Data{
-list {
-0 :{Result should be mapped to A}
-1 :{Result should be mapped to B}
-2 :{Result should be mapped to C}
}
}
and the list is ArrayList (or can be ArrayList of a super class for the three classes). Now when I ask Jersey to parse this JSON response, It will find an ArrayList when handling list and dosen't know what's the exact type of the object to parse into, so it convert the data inside -0, -1, -2 to a linkedHashMap as a key/value pairs.
I use jackson in a jersey client to map json to a hashMap but the mapper will work for pojo's as well. Hope the following helps.
Get the list elements into an array/list.
Loop through and determine the correct class for each element.
Pass each list element and its respective class name to a method that handles the mapping and returns an object.
import ...
import com.fasterxml.jackson.databind.ObjectMapper;
public class RequestProcessor {
void jerseyClient(){
//rest request
WebResource resource = ...
ClientResponse responseContent = resource.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
List list = parseJSonResponse(responseContent);
for (String jsonListElement : list){
//determine correct class for jsonListElement
//Pass jsonListElement and class name to mapper method
Object obj = mapElementToObject(jsonListElement , className);
//do something with obj
}
}
private Object mapElementToObject(String jsonString, String className){
ObjectMapper mapper = new ObjectMapper();
Object obj = mapper.readValue(jsonString, Class.forName(className);
return obj;
}
private List parseJsonResponse(responseContent){
//use regexp to replace unnecessary content in response so you have a string
//that looks like -0:{element values}-1:{element values}-2:{element values}
//then split the string on "-.*\\:" into a array/list
//return the list
}
}
I'm a beginner Java and Gson user and have been able to apply it to my needs. I now have some JSON data that I need to parse into a spinner as follows:
{
"lang":[
"arabic",
"bengali",
"dutch-utf8",
"eng_root",
"english",
"english-utf8",
...
],
"themes":{
"blue":{
"chinese_ibm500":1,
"spanish":1,
"bengali":1,
"japanese":1,
"english":1,
"russian":1,
"french-utf8":1,
"eng_root":1,
"arabic":1,
"spanish-utf8":1,
"portuguese":1,
...
},
"green":{
"eng_root":1,
"engmonsoon":1,
"english":1
...
},
"red":{
"chinese_ibm500":1,
"spanish":1,
"bengali":1,
...
}
}
}
So from this JSON I need 2 things:
1) the array under lang is dynamic as for its the languages installed on the server. How could I get all the entries?
I have a class as follows but im stuck as to what I should do after I return lang
public class ListData {
private List<Language> lang;
public List<Language> getLang {
return lang;
}
public static class Language {
???
}
}
2) after understanding 1 I might be able to figure this one out. Under themes are colors which again can be more or less {purple, orange, whatever}. I just need a list of those themes, as far as I'm concerned I don't need to know the languages for each.
Feel like this question is turning into a book. I have searched SO extensively and hate asking questions but I'm pretty stumped. Thanks in advance.
1) In order to get the "lang" array, just modify
private List<Language> lang;
for
private List<String> lang;
Since the elements inside "lang" array are all strings, you don't need any class Language to store those values, they'll be parsed correctly as strings. And it doesn't matter how many strings the array contains...
2) In order to parse "themes", you have to notice that it's not an array [ ], but an object { }, so you do need to parse it with some object, and the most suitable class here is a Map like this:
private Map<String, Object> themes;
Note: as you said that you don't need the data under "blue", "green", etc... you can just Object as the value type in the map, otherwise you'd need some class...
Using a Map here allows you to have an arbitrary number of themes in your JSON response.
So in summary, you just need a class like:
public class ListData {
private List<String> lang;
private Map<String, Object> themes;
//getters & setters
}
and parse your JSON with:
Gson gson = new Gson();
ListData data = gson.fromJson(yourJsonString, ListData.class);
Your list of langs will be under:
data.getLang();
and your list of themes will be under:
data.getThemes().keySet();
I suggest you to take a look at Gson documentation. It's quite short and clear and you'll understand everything much better...
there is a dependent list
Dependents contains
String emp_Id, name etc,
List<Dependent> dependentList;
dependentList contains all the dependent information of an employee.
how to get the list of dependents by providing the emp_Id ?
for example an employee will have 2 or 3 dependents.
ok i dont want to loop over it.
i tried binary search on list using comparator but it does not return the desired data.
already i will loop over the employee list... subsequently i should get the depends of the particular employee...
what will be the best & efficient solution ?
Binary search works only if the list is sorted according to the comparator. For lists that are not sorted or sorted according to other criteria, you have to filter them.
Either loop though the list and do whatever you want to do in the loop body
Or use a filter functionality from a library
If you want to filter, then I recommend Google Collections (or Google Guava, which is a superset of Google collections):
Collection<Dependent> filtered = Collections2.filter(dependentList, new Predicate<Dependent>() {
public boolean apply(Dependent from) {
return from != null && from.getId().equals(id_so_search_for);
}
}
Of course, you are not restricted to .equals(), but can match according to any operation required (e.g. by regular expression).
If searches for one kind of data heavily outweight searches for any other kind of data, then storing them in a Map<kind-of-id, Dependent> may be a good choice as well. You still can retrieve a collection of all stored objects using Map.values().
If one key maps to several items, then either use a Map<kind-of-id, Collection<Dependent>> or (better) consider using existing Multimap functionality: com.google.common.collect.Multimap or org.apache.commons.collections.MultiMap (note that Apache Commons does not have a genericized version of this).
You want to model relationships. I guess, you have the basic dependencies:
Supervisor is-a Employee
Supervisor has-many Employees (Dependants in your case)
So a very basic implementatin could go like this:
public class Employee {
int emp_id;
// more fields, more methods
}
public class Supervisor extends Employee {
private List<Employee> dependants = new ArrayList<Employee>();
// more fields, more methods
public List<Employee> getDependants() {
return dependants;
}
}
public class StaffDirectory {
private Map<Integer, Employee> staff = new HashMap<Integer, Employee>();
public static List<Employee> getAllDependantsOf(int employeeId) {
Employee employee = staff.get(employeeId);
if (employee instanceof Supervisor) {
return ((Supervisor) employee).getDependants());
} else {
return Collections.emptyList();
}
}
}
What have you tried so far? Do you have anything written?
Here is a general guess:
int employeeToFind = 10;//the id to search for
for(Dependant dep : dependentList ) {
if(dep.getEmployeeId() == employeeToFind) {
//do something
}
}
You could also store dependents in a Hashtable<Integer employeeId,List<Dependent>>(); keyed by EmployeeId for an easy lookup.
As alzoid mentioned, a HashMap or HashTable is the perfect data structure for this task. If you have any chance to load your instances of Dependent into such an object, do so.
Still, have this delicious code:
String emp_Id //IDs are usually integer, but I'll go with your example
List<Dependent> dependentList; //assume this is populated
List<Dependent> desiredSublist = new ArrayList<Dependent>();
for(Dependent dep:dependentList){
//make sure to compare with equals in case of Id being String or Integer
if(dep.getId().equals(emp_Id)){
desiredSubList.add(dep);
}
}
//desiredSublist now contains all instances of Dependent that belong to emp_Id.