Say I have a list of of Objects (say List<User>), something like
[
{
"name": "myName",
"age": 1,
"someField": "foo"
},
{
"name": "otherName",
"age": 2,
"someField": "bar"
},
]
I want to convert this to Set<Map<String, Integer>> such that I get a set of name => age pair. So final result should be [{"myName": 1},{"otherName": 2}]
How can I use the stream and collector to do this?
You can use below piece of code for parsing JSON String and then doing some manipulation with that :
public static void main(String[] args) {
String str = "[ { \"name\": \"myName\", \"age\": 1, \"someField\": \"foo\" }, { \"name\": \"otherName\", \"age\": 2, \"someField\": \"bar\" }, ]";
JSONArray jsonobj = new JSONArray(str);
Map<String,Integer> map = new HashMap<>();
for(int i = 0;i<jsonobj.length();i++){
JSONObject obj = jsonobj.getJSONObject(i);
map.put(obj.getString("name"), obj.getInt("age"));
}
Set<Map<String,Integer>> set = new HashSet<>();
set.add(map);
System.out.println(set);
}
Library used : org.json
I ended up doing this:
final List<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
users.add(user3);
Set<Map<String, Integer>> usersSet =
users
.stream()
.map(u -> Collections.singletonMap(u.getName(), u.getAge()))
.collect(Collectors.toSet());
Here is a possible conversion to Map<String, Integer> with duplicate handling:
final List<User> users = new ArrayList<>();
users.add(new User("name 1", 25));
users.add(new User("name 1", 49));
users.add(new User("name 2", 67));
final Map<String, Integer> nameToAge = users.stream().collect(
Collectors.toMap(
user -> user.name,
user -> user.age,
(user1, user2) -> {
System.out.println("Resolving duplicates...");
return user1;
})
);
In this case the definition for type User is:
public class User {
public final String name;
public final int age;
public User(final String name, final int age) {
this.name = name;
this.age = age;
}
}
Related
I have a json file with which has an array of product items i want to split them based on the category of the item,
This is my json file looks like,
{
"items":[
{
"item-id": 123,
"name": "Cheese",
"price": 8,
"category": "Dairy"
},
{
"item-id": 124,
"name": "Milk",
"price": 23,
"category": "Dairy"
},
{
"item-id": 125,
"name": "Chicken",
"price": 100,
"category": "Meat"
},
{
"item-id": 126,
"name": "Fish",
"price": 45,
"category": "Meat"
}
]
}
i want to split them like like this,
[
{
"category":"Dairy",
"items":[
{
"item-id": 123,
"name": "Cheese",
"price": 8,
"category": "Dairy"
},
{
"item-id": 124,
"name": "Milk",
"price": 23,
"category": "Dairy"
}
]
},
{
"category":"Meat",
"items":[
{
"item-id": 125,
"name": "Chicken",
"price": 100,
"category": "Meat"
},
{
"item-id": 126,
"name": "Fish",
"price": 45,
"category": "Meat"
}
]
}
]
this is the code i tried so far but can't find way to split them like the way i wanted, I'm using java and also i am new to java
import java.io.*;
import java.util.*;
import org.json.simple.*;
import org.json.simple.parser.*;
public class ReadOrderDetails {
#SuppressWarnings("unused")
public static void main(String[] args) {
JSONParser parser = new JSONParser();
JSONObject subOrder = new JSONObject();
JSONArray gitems = new JSONArray();
JSONArray subOrders = new JSONArray();
try {
Object obj = parser.parse(new FileReader("order-details.json"));
JSONObject jsonObject = (JSONObject)obj;
String orderId = (String)jsonObject.get("orderId");
JSONArray items = (JSONArray)jsonObject.get("items");
#SuppressWarnings("rawtypes")
Iterator iterator = items.iterator();
System.out.println("Order Id: " + orderId);
while(iterator.hasNext()) {
JSONObject item = (JSONObject)iterator.next();
if(subOrders.isEmpty()) {
subOrder.put("category", item.get("category"));
gitems.add(item);
subOrder.put("items", gitems);
subOrders.add(subOrder);
} else {
Iterator subOrdersIterator = subOrders.iterator();
for(int i=0; i<subOrders.size(); i++) {
JSONObject sitem = (JSONObject) subOrdersIterator.next();
if(sitem.get("category") == item.get("category")) {
gitems.add(item);
subOrder.put("items", gitems);
subOrders.add(subOrder);
} else {
subOrder.put("category", item.get("category"));
gitems.add(item);
subOrder.put("items", gitems);
subOrders.add(subOrder);
}
}
}
}
} catch(Exception e) {
e.printStackTrace();
}
System.out.println(subOrders);
}
}
and also i'm getting an error at java.util.ConcurrentModificationException but that's not my main question, what i really wnated a way to split them i tried couple of things didn't working
In general, you violated the Single Responsibility principal (SOLID), because your code do two different things together: parse a file and categorize items. You should split these responsibilities.
One of ways how you do this is to create classes that represents your data. Let's assume that these classes are Item, Category, Order and CategorizedItems. Order is a class that represents your source JSONObject and CategorizedItems - represents your result JSONArray.
In this case, you should firstly parse the file into these classes and only after that transform them into JSONArray.
Code sample:
Data classes:
class Order {
private final List<Item> items = new ArrayList<>();
public void addItem(Item item) {
items.add(item);
}
public List<Item> getAllItems() {
return new ArrayList<>(items);
}
}
enum Category {
DAIRY("Dairy"),
MEAT("Meat");
private final String value;
Category(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static Category of(String value) {
for (Category category : values()) {
if (category.value.equals(value)) {
return category;
}
}
throw new IllegalArgumentException("Unknown category");
}
}
class CategorizedItems {
private final Category category;
private final List<Item> items;
public CategorizedItems(Category category, List<Item> items) {
this.category = category;
this.items = items;
}
public Category getCategory() {
return category;
}
public List<Item> getItems() {
return items;
}
}
class Item {
private final long id;
private final String name;
private final long price;
private final Category category;
public Item(long id, String name, long price, Category category) {
this.id = id;
this.name = name;
this.price = price;
this.category = category;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public long getPrice() {
return price;
}
public Category getCategory() {
return category;
}
}
Now you should write methods, for example, static ones in your ReadOrderDetails:
private static JSONArray categorizedItemsToJSONArray(List<CategorizedItems> categorizedItems) {
JSONArray jsonArray = new JSONArray();
categorizedItems.forEach(category -> jsonArray.add(toJSONObject(category)));
return jsonArray;
}
private static JSONObject toJSONObject(CategorizedItems categorizedItems) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("category", categorizedItems.getCategory().getValue());
jsonObject.put("items", itemsToJSONArray(categorizedItems.getItems()));
return jsonObject;
}
private static JSONArray itemsToJSONArray(List<Item> items) {
JSONArray jsonArray = new JSONArray();
items.forEach(item -> jsonArray.add(toJSONObject(item)));
return jsonArray;
}
private static JSONObject toJSONObject(Item item) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("item-id", item.getId());
jsonObject.put("name", item.getName());
jsonObject.put("price", item.getName());
jsonObject.put("category", item.getCategory().getValue());
return jsonObject;
}
private static List<CategorizedItems> categorize(Order order) {
List<CategorizedItems> categorizedItems = new ArrayList<>();
for (Category category : Category.values()) {
categorizedItems.add(
new CategorizedItems(
category,
order.getAllItems()
.stream()
.filter(item -> item.getCategory() == category)
.collect(Collectors.toList())
)
);
}
return categorizedItems;
}
private static Order orderFrom(JSONObject jsonObject) {
JSONArray itemsJsonArray = (JSONArray) jsonObject.get("items");
Order order = new Order();
for (Object jsonArrayMember : itemsJsonArray) {
JSONObject itemJsonObject = (JSONObject) jsonArrayMember;
order.addItem(itemFrom(itemJsonObject));
}
return order;
}
private static Item itemFrom(JSONObject jsonObject) {
long itemId = (Long) jsonObject.get("item-id");
String itemName = (String) jsonObject.get("name");
long itemPrice = (Long) jsonObject.get("price");
Category category = Category.of((String) jsonObject.get("category"));
return new Item(
itemId,
itemName,
itemPrice,
category
);
}
After that you should do the last step. Write a main function like:
public static void main(String[] args) {
JSONParser parser = new JSONParser();
try {
JSONObject orderJsonObject = (JSONObject) parser.parse(new FileReader("order-details.json"));
Order order = orderFrom(orderJsonObject);
List<CategorizedItems> categorizedItems = categorize(order);
JSONArray categorizedItemsJsonArray = categorizedItemsToJSONArray(categorizedItems);
// write categorizedItemsJsonArray to a file here
} catch(IOException | ParseException e) {
throw new RuntimeException(e);
}
}
Now your code is simply readable and clear. You can easily maintain it. I advise you to read about SOLID principals and about code style (I highly recommend Robert Martin. "Clean code"). These topics will help you learn how to write elegant and clear code.
Besides, ConcurrentModificationException is a sign that you do something wrong with collections. In your particular case, I guess (not sure) the problem is modifying the JSONArray while you are iteratting it. Only some of Java collections allow it.
EDIT Fix problem with integers by replacing them with long.
Good luck with Java :)
You can't alter a collection while iterating it. Try this:
Object[] subOrderArray = subOrders.toArray();
for(Object o: subOrderArray) {
JSONObject sitem = (JSONObject) o;
Here you want to group the JSON data by category under items array. The code will be very long if you try to get this done in Java. Just try using SPL, an open-source Java package, to do this. You only need one line of code:
A
1
=json(file("data.json").read()).items.group(#4;~:items)
SPL offers JDBC driver to be invoked by Java. Just store the above SPL script as group.splx and invoke it in a Java application as you call a stored procedure:
…
Class.forName("com.esproc.jdbc.InternalDriver");
con= DriverManager.getConnection("jdbc:esproc:local://");
st = con.prepareCall("call group()");
st.execute();
…
Library Josson can do the job with a very simple statement.
https://github.com/octomix/josson
Deserialization
Josson josson = Josson.fromJsonString(
"{" +
" \"items\":[" +
" {" +
" \"item-id\": 123," +
" \"name\": \"Cheese\"," +
" \"price\": 8," +
" \"category\": \"Dairy\" " +
" }," +
" {" +
" \"item-id\": 124," +
" \"name\": \"Milk\"," +
" \"price\": 23," +
" \"category\": \"Dairy\"" +
" }," +
" {" +
" \"item-id\": 125," +
" \"name\": \"Chicken\"," +
" \"price\": 100," +
" \"category\": \"Meat\"" +
" }," +
" {" +
" \"item-id\": 126," +
" \"name\": \"Fish\"," +
" \"price\": 45," +
" \"category\": \"Meat\"" +
" }" +
" ]" +
"}");
Transformation
JsonNode node = josson.getNode("items.group(category, items:?)");
System.out.println(node.toPrettyString());
Output
[ {
"category" : "Dairy",
"items" : [ {
"item-id" : 123,
"name" : "Cheese",
"price" : 8,
"category" : "Dairy"
}, {
"item-id" : 124,
"name" : "Milk",
"price" : 23,
"category" : "Dairy"
} ]
}, {
"category" : "Meat",
"items" : [ {
"item-id" : 125,
"name" : "Chicken",
"price" : 100,
"category" : "Meat"
}, {
"item-id" : 126,
"name" : "Fish",
"price" : 45,
"category" : "Meat"
} ]
} ]
In our legacy code, we are sending request body in a form of a HashMap, something I cannot change because other applications may be affected.
Sample Hashmap value = {name=name1, age=age1}
However I have problem on using HashMap if there are multiple JSON objects in my request, for example
HashMap<String, Object> map = new HashMap<String, Object>();
for (Person person: Persons) {
map.put("name", "name1");
map.put("age", "age1");
}
If there are 2 or more people, only the last person's name and age will be put in the Map, because the first person's name and age are overridden because they have the same key ("name" and "age").
My desired map value was [{name=name1, age=age1}, {name=name2, age=age2}], but I only got {name=name2, age=age2}
What I did is, in every loop, I put it in JSONArray:
JSONArray jsonArray = new jsonArray();
for (Person person: Persons) {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("name", "name1");
map.put("age", "age1");
jsonArray.put(map);
}
So when I print JSONArray, it is:
[{"name":"name1", "age":"age1"}, {"name":"name2", "age":"age2"}]
But again, I need to transform this into HashMap so I can pass it as parameter to oursendRequest() method.
I tried to use the ObjectMapper in Jackson, but it didn't work.
Is this possible, and how?
I would deserialize the JSON into a List of people first. After that, I would group them by name.
Main.java
package q61078696;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
try {
List<Person> people = loadJSON("q61078696/people.json", Person.class);
Map<String, List<Person>> groupedByName = people.stream()
.collect(Collectors.groupingBy(Person::getName));
System.out.println(groupedByName);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String loadJSON(String resourceName) {
InputStream is = Main.class.getClassLoader().getResourceAsStream(resourceName);
String jsonString = null;
try (Scanner scanner = new Scanner(is, StandardCharsets.UTF_8.name())) {
jsonString = scanner.useDelimiter("\\A").next();
}
return jsonString;
}
public static <E> List<E> loadJSON(String resourceName, Class<E> clazz) throws IOException {
ObjectMapper mapper = new ObjectMapper();
String jsonString = loadJSON(resourceName);
CollectionType typeReference = TypeFactory.defaultInstance().constructCollectionType(List.class, clazz);
return mapper.readValue(jsonString, typeReference);
}
}
Output
{Tom=[{ "name": "Tom", "age": 28 }], Bob=[{ "name": "Bob", "age": 42 }, { "name": "Bob", "age": 21 }], Mary=[{ "name": "Mary", "age": 35 }]}
Person.java
package q61078696;
public class Person {
private String name;
private int age;
public Person() {
this(null, 0);
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
#Override
public String toString() {
return String.format("{ \"name\": \"%s\", \"age\": %d }", this.name, this.age);
}
}
people.json
[
{ "name" : "Bob", "age" : 42 },
{ "name" : "Bob", "age" : 21 },
{ "name" : "Mary", "age" : 35 },
{ "name" : "Tom", "age" : 28 }
]
Dependencies
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
Additional Info
If you want to map by name and avoid grouping (ignoring dupes).
This will throw an IllegalStateException, because there are duplicate keys, to avoid this, you will need to access the map.
Map<String, Person> groupedByName = people.stream()
.collect(Collectors.toMap(Person::getName, Function.identity()));
You can avoid this by specifying a mergeFunction
Map<String, Person> groupedByName = people.stream()
.collect(Collectors.toMap(
Person::getName, // keyMapper
Function.identity(), // valueMapper
(o1, o2) -> o1, // mergeFunction (keep the first occurrence)
TreeMap::new) // mapSupplier
);
You can also specify a supplier in the 4th parameter. I chose a TreeMap to keep the name keys in order.
See: https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toMap-java.util.function.Function-java.util.function.Function-java.util.function.BinaryOperator-
I have a class
public class Person {
private String name;
private String country;
private String city;
private String pet;
private int totalCountryToCityCount;
private int petCount;
public Person(String name, String country, String city, String pet, int total, int petCount) {
this.name = name;
this.country = country;
this.city = city;
this.pet = pet;
this.totalCountryToCityCount = total;
this.petCount = petCount;
}
public String getName() {
return name;
}
public String getCountry() {
return country;
}
public String getCity() {
return city;
}
public String getPet() {
return pet;
}
public int getPetCount() {
return petCount;
}
public int getTotalCountryToCityCount() {
return totalCountryToCityCount;
}
}
and Given a list of Person class, I have do aggregations based upon the different properties of the class.
For eg -
Person person1 = new Person("John", "USA", "NYC", "Max", 1, 2);
Person person2 = new Person("Steve", "UK", "London", "Lucy", 2, 8);
Person person3 = new Person("Anna", "USA", "NYC", "Max", 4, 32);
Person person4 = new Person("Mike", "USA", "Chicago", "Duke", 5, 1);
Person person5 = new Person("Test", "INDIA", "HYD", "Tommy", 4, 32);
Person person6 = new Person("Test1", "INDIA", "HYD", "Tommy", 4, 65);
Person person7 = new Person("Tim", "USA", "Chicago", "Duke", 5, 111);
Person person8 = new Person("Tim", "USA", "Chicago", "Puke", 5, 111);
Person person9 = new Person("Test1", "INDIA", "DELHI", "Tommy", 4, 65);
List<Person> persons = Arrays
.asList(person1, person2, person3, person4, person5, person6, person7, person8,
person9);
Now I need to get a result such that I should get the total "totalCountryToCityCount" based upon the combinations of country and city and I should get total "petCount" based upon combinations of country,city and pet. I am able to get them separately using groupingBy and summingint
private Map<String, Map<String, Integer>> getTotalCountForCountry(List<Person> persons) {
return persons.stream().collect(groupingBy(Person::getCountry, getCityCount()));
}
public Collector<Person, ?, Map<String, Integer>> getCityCount() {
return groupingBy(Person::getCity, summingInt(Person::getTotal));
}
public Map<String, Map<String, Map<String, Integer>>> threeLevelGrouping(List<Person> persons) {
return persons
.stream().collect(
groupingBy(Person::getCountry, groupByCityAndPetName()
)
);
}
private Collector<Person, ?, Map<String, Map<String, Integer>>> groupByCityAndPetName() {
return groupingBy(Person::getCity, groupByPetName());
}
private Collector<Person, ?, Map<String, Integer>> groupByPetName() {
return groupingBy(Person::getPet, summingInt(Person::getPetCount));
}
which gives the result
{USA={Chicago={Puke=111, Duke=112}, NYC={Max=34}}, UK={London={Lucy=8}}, INDIA={DELHI={Tommy=65}, HYD={Tommy=97}}}
{USA={Chicago=15, NYC=5}, UK={London=2}, INDIA={DELHI=4, HYD=8}}
but the actual result which I want is :-
{USA={Chicago={15,{Puke=111, Duke=112}}, NYC={5,{Max=34} }, UK={London={2, {Lucy=8}}, INDIA={DELHI={4, {Tommy=65}}, , HYD={8,{Tommy=97}}}}
is there a way to achieve the same using Java stream API
I also tried using the code -
personList.stream().collect(groupingBy(person -> person.getCountry(), collectingAndThen(reducing(
(a, b) -> new Person(a.getName(), a.getCountry(), a.getCity(), a.getPet(),
a.getTotal() + b.getTotal(), a.getPetCount() + b.getPetCount())),
Optional::get)))
.forEach((country, person) -> System.out.println(country + person));
But was getting the result -
USAPerson{name='John', country='USA', city='NYC'}
UKPerson{name='Steve', country='UK', city='London'}
INDIAPerson{name='Test', country='INDIA', city='HYD'}
with the counts surprisingly removed
What you are looking for really is Collectors::teeing, but only available in java-12:
System.out.println(
persons.stream()
.collect(Collectors.groupingBy(
Person::getCountry,
Collectors.groupingBy(
Person::getCity,
Collectors.teeing(
Collectors.summingInt(Person::getTotalCountryToCityCount),
Collectors.groupingBy(
Person::getPet,
Collectors.summingInt(Person::getPetCount)
),
SimpleEntry::new
)
))));
A back-port for java-8 it is available here.
I have two list of ArrayList where i want to compare merge them final list of ArrayList.
example i have Database calls where i get these items as
List<List<String>> data1 = dao1.getall();
List<List<String>> data2 = dao2.getall();
data1 looks like result set of
"results": [
[
"India",
"Idea",
"30"
],
[
"USA",
"Idea",
"10"
],
[
"irland",
"Idea",
"10"
]
data2 looks like result set of
"results": [
[
"India",
"Idea",
"50"
],
[
"usa",
"Idea",
"30"
],
[
"sweden",
"Idea",
"10"
]
I would like to merge these two List of Lists as shown below by comparing the Country and Operator field.
"results": [
[
"India",
"Idea",
"30",
"50",
"80" ====== sum of the above two values
],
[
"usa",
"Idea",
"10",
"30",
"40"
],
[
"irland",
"Idea",
"10"
"0"
"10"
]
[
"sweden",
"Idea",
"0"
"10" ==== order is very important here
"10"
]
Can anyone help me here?Thanks in advance.
I tried this but not at all working for me.
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Public class Hello{
public static void main(String[] args) {
List<List<String>> list1 = new ArrayList<List<String>>();
List<List<String>> list2 = new ArrayList<List<String>>();
// add data
List<String> datalist1 = new ArrayList<String>();
datalist1.add("India");
datalist1.add("vodafone");
datalist1.add("23");
list1.add(datalist1);
System.out.println(list1);
List<String> datalist2 = new ArrayList<String>();
datalist2.add("India");
datalist2.add("vodafone");
datalist2.add("20");
list2.add(datalist2);
System.out.println(list2);
Collection<List<Country>> list3 = Stream.concat(list1.get(0).stream(), list2.get(0).stream())
.collect(Collectors.toMap(Country::getCountryName, Country::getOperator, Country::merge)).values();
}
private static final class Country {
private final String countryName;
private final String operator;
private final List<String> value;
public Country(String countryName, String name, List<String> values) {
this.countryName = countryName;
this.operator = name;
this.value = values;
}
public String getCountryName() {
return countryName;
}
public String getOperator() {
return operator;
}
public List<String> getValue() {
return value;
}
/*
* This method is accepting both Country and merging data that you need.
*/
public static Country merge(Country country1, Country country2) {
if (country1.getCountryName().equalsIgnoreCase(country2.getCountryName().toLowerCase())
&& country1.getOperator().equalsIgnoreCase(country2.getOperator().toLowerCase())) {
List<String> newValue = country1.getValue();
newValue.add(country2.getValue().get(0));
Integer Total = Integer.parseInt(country1.getValue().get(0)) + Integer.parseInt(country2.getValue().get(0));
newValue.add(Total.toString());
return new Country(country1.getCountryName(), country1.getOperator(), newValue);
}
return new Country(country1.getCountryName(), country1.getOperator(), country1.getValue());
}
}
}
If you are using java8, you could use Map and merge option here. You just need to implement your own merge method, that will join data from both Elements.
In below example I have called your class Country.
#Test
public void should_create_plain_text_tag_for_booking_init_rq() {
List<Country> data1 = new ArrayList<>();
List<Country> data2 = new ArrayList<>();
Collection<Country> results = Stream.concat(data1.stream(), data2.stream())
.collect(Collectors.toMap(
Country::getCountryName, // I asume that you want to merge Data with same country name.
Function.identity(),
Country::merge)
).values();
}
static class Country {
private final String countryName;
private final String name;
private final List<Integer> values;
public Country(String countryName, String name, List<Integer> values) {
this.countryName = countryName;
this.name = name;
this.values = values;
}
/* This method is accepting both Country and merging data that you need. */
public static Country merge(Country country1, Country country2) {
List<Integer> newValue = country1.getValues();
newValue.addAll(country2.getValues());
// compute sum here;
return new Country(country1.getCountryName(), country1.getName(), newValue);
}
// getters
}
I have a JSON-String array, where its entry has the following properties, firstName, lastName, loginName, Country, phoneNumber, and status. Here's an example
[
{
"firstName": "Patrick",
"lastName": "Smith",
"loginName":"test0003#test.com",
"Country":"US",
"phoneNumber": "287 125-1434",
"status": "340"
},
{
"firstName": "Bob",
"lastName": "Williams",
"loginName":"test0002#test.com",
"Country":"US",
"phoneNumber": "213 111-9943",
"status": "215"
},
{
"firstName": "John",
"lastName": "Johnson",
"loginName":"test0001#test.com",
"Country":"DE",
"phoneNumber": "212 555-1234",
"status": "167"
},
{
"firstName": "George",
"lastName": "Jones",
"loginName":"test0004#test.com",
"Country":"FR",
"phoneNumber": "217 987-2634",
"status": "340"
}
]
Now, I want to search for a specific entry based on the properties loginName and status
For example
loginName: test0001#test.com
status: 167
{
"firstName": "John",
"lastName": "Johnson",
"loginName":"test0001#test.com",
"Country":"DE",
"phoneNumber": "212 555-1234",
"status": "167"
}
What would be the most optimized solution?
I use JSONObject and here is another way to parse.
1. Parse using JSONArray
2. Loop the array and read into UserProfile objects
3. Store it in HashMap to get using key
import java.util.HashMap;
import java.util.Map;
import org.json.*;
class UserProfile{
String loginName;
int status;
String firstName;
String key;
UserProfile(){
}
String getLoginName(){
return loginName;
}
String getFirstName(){
return firstName;
}
void setKey(String key){
this.key = key;
}
void setLoginName(String loginName){
this.loginName = loginName;
}
void setStatus(int status){
this.status = status;
}
void setFirstName(String firstName){
this.firstName = firstName;
}
}
public class JSONObjParser {
public static void main(String[] args){
Map<String, UserProfile> map = new HashMap<String, UserProfile>();
String msg ="[{ firstName: Patrick, lastName: Smith, loginName:test0003#test.com, Country:US, phoneNumber: 287 125-1434, status: 340 }, { firstName: Bob, lastName: Williams, loginName:test0002#test.com, Country:US, phoneNumber: 213 111-9943, status: 215 }]";
JSONArray jsonarray = new JSONArray(msg);
for (int i = 0; i < jsonarray.length(); i++) {
JSONObject jsonobject = jsonarray.getJSONObject(i);
String loginName = jsonobject.getString("loginName");
int status = jsonobject.getInt("status");
String firstName = jsonobject.getString("firstName");
UserProfile profile = new UserProfile();
profile.setFirstName(firstName);
profile.setLoginName(loginName);
profile.setStatus(status);
String key = loginName + Integer.toString(status);
map.put(key, profile);
}
for (String key : map.keySet()) {
UserProfile profile = map.get(key);
System.out.println("Key = " + key + ", FirstName = " + profile.getFirstName());
}
}
}
Using Jackson, this is the crudest snippet I can think of:
private static ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) throws IOException {
System.out.println(filterJsonArray(JSON, "loginName", "test0001#test.com", "status", "167"));
}
public static String filterJsonArray(String array, String keyOne, Object valueOne, String keyTwo, Object valueTwo) throws IOException {
Map[] nodes = mapper.readValue(array, HashMap[].class);
for (Map node : nodes) {
if (node.containsKey(keyOne) && node.containsKey(keyTwo)) {
if (node.get(keyOne).equals(valueOne) && node.get(keyTwo).equals(valueTwo)) {
return mapper.writeValueAsString(node);
}
}
}
return null;
}
Of course it will only returns the first match to the given pairs. If you need all the values, make it return a list instead and populate it inside the loop.