This question already has answers here:
How to get value by key from JSON?
(3 answers)
Closed 6 months ago.
Hi I have a nested JSON which has JSON array also. i need all single key-value pairs.
below is the nested JSON which has some JSON arrays.
{
"relatedquoteresponse":{
"responsepreamble":{
"responseStatus":"Passed",
"statusCode":"200"
},
"phone":null,
"symbol":"$",
"retrieverelatedquotes":[
{
"quoteId":23232
},
{
"quoteName":"Netally-Service"
}
],
"CheckStatus":{
"StatusCode":200,
"responseMessage":"Found"
}
}
}
I need an output like this:
responseStatus : "Passed"
statusCode : "200"
Phone: null
symbol: "$"
quoteID: 23232
quoteName: "Netally-Service"
I tried below code, but getting this output.
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(json);
Iterator<String> iterator = jsonNode.fieldNames();
while (iterator.hasNext()) {
String key = iterator.next();
printRec(jsonNode, key);
}
public static void printRec(JsonNode jsonNode, String key) {
JsonNode node = jsonNode.get(key);
if (node.isObject()) {
Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
fields.forEachRemaining(field -> {
printRec(node, field.getKey());
if (!field.getValue().isObject()) {
System.out.println(field.getKey() + " : " + field.getValue());
}
});
}
}
OutPut:
responseStatus : "Passed"
statusCode : "200"
Phone: null
symbol: "$"
retrieverelatedquotes : [{"quoteId":23232},{"quoteName":"Netally-Service"}]
Can anyone help me on this to get all key-value pairs not in array like above.
Thanks
Recursion really helps, even works for multiple levels.
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(json);
printRec(jsonNode, "");
----------
public static void printRec(JsonNode jsonNode, String key) {
if (jsonNode.isValueNode()) {
System.out.println(key + " : " + jsonNode);
} else if (jsonNode.isObject()) {
jsonNode.fields().forEachRemaining(field -> printRec(field.getValue(), field.getKey()));
} else if (jsonNode.isArray()) {
for (int i = 0; i < jsonNode.size(); i++) {
printRec(jsonNode.get(i), key + "[" + i + "]");
}
}
}
Output
responseStatus : "Passed"
statusCode : "200"
phone : null
symbol : "$"
quoteId : 23232
quoteName : "Netally-Service"
StatusCode : 200
responseMessage : "Found"
You can use Regex named groups feature to get all single keys and values
Pattern pattern = Pattern.compile("\"(.+)\":(?![{\\[])(.+)");
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group(1) + " : " + matcher.group(2));
}
Output will be:
responseStatus : "Passed",
statusCode : "200"
phone : null,
symbol : "$",
quoteId : 23232
quoteName : "Netally-Service"
StatusCode : 200,
responseMessage : "Found"
You can easily modify the pattern to get all single keys with int value for example or null value
Simple article about Regex Named groups and Backreferences
Related
I have a JSON in the following format. I want to replace the Nutrition at the last index with Title using java code.
Current Format
{
"nutrients" : [{
"Nutrient" : "Alcohol, ethyl",
"Amount" : " 3.9",
"Unit" : " g"
}, {
"Nutrient" : "Fiber",
"Amount" : " 0.0",
"Unit" : " g"
}, {
"Nutrient" : "Alcoholic beverage, BUDWEISER, regular, beer"
}]
}
Required Format
{
"nutrients" : [{
"Nutrient" : "Alcohol, ethyl",
"Amount" : " 3.9",
"Unit" : " g"
}, {
"Nutrient" : "Fiber",
"Amount" : " 0.0",
"Unit" : " g"
}, {
"Title" : "Alcoholic beverage, BUDWEISER, regular, beer"
}]
}
You can do it by using String#lastIndexOf and String#substring. Note that both of these methods have two variants.
public class Main {
public static void main(String[] args) {
String json = "{\n" + " \"nutrients\" : [{\n" + "\n" + " \"Nutrient\" : \"Alcohol, ethyl\",\n"
+ " \"Amount\" : \" 3.9\",\n" + " \"Unit\" : \" g\"\n" + "\n" + " }, {\n" + "\n"
+ " \"Nutrient\" : \"Fiber\",\n" + " \"Amount\" : \" 0.0\",\n"
+ " \"Unit\" : \" g\"\n" + "\n" + " }, {\n" + "\n"
+ " \"Nutrient\" : \"Alcoholic beverage, BUDWEISER, regular, beer\"\n" + "\n" + " }] \n"
+ "}";
int i = json.lastIndexOf("\"Nutrient\"");
// (string before last "Nutrient") + ("Title") + (string after last "Nutrient")
String result = json.substring(0, i) + "\"Title\"" + json.substring(i + "\"Nutrient\"".length());
System.out.println(result);
}
}
Output:
{
"nutrients" : [{
"Nutrient" : "Alcohol, ethyl",
"Amount" : " 3.9",
"Unit" : " g"
}, {
"Nutrient" : "Fiber",
"Amount" : " 0.0",
"Unit" : " g"
}, {
"Title" : "Alcoholic beverage, BUDWEISER, regular, beer"
}]
}
You can achieve this using Jackson.
Needed libs:
Jackson Core: https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core/2.11.0
Jackson Databind: https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind/2.11.0
But you can't change field name. Instead you can add a new one and remove the other.
public static void main(String[] args) throws JsonProcessingException {
String json = ...;
//transform string to jsonnode
JsonNode jsonNode = (new ObjectMapper()).readTree(json);
//get nutrients array node
JsonNode nutrientsNode = jsonNode.get("nutrients");
int lastIndex = nutrientsNode.size() - 1;
//get last node from nutrients and cast to objectnode
ObjectNode lastNutrientObject = (ObjectNode)nutrientsNode.get(lastIndex);
//get Nutrient field value from last nutrient
JsonNode nutrientField = lastNutrientObject.get("Nutrient");
//remove Nutriend field from last nutrient
lastNutrientObject.remove("Nutrient");
//add Title field with node nutrientField into last nutrient
lastNutrientObject.set("Title", nutrientField);
System.out.println(jsonNode.toPrettyString());
}
This looks fairly easy, if you use jackson a well known java library for json.
once you have this on your classpath(may be using the jar or the gradle dependency,etc) you can now do this a couple of ways.
the easiest of which is to probably use objectmapper to parse this data into a pojo and then manipulate the pojo.
or otherwise you could save extra cost and computation by may be parsing this into a JsonNode.
//try to use this instance as much as you can(JACKSON-101)
ObjectMapper mapper = new ObjectMapper();
final JsonNode jsonNode = mapper.readTree("parse_your_json_string_here");
/**
* to get to the nutrients i use `at` because it is nullsafe :D although if performance is a
* concern and `/nutrients` is a common use case you may want to precompile the pointer using <code>
* JsonPointer nutsPointer = JsonPointer.compile("/nutrients");
* </code>
*/
final JsonNode temp = jsonNode.at("/nutrients");
// once done just to be sure we have the right data check that its an arrayNode :D
if (temp.isArray()) {
// if true cast it :D
ArrayNode arrayNode = (ArrayNode) temp;
final JsonNode nutrientsNodeToModify = arrayNode.path(arrayNode.size() - 1);
if (nutrientsNodeToModify.isObject()) {
ObjectNode nutrientsNode = (ObjectNode) nutrientsNodeToModify;
nutrientsNode.set("Title", nutrientsNode.at("/Nutrient"));
nutrientsNode.remove("Nutrient");
}
}
Given the following JSON:
{
"full_report": {
"score": "5",
"history": {
"record": [
{
"name": "John Smith",
"SSN": "123456789",
"last_visit": "02.03.2019",
"expiry_date": "15.03.2019"
},
{
"name": "John Doe",
"SSN": "987654321",
"last_visit": "05.03.2019",
"expiry_date": "15.09.2019"
},
{
"name": "Jane Doe",
"SSN": "999999999",
"last_visit": "02.03.2019"
}
]
}
}
}
I would like to be able to use JsonPath to find if any of the objects under the record array are missing the expiry_date key and value pair. I currently have a working solution using a method that adds all entries of record to one list and all entries of expiry_date to another and simply checks if the subtraction of both list sizes equals 0 to return a boolean value.
public Boolean isMissing(String json, String expression) {
String[] expr = expression.split(";");
List<String> totalRecords = JsonPath.read(json, expr[0]);
List<String> foundRecords = JsonPath.read(json, expr[1]);
return totalRecords.size() - foundRecords.size() != 0;
}
This requires using two JsonPath expressions with a ; delimiter like so which causes some other, unrelated issues that I'd like to avoid:
$.full_report.history.record[?(#.expiry_date)];$.full_report.history.record[*]
For a copy of this exact structure in XML I can use an XPath expression like so: boolean(full_report/history/record[not(expiry_date/text())])
Any chance there's an equivalent filter that I may have missed in JsonPath?
Update
The answer below by Saeed Alizadeh to use predicate does not solve the problem of having to pass in the property to look for in the first place.
It did help make the method and passing in the property a fair bit cleaner as there's no longer a need to write two separate expressions rather than expression;property_to_lookup. As seen in the method below, that fundamental issue still remains but is much more hassle-free:
public Boolean isMissing(String json, String expression) {
String[] expressionAndPredicate = expression.split(";");
Predicate missingKeyValue = predicateContext -> {
Object missingKey = predicateContext.item(Map.class).get(expressionAndPredicate[1]);
return missingKey == null || missingKey.toString().length() <= 0;
};
List<String> records = JsonPath.read(json, expressionAndPredicate[0], missingKeyValue);
return records.size() != 0;
}
I don't want to add a 3rd parameter to the method for the property nor split the incoming string to sidestep the issue as it creates some issues when providing the JsonPath expression as input at the endpoint. If no one-line expression exists similar to the XPath example, I'll mark the question as answered by Saeed.
As you can see example below by using predicate you can filter result:
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Predicate;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
String json = "{\n" +
" \"full_report\": {\n" +
" \"score\": \"5\",\n" +
" \"history\": {\n" +
" \"record\": [\n" +
" {\n" +
" \"name\": \"John Smith\",\n" +
" \"SSN\": \"123456789\",\n" +
" \"last_visit\": \"02.03.2019\",\n" +
" \"expiry_date\": \"15.03.2019\"\n" +
" },\n" +
" {\n" +
" \"name\": \"John Doe\",\n" +
" \"SSN\": \"987654321\",\n" +
" \"last_visit\": \"05.03.2019\",\n" +
" \"expiry_date\": \"15.09.2019\"\n" +
" },\n" +
" {\n" +
" \"name\": \"Jane Doe\",\n" +
" \"SSN\": \"999999999\",\n" +
" \"last_visit\": \"02.03.2019\"\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}";
Predicate expiry_date = new Predicate() {
public boolean apply(PredicateContext predicateContext) {
Object expiry_date = predicateContext.item(Map.class).get("expiry_date");
return expiry_date != null && expiry_date.toString().length() > 0 ? false : true;
}
};
List records = JsonPath.parse(json).read("full_report.history.record[?]", List.class, expiry_date);
System.out.println(records);
}
}
this will print following output as the only record missing "expiry_date" property
[{"name":"Jane Doe","SSN":"999999999","last_visit":"02.03.2019"}]
update
public static boolean isMissing(String json, final String jsonProperty) {
Predicate predicate = new Predicate() {
public boolean apply(PredicateContext predicateContext) {
Object propertyObject = predicateContext.item(Map.class).get(jsonProperty);
return propertyObject != null && propertyObject.toString().length() > 0 ? false : true;
}
};
List records = JsonPath.parse(json).read("full_report.history.record[?]", List.class, predicate);
return (records != null && records.size() > 0) ? true : false;
}
output:
System.out.println(isMissing(json, "expiry_date")); // prints true
System.out.println(isMissing(json, "name")); // prints false
I have seen many examples, but none of them are not like what i want.
Consider I have a JSONObject like:
[ {
"id" : "572add95e4b0b04f4d502a3c",
"amount" : 109.27,
"sourceCurrency" : "MXN",
"targetCurrency" : "USD",
"recipientBankId" : "572add95e4b0b04f4d502a37",
"iban" : "5805742027",
"created" : "2016-05-05T05:43:49.194"
}, {
"id" : "572add95e4b0b04f4d502a3e",
"amount" : 722.41,
"sourceCurrency" : "GBP",
"targetCurrency" : "INR",
"recipientBankId" : "572add95e4b0b04f4d502a32",
"iban" : "4688276585",
"created" : "2016-05-05T05:43:49.2"
}]
and i want to access to the second json and iban value.
How can i do it?
With your json content
String json = "[ {\n" +
" \"id\" : \"572add95e4b0b04f4d502a3c\",\n" +
" \"amount\" : 109.27,\n" +
" \"sourceCurrency\" : \"MXN\",\n" +
" \"targetCurrency\" : \"USD\",\n" +
" \"recipientBankId\" : \"572add95e4b0b04f4d502a37\",\n" +
" \"iban\" : \"5805742027\",\n" +
" \"created\" : \"2016-05-05T05:43:49.194\"\n" +
"}, {\n" +
" \"id\" : \"572add95e4b0b04f4d502a3e\",\n" +
" \"amount\" : 722.41,\n" +
" \"sourceCurrency\" : \"GBP\",\n" +
" \"targetCurrency\" : \"INR\",\n" +
" \"recipientBankId\" : \"572add95e4b0b04f4d502a32\",\n" +
" \"iban\" : \"4688276585\",\n" +
" \"created\" : \"2016-05-05T05:43:49.2\"\n" +
"}]";
You first need to get a JSONArray from your json content :
JSONArray array = new JSONArray(json);
Then you read the second ( at the index 1 ) JSONObject within the array:
JSONObject o = array.getJSONObject(1);
And finally you read the iban from the JSONObject :
String secondIban = o.getString("iban");
System.out.println(secondIban);
With of course all this surrounded with a try/catch to catch JSONException:
try {
JSONArray array = new JSONArray(json);
JSONObject o = array.getJSONObject(1);
String secondIban = o.getString("iban");
System.out.println(secondIban);
}catch(JSONException jse){
jse.printStackTrace();
}
Note
If you want to be aware that the iban field doesn't exist use o.getString("iban").
A JSONException will be thrown if the field is missing.
If you are okay to work with an empty string "" as default value for the eventually missing field then use o.optString("iban") to read the field.
you can do something like below.
public static void main(String[] args) {
String json = "[{\n" +
" \"id\" : \"572add95e4b0b04f4d502a3c\",\n" +
" \"amount\" : 109.27,\n" +
" \"sourceCurrency\" : \"MXN\",\n" +
" \"targetCurrency\" : \"USD\",\n" +
" \"recipientBankId\" : \"572add95e4b0b04f4d502a37\",\n" +
" \"iban\" : \"5805742027\",\n" +
" \"created\" : \"2016-05-05T05:43:49.194\"\n" +
"}, {\n" +
" \"id\" : \"572add95e4b0b04f4d502a3e\",\n" +
" \"amount\" : 722.41,\n" +
" \"sourceCurrency\" : \"GBP\",\n" +
" \"targetCurrency\" : \"INR\",\n" +
" \"recipientBankId\" : \"572add95e4b0b04f4d502a32\",\n" +
" \"iban\" : \"4688276585\",\n" +
" \"created\" : \"2016-05-05T05:43:49.2\"\n" +
"}]";
JSONArray objects = new JSONArray(json);
System.out.println(((JSONObject)objects.get(1)).get("iban"));
}
Use optString instead getString.
optString - Will return blank string if element/key not found in JSON.
getString - Will throw an exception.
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
================================
public static void main(String[] args) throws JSONException {
String str = "[{\"id\":\"572add95e4b0b04f4d502a3c\",\"amount\":109.27,\"sourceCurrency\":\"MXN\",\"targetCurrency\":\"USD\",\"recipientBankId\":\"572add95e4b0b04f4d502a37\",\"iban\":\"5805742027\",\"created\":\"2016-05-05T05:43:49.194\"},{\"id\":\"572add95e4b0b04f4d502a3e\",\"amount\":722.41,\"sourceCurrency\":\"GBP\",\"targetCurrency\":\"INR\",\"recipientBankId\":\"572add95e4b0b04f4d502a32\",\"iban\":\"4688276585\",\"created\":\"2016-05-05T05:43:49.2\"}]";
JSONArray jsonArray = new JSONArray(str);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.optJSONObject(i);
/* optString - will return blank string if element not found */
String iban = jsonObject.optString("iban");
System.out.println(iban);
}
}
What you should do is transform string into json:
public JsonObject parseJsonString(String jsonString) {
JsonParser jsonParser = new JsonParser();
JsonObject jsonObject = (JsonObject) jsonParser.parse(jsonString);
return jsonObject;
}
then extract values that you are after:
final String iban = jsonObject.get("iban").getAsString();
To read a value from JSON you can only with the next methods:
Data bind
Tree Model
Streaming API
XPath like
Which method to use is up to you because all methods have advantage and disadvantage.
Engines:
Fastjson supports Data bind, XPath like
Gson supports Data bind, Tree Model, Streaming API
Jackson supports Data bind, Tree Model, Streaming API
JsonPath supports XPath like
Genson supports Data bind, Tree model with Jsonp, Streaming API
Ig json parser supports Data bind
Moshi supports Data bind
JSON java supports Tree Model
LoganSquare supports Data bind
I guess you want to select only a part from json, so xpath like could be your choice with syntax like this $[1].iban (JsonPath)
I am trying to get for example only this:
-68.06993865966797
from a output of this type:
{
"results" : [
{
"elevation" : -68.06993865966797,
"location" : {
"lat" : 27.85061,
"lng" : -95.58962
},
"resolution" : 152.7032318115234
}
],
"status" : "OK"
}
How is it possible to only get the string after
"elevation" :
and end with a comma, but get the string inbetween the colon after elevation until the comma that ends the line
Using regex is not recommended for JSON data. Despite that, I put the two ways (i.e. regex and JSON parser) together as follows:
import java.util.regex.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public static void main(String[] args) throws JSONException {
String JSON_DATA = "{\n"+
" \"results\" : [\n"+
" {\n"+
" \"elevation\" : -68.06993865966797,\n"+
" \"location\" : {\n"+
" \"lat\" : 27.85061,\n"+
" \"lng\" : -95.58962\n"+
" },\n"+
" \"resolution\" : 152.7032318115234\n"+
" }\n"+
" ],\n"+
" \"status\" : \"OK\"\n"+
"}\n"+
"";
// 1. If using REGEX to find all values of "elevation".
Matcher m = Pattern.compile("\"elevation\"\\s+:\\s+(-?[\\d.]+),").matcher(JSON_DATA);
while (m.find()) {
System.out.println("elevation: " + m.group(1));
}
// 2. If using a JSON parser
JSONObject obj = new JSONObject(JSON_DATA);
JSONArray geodata = obj.getJSONArray("results");
for (int i = 0; i < geodata.length(); ++i) {
final JSONObject site = geodata.getJSONObject(i);
System.out.println("elevation: " + site.getDouble("elevation"));
}
}
I have a Map :
Map<String, String> value
How to obtain particular value "String" ----> "type" from json value, how do i obtain it?
I tried below code, which only returns me KEY and not VALUE
Iterator it = payload.entrySet().iterator();
while (it.hasNext())
{
Map.Entry pair = (Map.Entry)it.next();
System.out.println(pair.getKey() + " = " + pair.getValue());
String key = pair.getKey().toString();
String value = pair.getValue().toString();
Log.d("getActionValue", "key : " + key);
Log.d("getActionValue", "value : + value");
}
You don't access your variable in your output, since value is still in in the string.
Change like this:
Log.d("getActionValue", "value : " + value);
Your problem is here Log.d("getActionValue", "value : + value"); it should be Log.d("getActionValue", "value : "+ value);
Try this for loop :-
for (Map.Entry<String, String> entry : payload.entrySet())
{
Log.d("getActionValue", "key : " + entry.getKey());
Log.d("getActionValue", "value :" + entry.getValue());
}
I want that particular "String" ----> "type" from value, how do i
obtain it?
payload Map contains JSONObject's as value of every key. so to get type need to first convert String to JSONObject then get value using type key:
JSONObject jsonObject=new JSONObject(value);
String strType="";
if(jsonObject.has("notification-action")){
JSONObject jsonObjectnotification=jsonObject.optJSONObject
(""notification-action"");
if(jsonObjectnotification.has("type")){
strType=jsonObjectnotification.optString("type");
}
}
You logging it wrong.
Should be:
Log.d("getActionValue", "value : " + value);