How to construct this JSON array of JSONObjects - java

I have a Problem which I have to solve in Java.I have a data in YAML where the data is in this structure
600450:
STATE:STATE1
CITY:CITY1
ID:1
CONTACT:1234
600453:
STATE:STATE1
CITY:CITY1
ID:2
CONTACT:3456
600451:
STATE:STATE2
CITY:CITY2
ID:3
CONTACT:2234
.....
I converted this into JSONObject but I am strugling to change this into this JSONObject object where the structure should be of this form
{
STATE1:
{[
CITY1:{
[{ID:1,CODE:600450,CONTACT:1234},
{ID:2,CODE:600453,CONTACT:3456}
]
},
CITY2:{
[
{ID:3,CODE:600451,CONTACT:1234}
]
}
]}
}
I have almost lost a hour by doing different Things with JSONObject and JSONArray and then switched to HashMap and ArrayList of HashMap but I am not able to get it !
This was my try I am sure this is absurd I know that How to achieve this in Java .

Assuming that your converted initial JSON looks like this:
{
"600450": {
"STATE": "STATE1",
"CITY": "CITY1",
"ID": 1,
"CONTACT": 1234
},
"600451": {
"STATE": "STATE2",
"CITY": "CITY2",
"ID": 3,
"CONTACT": 2234
},
"600453": {
"STATE": "STATE1",
"CITY": "CITY1",
"ID": 2,
"CONTACT": 3456
}
}
Here is a static method that does the full conversion to your desired format:
static JSONObject convert(JSONObject initial) {
// STATE -> CITY -> Address[]
Map<String, Map<String, List<Map<String, Object>>>> stateToCityToAddresses = new HashMap<>();
// Get list of codes
String[] codes = JSONObject.getNames(initial);
// Loop over codes - "600450", "600451", "600453", ...
for (String code : codes) {
// Get the JSONObject containing state data
JSONObject state = initial.getJSONObject(code);
// Extract information from state JSONObject
String stateName = state.getString("STATE");
String cityName = state.getString("CITY");
long id = state.getLong("ID");
long contact = state.getLong("CONTACT");
// Some Java 8 awesomeness!
List<Map<String, Object>> addresses = stateToCityToAddresses
.computeIfAbsent(stateName, sn -> new HashMap<>()) // This makes sure that there is a Map available to hold cities for a given state
.computeIfAbsent(cityName, cn -> new ArrayList<>()); // This makes sure that there is a List available to hold addresses for a given city
// Save data in a map representing a json object like: {"CONTACT":1234,"CODE":600450,"ID":1}
Map<String, Object> address = new HashMap<>();
address.put("ID", id);
address.put("CONTACT", contact);
address.put("CODE", Long.parseLong(code));
// Add the address under city
addresses.add(address);
}
// Just use the JSONObject.JSONObject(Map<?, ?>) constructor to get the final result
JSONObject result = new JSONObject(stateToCityToAddresses);
// You can sysout the result to see the data
// System.out.println(result);
return result;
}

Allow me to play Devil's Advocate.
I think you may risk deviating away from the relationships that have been described in the .yaml. You should try to avoid embedding any application-specific logic inside of your data models, since your assumptions may land you in trickier places in the future.
Generally speaking, you should respect the initial form of the data and interpret the relationships or associated logic with the flexibility of runtime processing. Otherwise, you end up serializing data structures which do not correlate directly with the source, and your assumptions may land you in a hot spot.
The "real" JSON equivalent, I suspect; would look something like this:
{
"600450": {
"STATE": "STATE1",
"CITY": "CITY1",
"ID": 1,
"CONTACT": 1234
},
"600453": { ...etc }
}
It would still be possible to pair these relationships. If you want to relate all Objects by city, you could first separate them into bins. You could do this by using a Map which relates a String city to a List of JSONObjects:
// This will be a Map of List of JSONObjects separated by the City they belong to.
final Map<String, List<JSONObject>> mCityBins = new ArrayList();
// Iterate the List of JSONObjects.
for(final JSONObject lJSONObject : lSomeListOfJSONObjects) {
// Fetch the appropriate bin for this kind of JSONObject's city.
List<JSONObject> lBin = mCityBins.get(lJSONObject.get("city"));
// Does the right bin not exist yet?
if (lBin == null) {
// Create it!
lBin = new ArrayList();
// Make sure it is in the Map for next time!
mCityBins.add(lJSONObject.get("city"), lBin);
}
// Add the JSONObject to the selected bin.
lBin.add(lJSONObject);
}
Whilst you process the JSONObjects, whenever you come across a city whose key does not exist in the Map, you can allocate a new List<JSONObject>, add the current item to that List and add it into the Map. For the next JSONObject you process, if it belongs to the same city, you'd find the existing List to add it to.
Once they're separated into bins, generating the corresponding JSON will be easy!

As I see your git repo you just try to convert first Json structure to new Structure you want. I must to tell you probably you could create this structure directly from YAML file.
As I don't see your first Json structure so I guess that must be something like this :
{600450:{STATE:STATE1 , CITY:CITY2 , ...} , ...}
If this is true so this way can help you :
public static JSONObject convert(JSONObject first) throws JSONException {
HashMap<String , HashMap<String , JSONArray>> hashMap = new HashMap<>();
Iterator<String> keys = first.keys();
while (keys.hasNext())
{
String key = keys.next();
JSONObject inner = first.getJSONObject(key);
String state = inner.getString("STATE");
HashMap<String , JSONArray> stateMap =
hashMap.computeIfAbsent(state , s -> new HashMap<>());
String city = inner.getString("CITY");
JSONArray array = stateMap.computeIfAbsent(city , s->new JSONArray());
JSONObject o = new JSONObject();
o.put("ID" , inner.getInt("ID"));
//in this section you could create int key by calling Integer.parse(String s);
o.put("CODE" , Integer.valueOf(key));
o.put("CONTACT" , inner.getInt("CONTACT"));
array.put(o);
}
JSONObject newStructureObject = new JSONObject();
for(String stateKey:hashMap.keySet())
{
JSONArray array = new JSONArray();
JSONObject cityObject = new JSONObject();
HashMap<String , JSONArray> cityMap = hashMap.get(stateKey);
for(String cityKey : cityMap.keySet())
{
cityObject.put(cityKey , cityMap.get(cityKey));
}
array.put(cityObject);
newStructureObject.put(stateKey , array);
}
return newStructureObject;
}

Related

Parse a URI String into a JSON object

I've got the URI like this:
http://localhost:8080/profile/55cbd?id=123&type=product&productCategories.id=ICTLicense&productCategories.name.firstName=Jack&productCategories.name.lastName=Sparrow&groups=a&groups=b
I need a JSON object like this:
{
"id": "123",
"type": "product",
"productCategories": {
"id": "ICTlicense",
"name": {
"firstName": "Jack",
"lastName": "Sparrow"
}
},
"groups":["a", "b"]
}
Query parameters nesting can be dynamic, like for example abc.def.ghi.jkl.mno=value1&abc.xyz=value2 will result in
{
"abc": {
"def": {
"ghi": {
"jkl": {
"mno": "value1"
}
}
},
"xyz": "value2"
}
}
I have tried this but it can not handle the nesting.
final Map<String, String> map = Splitter.on('&').trimResults().withKeyValueSeparator('=').split(request.getQuery());
How to do this in Java?
With the way that your URI string is structured it wouldn't be possible to nest it the way you'd like, here's why.
id=123 This is simple enough since id would just be an int
productCategories.id=ICTLicense This would also be simple enough since we can assume that productCategories is an object and id is a key inside of the object
However, it gets more complex when you start using arrays, for instance:
&groups=a&groups=b
How do you know that groups is an array, and not simply a key called groups with a value of a or b
Also, you store all your data to Map<String, String>,
This wouldn't support arrays as it stores objects to key-value, so you wouldn't be able to have multiple keys of groups with different values.
I'd also suggest you use a library like Gson and parse your data to a JsonObject
https://github.com/google/gson
If you were to use Gson, you could do something similar to this:
public JsonObject convertToJson(String urlString) {
//Create a JsonObject to store all our data to
JsonObject json = new JsonObject();
//Split the data part of the url by the props
String[] props = urlString.split("&");
//Loop through every prop in the url
for (String prop : props) {
//Create a list of all the props and nested props
String[] nestedProps = prop.split("=")[0].split("\\.");
//Get the actual key for our prop
String key = nestedProps[nestedProps.length - 1];
//Get the value
String value = prop.split("=")[1];
//Loop through our props array
for (String nestedProp : nestedProps) {
//If the property already exists, then skip
if (json.has(nestedProp)) continue;
//If the prop is the key, add it to the json object
if(nestedProp.equalsIgnoreCase(key)) {
json.addProperty(nestedProp, value);
continue;
}
//If the above checks fail, then create an object in the json
json.add(nestedProp, new JsonObject());
}
}
return json;
}

How to compare entries in the same JSONObject

I have this JSONObject below containing multiple entries of orders
{"ORDER_1": {"FNAME" : "JOHN","LNAME" : "B","ORDER_ID" : "D123"},
"ORDER_2": {"FNAME" : "LAURA","LNAME" : "S","ORDER_ID" : "D456"},
"ORDER_3": {"FNAME" : "JACK","LNAME" : "H","ORDER_ID" : "D123"}}
And here is the code where I dynamically collected the customers information and put them into a hashset of JSONObject
Set<JSONObject> customerCollection = new HashSet<JSONObject>();
Iterator<String> keys = (Iterator<String>) customerOrders.keys();
while (keys.hasNext()) {
String key = keys.next();
JSONObject obj = customerOrders.getJSONObject(key);
JSONObject result = new JSONObject();
JSONArray customerList = new JSONArray();
JSONObject customer = new JSONObject();
JSONObject customerObj = new JSONObject();
customer.put("FNAME", obj.optString("FNAME"));
customer.put("LNAME", obj.optString("LNAME"));
customerObj.put("customer", customer);
customerList.add(customerObj);
result.put("customers", customerList);
customerCollection.add(result);
}
Based on the above code, the output is formatted as followed:
[{"customers": [{"name": {"fname": "JOHN","lname": "B"}}]},
{"customers": [{"name": {"fname": "LAURA","lname": "S"}}]},
{"customers": [{"name": {"fname": "JACK","lname": "H"}}]}]
Now there is a requirement to group customers that share the same ORDER_ID. And this is where I struggle a little bit. How can I compare entries of the same JSONObject and group them together based on the ORDER_ID key value? So the final output should be like this (noticed the customer JOHN and JACK are now in the same JSONArray because they share the same ORDER_ID)
[
{
"customers": [{"name": {"fname": "JOHN","lname": "B"}},
{"name": {"fname": "JACK","lname": "H"}}
]
},
{"customers": [{"name": {"fname": "LAURA","lname": "S"}}
]
The problem with your logic is that you are using a single loop and saving the customer for each customer entry instead of once per order.
For this to work, create a map that uses the ORDER_ID as the key for each entry and has an array of customers as its value.
Then you can iterate over that map's keyset and create the "customers" object from the array value for each key.
I realize that stackoverflow would like me to actually code the answer and post it, but I don't think that is productive (for me).

Adding json array in a json object in java

I am struggling to fit in a jsonArray inside a json object (through java code).. please help me out.
My Input JsonObject is :
{
"products":{
"productId":"712161780324",
"imageURL":"http:example.com/imageResource.jpg",
"internalItemCode":"N08792 8W"
}
}
I will have to read "imageURL" property from this JSONObject and append its variants to the same json object (image variants will be in SortedSet data structure).
Sample O/P 1 :
{
"products":{
"productId":"712161780324",
"imageURL":"http:example.com/imageResource.jpg",
"internalItemCode":"N08792 8W",
"variants":[
"http:example.com/imageResource_variant1.jpg",
"http:example.com/imageResource_variant2.jpg"
]
}
}
Sample O/P 2 :
{
"products":{
"productId":"712161780324",
"imageURL":"http:example.com/imageResource.jpg",
"internalItemCode":"N08792 8W",
"variants":[
{
"url" : "http:example.com/imageResource_variant1.jpg"
},
{
"url" : "http:example.com/imageResource_variant2.jpg"
}
]
}
}
The logic i tried to get sample output 2 is some what like below,
// productDetail is the give input JSONObject
JSONObject product = productDetail.optJSONObject("products");
SortedSet<String> imageUrls = new TreeSet<>();
imageUrls.add("http:example.com/imageResource_variant1.jpg");
imageUrls.add("http:example.com/imageResource_variant2.jpg");
Iterator<String> itr = imageUrls.iterator();
JSONArray imageUrlsArray = new JSONArray();
while (itr.hasNext()) {
JSONObject imageUrlObj = new JSONObject();
imageUrlObj.put("url", itr.next());
imageUrlsArray.put(imageUrlObj);
}
product.append("variants", imageUrlsArray);
When i tried to print the productDetail JSON object after executing above logic
System.out.println(productDetail.toString());
I observed the following output :
{
"products":{
"productId":"712161780324",
"imageURL":"http:example.com/imageResource.jpg",
"internalItemCode":"N08792 8W",
"variants":[
[
{
"url" : "http:example.com/imageResource_variant1.jpg"
},
{
"url" : "http:example.com/imageResource_variant2.jpg"
}
]
]
}
}
If you notice, It's coming up like Array of arrays (extra [ ] for "variants"),
Please help me in understanding Where my logic is going wrong.
And also, Please help me getting the First sample out put.
Appreciate quick response..
Thanks,
Rohit.
First sample can be archivable as simple as this:
JSONObject product = productDetail.optJSONObject("products");
JSONArray imageUrlsArray = new JSONArray();
imageUrlsArray.put(0, "http:example.com/imageResource_variant1.jpg");
imageUrlsArray.put(1, "http:example.com/imageResource_variant2.jpg");
product.append("variants", imageUrlsArray);
Try using put instead of append:
JSONObject product = productDetail.optJSONObject("products");
SortedSet<String> imageUrls = new TreeSet<>();
imageUrls.add("http:example.com/imageResource_variant1.jpg");
imageUrls.add("http:example.com/imageResource_variant2.jpg");
Iterator<String> itr = imageUrls.iterator();
JSONArray imageUrlsArray = new JSONArray();
while (itr.hasNext()) {
JSONObject imageUrlObj = new JSONObject();
imageUrlObj.put("url", itr.next());
imageUrlsArray.put(imageUrlObj);
}
-product.append("variants", imageUrlsArray);
+product.put("variants", imageUrlsArray);
From the docs:
Append values to the array under a key. If the key does not exist in the JSONObject, then the key is put in the JSONObject with its value being a JSONArray containing the value parameter. If the key was already associated with a JSONArray, then the value parameter is appended to it.

represent JSON file in a java program for querying values by key

I want to represent this file in my java program.
What I want to do is quickly search through it by "key" value, so for instance, given the value P26 I'd want to return spouse.
Maybe I can read it in as a HashMap using gson as I did with this program.
But what to do about this wonky structure:
{
"properties": {
"P6": "head of government",
"P7": "brother",
...
How could I fit that well into a HashMap? Is HashMap even the best choice?
I've sort of simplified it to this:
{
"P6": "head of government",
"P7": "brother",
"P9": "sister",
"P10": "video",
"P14": "highway marker",
"P15": "road map",
"P16": "highway system",
"P17": "country",
"P18": "image",
I've tried to use this code, but it outputs null
/*
* P values file
*/
String jsonTxt_P = null;
File P_Value_file = new File("properties-es.json");
//read in the P values
if (P_Value_file.exists())
{
InputStream is = new FileInputStream("properties-es.json");
jsonTxt_P = IOUtils.toString(is);
}
Gson gson = new Gson();
Type stringStringMap = new TypeToken<Map<String, String>>(){}.getType();
Map<String,String> map = gson.fromJson(jsonTxt_P, stringStringMap);
System.out.println(map);
It doesn't work because that file is not a Map<String, String>. it has a properties element, which contains a mapping, and a missing element, which contains an array. This mismatch will cause Json to return null, which is what you're seeing. Instead, try doing this:
public class MyData {
Map<String, String> properties;
List<String> missing;
}
And then, to deserialize, do:
MyData data = gson.fromJson(jsonTxt_P, MyData.class);
Map<String, String> stringStringMap = data.properties;
This will make the data structure match the structure of the json, and allow json to properly deserialize.

Get JSON key name using GSON

I have a JSON array which contains objects such as this:
{
"bjones": {
"fname": "Betty",
"lname": "Jones",
"password": "ababab",
"level": "manager"
}
}
my User class has a username which would require the JSON object's key to be used. How would I get the key of my JSON object?
What I have now is getting everything and creating a new User object, but leaving the username null. Which is understandable because my JSON object does not contain a key/value pair for "username":"value".
Gson gson = new Gson();
JsonParser p = new JsonParser();
JsonReader file = new JsonReader(new FileReader(this.filename));
JsonObject result = p.parse(file).getAsJsonObject().getAsJsonObject("bjones");
User newUser = gson.fromJson(result, User.class);
// newUser.username = null
// newUser.fname = "Betty"
// newUser.lname = "Jones"
// newUser.password = "ababab"
// newUser.level = "manager"
edit:
I'm trying to insert "bjones" into newUser.username with Gson, sorry for the lack of clarification
Use entrySet to get the keys. Loop through the entries and create a User for every key.
JsonObject result = p.parse(file).getAsJsonObject();
Set<Map.Entry<String, JsonElement>> entrySet = result.entrySet();
for(Map.Entry<String, JsonElement> entry : entrySet) {
User newUser = gson.fromJson(p.getAsJsonObject(entry.getKey()), User.class);
newUser.username = entry.getKey();
//code...
}
Using keySet() directly excludes the necessity in iteration:
ArrayList<String> objectKeys =
new ArrayList<String>(
myJsonObject.keySet());
Your JSON is fairly simple, so even the manual sort of methods (like creating maps of strings etc for type) will work fine.
For complex JSONs(where there are many nested complex objects and lists of other complex objects inside your JSON), you can create POJO for your JSON with some tool like http://www.jsonschema2pojo.org/
And then just :
final Gson gson = new Gson();
final MyJsonModel obj = gson.fromJson(response, MyJsonModel.class);
// Just access your stuff in object. Example
System.out.println(obj.getResponse().getResults().get(0).getId());

Categories