This question already has answers here:
Merge/Extend JSON Objects using Gson in Java
(4 answers)
Closed 1 year ago.
Given this JSON:
{
"contact_data": [
"address/all",
"telephone/us"
],
"financial_data": [
"bank_account_number/all",
"bank_account_number/uk",
"credit_card_numbers"
]
}
and this JSON:
{
"financial_data": [
"credit_card_numbers",
"bank_account_number/ca",
"bank_account_number/all"
],
"government_id": [
"driving_license/americas"
],
"sensitive_data": [
"racial_ethnic_origin"
]
}
I want to merge these to look like this:
{
"contact_data": [
"address/all",
"telephone/us"
],
"financial_data": [
"credit_card_numbers",
"bank_account_number/ca",
"bank_account_number/uk",
"bank_account_number/all"
],
"government_id": [
"driving_license/americas"
],
"sensitive_data": [
"racial_ethnic_origin"
]
}
I have the following, which almost works:
import org.json.JSONObject;
...
final List<String> jsonStrings = ...; // A list of the above sample JSONs
final List<JSONObject> jsonObjects = jsonStrings
.stream()
.map(JSONObject::new)
// JSONObject.getNames() (called later on) will return null if JSONObject is empty, so filter out empty objects.
.filter(jsonObject -> !jsonObject.isEmpty())
.collect(Collectors.toList());
if (jsonObjects..size() > 1) {
// Merge multiple JSONObjects: https://stackoverflow.com/a/2403453/12177456
final JSONObject firstJsonObject = jsonObjects.get(0);
final JSONObject merged = new JSONObject(firstJsonObject, JSONObject.getNames(firstJsonObject));
final List<JSONObject> remainingJsonObjects = jsonObjects.subList(1, jsonObjects.size());
for (final JSONObject nextJsonObject : remainingJsonObjects) {
for (final String nextJsonObjectFieldName : JSONObject.getNames(nextJsonObject)) {
merged.put(nextJsonObjectFieldName, nextJsonObject.get(nextJsonObjectFieldName));
}
}
return merged;
}
however, where I would expect to see 4 entries in financial_data:
...
"financial_data": [
"bank_account_number/all",
"bank_account_number/uk",
"bank_account_number/ca",
"credit_card_numbers"
]
...
instead, I see just 3, with bank_account_number/uk not in the merged result:
...
"financial_data": [
"bank_account_number/all",
"bank_account_number/ca",
"credit_card_numbers"
]
...
I'm not stuck on using org.json, if it's simplier using gson, jackson, plain Java maps, I'm ok with that.
Actually, it won't work. The problem is here:
merged.put(nextJsonObjectFieldName, nextJsonObject.get(nextJsonObjectFieldName))
This replaces the entry with key nextJsonObjectFieldName with the later one. That is why you're getting in the financial_data from the second object.
{
"financial_data": [
"credit_card_numbers",
"bank_account_number/ca",
"bank_account_number/all"
]
}
You are seeing other keys okay because other keys hase exact same value in both JSON's. If you change the value of other keys too in the second json, you'll see it the merged JSON will have the values with the same key from second json. That is, IT WON'T WORK.
What you can do is, you can check if the key has already value in the map or not. If there's no existing JSONobject for this key, just put it into the merged as you're doing. Otherwise, we have some more job to do:
JSONObject jsonObject = merged.get(nextJsonObjectFieldName);
if (jsonObject != null) {
final JSONObject finalObj = mergeJsonObjectCheckingFieldValues(jsonObject, nextJsonObject.get(nextJsonObjectFieldName));
merged.put(nextJsonObjectFieldName, finalObj);
} else {
merged.put(nextJsonObjectFieldName, nextJsonObject.get(nextJsonObjectFieldName));
}
The mergeJsonObjectCheckingFieldValues method checks each element between the given two JSONObject and compares whether they are same. As per your example and for simply answering this question, I've assumed that each of the JSONObject is nothing but a list of String. For this, we'll be needing objectMapper. So make sure you have the com.fasterxml.jackson.databind.ObjectMapper in your project. So, the checking will be:
ObjectMapper mapper = new ObjectMapper();
public JSONObject mergeJsonObjectCheckingFieldValues(JSONObject jsonObject, JSONObject nextJsonObject)) {
List<String> existingList = Arrays.asList(mapper
.readValue(jsonObject.toString(), String[].class));
List<String> newList = Arrays.asList(mapper
.readValue(nextJsonObject.toString(), String[].class));
List<String> toBeAdded = newList
.stream()
.filter(x -> !existingList.contains(x))
.collect(Collectors.toList());
if(toBeAdded.size() > 0) {
existingList.addAll(toBeAdded);
}
return new JSONObject(JSONArray.toJSONString(existingList));
}
This is the probable solution to your problem. I haven't tested it, but the code should be pretty much this.
I went with the accepted answer here using gson:
https://stackoverflow.com/a/34092374/12177456
Handles recursively merging JsonObjects
Handles conflicts when the key exists in both maps
Has special handling for non-primitive values such as Arrays
Related
I am trying to do JSON parsing. The JSON data is shown below, I am trying to get the "categories". I was able to JSON parse everything else, but I am not sure what does this "categories" belong to, is it a JSONObject, JSONArray, or something else? I am a newbie and self-taught, usually I am familiar that JSONArray has form of "JSONArray": {["content"]}, and the "content" is JSONObject. But in this case, "categories":["content"]. I am trying to parse this "categories", and turn it to string. Thank you for your help.
{
"results": [
{
"type": "Restaurant",
"id": "jfhuiewjkfkdljiahueijkfnlsdiejkl1484391hjk8421k",
"score": 99.9844207764,
"dist": 15.581982823437135,
"info": "search:ta:840369014527642-US",
"poi": {
"name": "RoofTop Bar",
"categorySet": [
{
"id": 184729472943
}
],
"categories": [
"pub food",
"restaurant"
]}
}]
}
This is what I have tried:
groups = new JSONArray();
groups = response.getJSONArray("results");
if (groups.length() > 0) {
JSONObject resultObject = groups.getJSONObject(0);
if (resultObject.has("poi")) {
if (resultObject.getJSONObject("poi").has("name")) {
nameResult = resultObject.getJSONObject("poi").getString("name");
} else {
nameResult = "Information is not available.";
}
if (resultObject.getJSONObject("poi").has("categories")) {
JSONObject categoriesResult;
categoriesResult = resultObject.getJSONObject("categories").toString();
}
results is an array of objects
The first object contains a property called poi
poi contains a property called categories
So using the top to bottom approach, we can arrive at
const categoriesArray = results[0].poi.categories; // gives categories as an array of strings
const categoriesString = categoriesArray.join(",") // gives categories as string, with comma separated values
I am not sure if it is the actual raw data but the poi object where the categories are contained is malformed. It is missing a closing bracket which could be causing parsing issues.
That apart, the field categories from the poi object is a list of strings I am not sure how you want to format it to a string but you could loop through them and do want you want with them.
In order to obtain them you can access them from your object with results[0].poi.categories or loop through the results before accessing the categories with result.poi.categories where result is the variable containing the currently looped result.
EDIT:
From your code sample, assuming response is a JSONObject you can do the following.
Then to obtain categories in a string without the array format, you can loop through the categories and concatenate them to a string.
String categories = resultObject.get("categories").join(", ");
I need to parse the following JSON and add values from it into three different Java objects. I was thinking to form other 3 Jsons in order to do this. I have some issues with parsing, as the JSON is a little bit complicated. The JSON is below:
{
"totalCount": 1,
"results": [
{
"teleCommunications": [
{
"areaCode": "100",
"telephoneNumber": "300-2444",
"internationalAreaCode": "",
"communicationType": 1
},
{
"areaCode": "100",
"telephoneNumber": "200-2555",
"internationalAreaCode": "",
"communicationType": 5
}
],
"delegate": {
"id": 0,
"range": 0,
},
"name": "Andrew",
"composedKey": {
"id": 615,
"range": 50,
},
"isBranchAddress": false,
"emailAddresses": [
{
"emailAddressType": 9,
"emailAddress": "andrew.brown#gmail.com"
}
],
"name": "Brown",
"zipCodeCity": "65760 Leipzig",
"salutation": "Mr.",
"openingDate": "2019-09-20",
"streetHouseNumber": "Offenbach. 37",
"modificationTimestamp": "2018-01-27"
}
]
}
I need to get separately the values from name, zipCodeCity, salutation, openingDate, streetHouseNumber in a JSON ( or any other way) , emailAddresses in a different JSON and the other data in another one.
I tried with this piece of code:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
HashMap<String, Object> result = new ObjectMapper().readValue(response, HashMap.class);
Object object = result.get("results");
List<HashMap<String, String>> jsonValues = (List<HashMap<String, String>>)object;
for(String key : jsonValues.get(0).keySet()){
System.out.println("name value " + jsonValues.get(0).get("name"));
System.out.println("zip code City " + jsonValues.get(0).get("zipCodeCity"));
}
The problem is that I would not go for such a hardcoded way...it is not very suitable.
Does anyone know a better approach for storing the values I need and parsing the Json more optimally?
Thank you
Look for this link, there's a demo application specially for you: https://github.com/nalmelune/jackson-demo-58591850 (look at JacksonTest)
First of all, you'll need data-classes for this (or so called dto), representing structure. You can use online generators for that (google "json to java dto online") or do it yourself. For example, here's root object (see github link for more):
public class Root {
private int totalCount;
private List<Results> results;
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getTotalCount() {
return this.totalCount;
}
public void setResults(List<Results> results) {
this.results = results;
}
public List<Results> getResults() {
return this.results;
}
}
Then configure your ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
And use it (don't create new mapper, as it is in your example):
Root result = mapper.readValue(response, Root.class);
Finally, you can access it as usual Plain Old Java Objects:
for (Results resultItem : result.getResults()) {
System.out.println(resultItem.getSalutation());
System.out.println(resultItem.getName());
System.out.println(resultItem.getZipCodeCity());
System.out.println(resultItem.getOpeningDate());
System.out.println(resultItem.getStreetHouseNumber());
}
To make it work be sure validate your json (there were invalid commas after "range", and "name" comes twice and so on, it fixed on github). And be sure to include jsr310 in classpath by adding com.fasterxml.jackson.datatype:jackson-datatype-jsr310 module. You will need it for LocalDate and LocalDateTime objects.
You may create java class something like below
#Data
class MyCustomClass{
int totalCount;
List<TeleCommunicationDetail> results;
// other props
}
Next you can add attributes to TeleCommunicationDetail and finally you can use MyCustomClass reaonseInJavaObject = objectMapper.readValue(myJson,MyCustomClass.class);
Refer to this question for details.
Your approach is correct, as you simply read your JSON object as Map<String, Object> which always will work (as long as JSON Object is valid). And then you retrieve your values that you expect to be there. Another approach is to create a Class that maps to your original JSON and parse JSON with ObjectMapper into your class. Then you can retrieve your data with your class setters and getters. In this case, you don't need to use key Strings like "results" etc but you have to ensure that your JSON is not only valid JSON but always conforms to your class. Pick your option...
I am new To JSon and i want to search the following json string and get the required output.
String:
{"status":"Success","code":"200","message":"Retrieved Successfully","reason":null,"
"projects":
[
{
"projectName": "example",
"users":
[
{
"userName": "xyz",
"executions":
[
{
"status": "check",
"runs":
[
{
"Id": "------",
"Key": "---"
}
],
"RCount": 1
}
],
"RCount": 1
}
],
"RCount": 1
},
Like that i have many projects and now , if i give projectname and username as input i wantt to get its status as output.
Is it possible?If yes how?
You may use JSONObject for this.
JSONObject json = new JSONObject(string);
JSONArray[] projectsArray = json.getJSONArray("projects");
for(int i = 0; i < projectsArray.length; ++i)
{
String projectName = projectsArray[i].getString("projectName");
...
}
Use the same method to get the users.
You can use gson library. Using gson convert your json string to Map and then you can iterate through map to get required item
Type type = new TypeToken<Map<String, Object>>(){}.getType();
Map<String, Object> myMap = gson.fromJson(jsonString, type);
You can use the Google gson to map your json data structure to a Java POJOs.
Example :
You can have Projects class containing list/array of Users.
Users class containing list/array of Executions and so on.
Gson library can easily map the json to these classes as objects and you can access your data in a more elegant manner.
Here are a few references :
http://howtodoinjava.com/2014/06/17/google-gson-tutorial-convert-java-object-to-from-json/
http://www.mkyong.com/java/how-do-convert-java-object-to-from-json-format-gson-api/
In java, I am trying to parse values from this json..
[
{
"2012-01-02": {
"age": 3,
"dob": "2010-01-03",
"name": "jack"
},
"2012-01-03": {
"age": 3,
"dob": "2010-01-04",
"name": "jill"
},
"2012-01-04": {
"age": 3,
"dob": "2010-01-05",
"name": "john"
},
"2012-01-05": {
"age": 3,
"dob": "2010-01-06",
"name": "miran"
}
}
]
Using JSONObject, I was trying to get the value of just "age" and then add them up to do some data manipulation.
I created a JSONObject
Created an iterator and then stored them to a map
This gets me the inner element like:
{
"age": 3,
"dob": "2010-01-06",
"name": "miran"
}
After this, not sure how to extract just age from each element. Do i create another jsonobject and pass this new string, extract age out of it or is there a better way to do this? (I am sure there is one)
UPDATE:
This is what I currently have that gives me {"age":3,"dob":"2012-01-06","name":"miran"}
JSONObject jsonobj = new JSONObject();
try {
jsonobj = new JSONObject(pastweekVol);
Iterator iter = jsonobj.keys();
Map<String, String> map = new HashMap<String, String>();
while(iter.hasNext()){
String jsonkey = (String)iter.next();
String value = jsonobj.getString(jsonkey);
logger.debug("first pass value is: {}", value);
} catch (JSONException je) {
logger.debug("exception is: {}",je);
}
I was thinking that since I am getting {"age":3,"dob":"2012-01-06","name":"miran"}, I would create another json object and pass in this string, which will give me value of "age". The problem here is that I get repetitive values. Of course, something very basic is missing here but I can't seem to figure that out.
If you have the inner element as a JSONObject instance - say person - then you can directly access the age:
int age = person.getInt("age");
and do something with it:
sum += age;
You might consider a library like Google's GSON (http://code.google.com/p/google-gson/) if you want to be able to easily parse arbitrarily complex JSON strnigs into generic objects.
Using org.json is probably not your best bet -- this API has many flaws. Using Jackson, you can easily extract age from each member value:
ObjectMapper mapper = new ObjectMapper();
JsonNode fullDocument = mapper.readTree(xxx); // xxx can be many things
// Not an object? Bail out
if (!fullDocument.isObject())
throw new IllegalArgumentException("not an object");
// This will iterate through object values
for (JsonNode value: fullDocument)
// do something with value.get("age")
// in particular, you can test for .isIntegralNumber()
I've read so many posts and still can't find or understand how to handle a result set that can have either 1 result, or an array of results. (From yahoo Search)
I can parse the results perfectly fine IF multiple results were received, but when there is only 1 search result I get the JSONException: blahblahbalh is not a JSONArray.
JSONArray results = resultObject.getJSONArray("Result");
Works fine when there are multiple results, but how can I FORCE the built-in JSON parser to accept it as a result when there is only 1 result returned from the Yahoo Query?
This single result fails to parse to JSON Array:
{
"ResultSet": {
"totalResultsAvailable": "108",
"totalResultsReturned": "1",
"firstResultPosition": "1",
"ResultSetMapUrl": "http:\/\/maps.yahoo.com\/broadband\/?q1=Virginia+Beach%2C+VA+23454-4608tt=mexicantp=1",
"Result": {
"id": "12811175",
}
}
}
But this parses to JSONArray just fine:
{
"ResultSet": {
"totalResultsAvailable": "108",
"totalResultsReturned": "2",
"firstResultPosition": "1",
"ResultSetMapUrl": "http:\/\/maps.yahoo.com\/broadband\/?q1=Virginia+Beach%2C+VA+23454-4608tt=mexicantp=1",
"Result": [
{
"id": "12811175",
},
{
"id": "12814560",
}
]
}
}
Sorry if I'm babbling, but it's driving me crazy that I just can't figure out how to get a JSONArray with length of 1, out of the first result example.
Thanks much!
This is one of the problems with working with JSOn. If there are two objects than it is considered an JSONArray, otherwise it is considered a JSONObject.
As far as I know, you need to just assume that it could be either and code accordingly. You could wrap the messy details in a helper function like:
JSONArray getArrayOrObject(JSONObject parent, String name) {
JSONArray results = parent.optJSONArray(name);
if (results == null) {
results = new JSONArray();
JSONObject object = parent.optJSONObject("Result");
if (object != null) {
results.put(object);
}
}
return results;
}
You want to check if totalResultsReturned == 1. if it is, you should use .getJSONObject('Result') instead, and then construct a JSONArray with that object as the first value (http://www.json.org/javadoc/org/json/JSONArray.html).
Presumably you're handling the case where there are 0 results as well.