Related
I am looking to extract certain values from a JSON path of arrays and objects and use these values for further processing and am struggling with accessing those elements. Here is the JSON response:
[
{
"od_pair":"7015400:8727100",
"buckets":[
{
"bucket":"C00",
"original":2,
"available":2
},
{
"bucket":"A01",
"original":76,
"available":0
},
{
"bucket":"B01",
"original":672,
"available":480
}
]
},
{
"od_pair":"7015400:8814001",
"buckets":[
{
"bucket":"C00",
"original":2,
"available":2
},
{
"bucket":"A01",
"original":40,
"available":40
},
{
"bucket":"B01",
"original":672,
"available":672
},
{
"bucket":"B03",
"original":632,
"available":632
},
{
"bucket":"B05",
"original":558,
"available":558
}
]
}
]
I tried accessing the root elements with $ but I could not get further with it.
Here is the test method that I have written. I want to extract the value for od_pair and within each od_pair, I need to be able to retrieve the bucket codes and their available numbers.
public static void updateBuckets(String ServiceName, String DateOfJourney) throws Exception {
File jsonExample = new File(System.getProperty("user.dir"), "\\LogAvResponse\\LogAvResponse.json");
JsonPath jsonPath = new JsonPath(jsonExample);
List<Object> LegList = jsonPath.getList("$");
// List<HashMap<String, String>> jsonObjectsInArray = jsonPath.getList("$");
int NoofLegs = LegList.size();
System.out.println("No of legs :" + NoofLegs);
for (int j = 0; j <= NoofLegs; j++)
// for (HashMap<String, String> jsonObject : jsonObjectsInArray) {
{
String OD_Pair = jsonPath.param("j", j).getString("[j].od_pair");
// String OD_Pair = jsonObject.get("od_pair");
System.out.println("OD Pair: " + OD_Pair);
List<Object> BucketsList = jsonPath.param("j", j).getList("[j].buckets");
int NoOfBuckets = BucketsList.size();
// System.out.println("OD Pair: " + OD_Pair);
System.out.println("no of Buckets: " + NoOfBuckets);
for (int i = 0; i < NoOfBuckets; i++) {
String BucketCode = jsonPath.param("j", j).param("i", i).getString("[j].buckets[i].bucket");
String Available = jsonPath.param("j", j).param("i", i).getString("[j].buckets[i].available");
int BucketCodeColumn = XLUtils.getBucketCodeColumn(BucketCode);
int ServiceRow = XLUtils.getServiceRow(ServiceName, DateOfJourney, OD_Pair);
System.out.println("Row of " + ServiceName + ":" + DateOfJourney + "is:" + ServiceRow);
System.out.println("Bucket Code column of " + BucketCode + " is: " + BucketCodeColumn);
XLUtils.updateAvailability(ServiceRow, BucketCodeColumn, Available);
}
}
}
}
This is the error I see:
Caused by:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup
failed:
Script1.groovy: 1: unexpected token: [ # line 1, column 27.
restAssuredJsonRootObject.[j].od_pair
Can someone help me please?
I would suggest parsing your JSON into Java classes to ease the processing.
How to do that?
First, we need to create Java classes which will represent the JSON you provided.
Let's analyze the JSON.
Starts with an array. The array contains multiple JSON Object. These objects contain od_pair value and array of objects called buckets.
Let's create a class (you can name it whatever you want) Pair
public class Pair {
public String od_pair; //this name is important because it corresponds with the json element's name!
public List<BucketObject> buckets; //same here!
}
This class represents a single JSON Object in the main Array. It contains od_pair value AND nested JSON Array but in Java representation -> List of BucketObject classes. Let's create BucketObject class:
public class BucketObject { //this name is NOT importnat
public String bucket; //names are important
public int original;
public int available;
}
We have only 3 values in each of the objects.
Now, it's time to parse JSON into the written classes.
JsonPath path = JsonPath.from(json);
Pair[] pairs = path.getObject("$", Pair[].class);
Remember that Pair is a single JSON Object. That's why we start parsing from the root represented by dollar sign $ and we declare that JSON should be parsed into an ARRAY of Pair objects!
Now, processing will be much simpler!
I am not sure what do you need, but I will show you an example of how to get data from the buckets based on od_pair field and you should be able to figure out the rest of the processing.
So, we have the array of Pair class: Pair[] pairs;
Now, we want to get 1 Pair object based on od_pair value.
public static Pair getPairBasedOnOdPairValue(Pair[] pairs, String odPairValue) {
for (Pair pair : pairs) {
if (pair.od_pair.equals(odPairValue)) return pair;
}
throw new NoSuchElementException();
}
Now, we have the Pair object. We can access buckets for this object using
List<BucketObject> buckets = pair.buckets;
The rest of the processing is iterating over List<BucketObject> and getting desired values.
Hope it helps!
OP asked me to advise on how to fix his code, hence the second answer.
Let's analyze the code you provided:
public static void updateBuckets(String ServiceName, String DateOfJourney) throws Exception {
File jsonExample = new File(System.getProperty("user.dir"), "\\LogAvResponse\\LogAvResponse.json");
JsonPath jsonPath = new JsonPath(jsonExample);
List<Object> LegList = jsonPath.getList("$");
// List<HashMap<String, String>> jsonObjectsInArray = jsonPath.getList("$");
int NoofLegs = LegList.size();
System.out.println("No of legs :" + NoofLegs);
for (int j = 0; j <= NoofLegs; j++)
// for (HashMap<String, String> jsonObject : jsonObjectsInArray) {
{
String OD_Pair = jsonPath.param("j", j).getString("[j].od_pair");
// String OD_Pair = jsonObject.get("od_pair");
System.out.println("OD Pair: " + OD_Pair);
List<Object> BucketsList = jsonPath.param("j", j).getList("[j].buckets");
int NoOfBuckets = BucketsList.size();
// System.out.println("OD Pair: " + OD_Pair);
System.out.println("no of Buckets: " + NoOfBuckets);
for (int i = 0; i < NoOfBuckets; i++) {
String BucketCode = jsonPath.param("j", j).param("i", i).getString("[j].buckets[i].bucket");
String Available = jsonPath.param("j", j).param("i", i).getString("[j].buckets[i].available");
int BucketCodeColumn = XLUtils.getBucketCodeColumn(BucketCode);
int ServiceRow = XLUtils.getServiceRow(ServiceName, DateOfJourney, OD_Pair);
System.out.println("Row of " + ServiceName + ":" + DateOfJourney + "is:" + ServiceRow);
System.out.println("Bucket Code column of " + BucketCode + " is: " + BucketCodeColumn);
XLUtils.updateAvailability(ServiceRow, BucketCodeColumn, Available);
}
}
}
I am not using compilator right now, so I can miss a few things.
#1
First thing I can see is that you save the main array into the List<Object>
List<Object> LegList = jsonPath.getList("$");
Instead, you could save it to more understandable type, since Object is so generic, you have no idea what's inside it.
List<HashMap<String, Object>> LegList = jsonPath.getList("$");
#2
The for loop looks incorrect because of the evaluator
j <= NoofLegs;.
This will probably cause IndexArrayOutOfBoundsException or something similar. With the given code, if you have 4 legs, the for loop will try to process 5 legs which are incorrect.
#3
Similar to the #1, line where you save the bucket list
List<Object> BucketsList = jsonPath.param("j", j).getList("[j].buckets");
Could also be changed to List<HashMap<String, Object>> instead.
If you'd do that, you wouldn't need integer-based nested for loop.
You see, the HashMap<String, Object> is actually crucial to parse nested objects. The String is just a name like buckets or od_pair. It's the JSON representation. The second argument Object is different. RestAssured returns different types within the HashMap, that's why we use Object instead of String. Sometimes it's not String.
Example based on your JSON:
Collect buckets to List of HashMaps:
List<HashMap<String, Object>> bucketsList = jsonPath.param("j", j).getList("[j].buckets");
Each of the HashMap in the list is a representation of this:
{
"bucket":"C00",
"original":2,
"available":2
},
The Object in HashMap is either String or Integer in your case.
So, if you get element bucket from a HashMap you'll get its value.
Let's combine it with for loop for further clarification:
List<HashMap<String, Object>> bucketsList = jsonPath.param("j", j).getList("[j].buckets");
for (HashMap<String, Object> singleBucket : bucketsList) {
String firstValue = (String) singleBucket.get("bucket");
Integer secondValue = (Integer) singleBucket.get("original");
}
Looking at the error message, it looks like you are using rest-assured and that the JsonPath class is io.restassured.path.json.JsonPath from the rest-assured library.
I'm sure you're aware, but (perhaps for other readers) note that this is different from Jayway's json-path and is NOT the com.jayway.jsonpath.JsonPath class from that library.
Also be aware that, as mentioned in the documentation rest-assured uses the Groovy GPath syntax for manipulating/extracting JSON.
With that, I believe the following will extract what you need, i.e. od_pair and their corresponding buckets with available numbers:
Map<String, Map<String, Integer>> map = JsonPath.with(jsonString).get("collectEntries{entry -> [entry.od_pair, entry.buckets.collectEntries{bucketEntry -> [bucketEntry.bucket, bucketEntry.available]}]}");
where for each entry of the map, the key is the od_pair and the value is another map whose key is the bucket and value is the available number. The jsonString is the JSON you provided in the question.
You can iterate through the map to get what you want:
for(Map.Entry<String, Map<String, Integer>> entry : map.entrySet())
{
String od_pair = entry.getKey();
Map<String, Integer> bucketMap = entry.getValue();
for(Map.Entry<String, Integer> bucketEntry : bucketMap.entrySet())
{
String bucket = bucketEntry.getKey();
int available = bucketEntry.getValue();
}
}
Printing out the map you will get:
{7015400:8727100={C00=2, A01=0, B01=480}, 7015400:8814001={C00=2, A01=40, B01=672, B03=632, B05=558}}
Printing the map as JSON using Gson:
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(map));
you will get
{
"7015400:8727100": {
"C00": 2,
"A01": 0,
"B01": 480
},
"7015400:8814001": {
"C00": 2,
"A01": 40,
"B01": 672,
"B03": 632,
"B05": 558
}
}
For background, the String collectEntries{entry -> [entry.od_pair, entry.buckets.collectEntries{bucketEntry -> [bucketEntry.bucket, bucketEntry.available]}]}
is a Groovy closure that uses methods from the Groovy Collections API: Refer Collection, List and Map
Shout out to #Fenio for the pure Java solution above.
Suppose i have json string in which there is a json array called data.
The array holds json object of user profile data for example name,age,gender etc.
Now want to parse that json object as per order, for example if the object is
{
"name": "sample name",
"age": "30",
"gender": "male"
}
i want to parse the list as ordered like name,age,gender but with ios,when i convert the json object as dictionary , the order is changed,i know dictionary is not ordered so what is the the alternative to achieve this?
its a third party api so i dont have any hand on it,we have done it in android with linked hash map,but really stuck in swift , the last thing i would want to do is parse with regular expression.
im parsing the json in following way :
var rootData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
if let val = fromList["data"] {
let dataNode = val as! [[String:Any]]
for row in dataNode {
for (key,keyVal) in row {
//here the key is not in order.because when we cast it as dictionary the order gets changed.
}
}
For android we have achieved to do this with following function :
public ArrayList<LinkedHashMap<String, Object>> parseJsonArrayList(String odata, String arrayName) {
ArrayList<LinkedHashMap<String, Object>> mylist = new ArrayList<>();
try {
JSONObject e = new JSONObject(odata);
JSONArray data = e.getJSONArray(arrayName);
for(int i = 0; i < data.length(); ++i) {
JSONObject v = data.getJSONObject(i);
LinkedHashMap<String, Object> map = new LinkedHashMap<>(100, 0.75f, false);
Iterator keys = v.keys();
while(keys.hasNext()) {
String key = String.valueOf(keys.next());
//gph.log("debug4", key);
map.put(key, v.getString(key));
//gph.log("debug4", v.getString(key));
}
mylist.add(map);
}
} catch (JSONException var10) {
var10.printStackTrace();
}
return mylist;
}
Don’t try to order the dictionary. Instead create an array of the keys in the order you desire:
let keys = [“name”, “age”, “gender”]
Then access the dictionary with them:
for key in keys {
let value = dict[key]
// Present the value.
}
That will ensure the order you expect.
As you mentioned you cannot get the ordered data in Dictionary. If possible you can add the "order" key in your JSON like
[
{
"name": "sample name",
"order": 1
},
{
"age": "30",
"order": 1
},
{
"gender": "",
"male": "",
"order": 1
}
]
so that based on the order key you can do the sorting.
Can someone help me to understand why if my val value is a "0" Integer.parseInt(val) returns me NumberFormatException and if i write Integer.parseInt("0") it works correctly...
There's a way easier to work directly with the int value read from Serial?
void draw(){
i=0;
byte[] str = new byte[5];
if ( myPort.available() > 0)
{
myPort.readBytesUntil(10, inBuffer);
if(inBuffer==null){
//...
}
else
{
while(inBuffer[i]!=13){
str[i]=inBuffer[i];
println((int)str[i]);
i++;
}
String val = new String(str);
i = Integer.parseInt(val);
println(i);
}
}
}
In Processing port.readBytesUntil(ch, buffer) reads all characters from port until encounter character equal to ch.
In Windows line separator is composed from 2 characters (0d,0a) or (13,10).
So if you write to serial "23", next newline, next "45", etc., the buffer will look like this:
char[] buffer : '2', '3', '0x0d', '0x0a', '4', '5', '0x0d', '0x0a', ...
So when you readBytesUntil(10, ...) it read 3 characters: '2', '3', '0x0d'.
Consider such example:
char[] c = {'2', '3', 13};
String str = new String(c);
System.out.println(str); // you will see "23"
System.out.println(str.length()); // lenght will be 3
System.out.println(Integer.parseInt(str.trim())); // will be OK
System.out.println(Integer.parseInt(str)); // will throw NumberFormatException
So try to convert your buffer to String and trim() this string.
== EDIT ==
Processing gives you some convenience methods such as:
String str = port.readStringUntil(10);
str = str.trim();
Can you provide an MCVE that demonstrates the problem? Use hardcoded values and keep it as simple as possible, like this:
void setup(){
String val = "0";
int i = Integer.parseInt(val);
println(i);
}
Print out the value of val before you try parsing it. I would bet it's not the value you think it is.
I have a Dictionary in Swift and I would like to get a key at a specific index.
var myDict : Dictionary<String,MyClass> = Dictionary<String,MyClass>()
I know that I can iterate over the keys and log them
for key in myDict.keys{
NSLog("key = \(key)")
}
However, strangely enough, something like this is not possible
var key : String = myDict.keys[0]
Why ?
That's because keys returns LazyMapCollection<[Key : Value], Key>, which can't be subscripted with an Int. One way to handle this is to advance the dictionary's startIndex by the integer that you wanted to subscript by, for example:
let intIndex = 1 // where intIndex < myDictionary.count
let index = myDictionary.index(myDictionary.startIndex, offsetBy: intIndex)
myDictionary.keys[index]
Another possible solution would be to initialize an array with keys as input, then you can use integer subscripts on the result:
let firstKey = Array(myDictionary.keys)[0] // or .first
Remember, dictionaries are inherently unordered, so don't expect the key at a given index to always be the same.
Swift 3 : Array() can be useful to do this .
Get Key :
let index = 5 // Int Value
Array(myDict)[index].key
Get Value :
Array(myDict)[index].value
Here is a small extension for accessing keys and values in dictionary by index:
extension Dictionary {
subscript(i: Int) -> (key: Key, value: Value) {
return self[index(startIndex, offsetBy: i)]
}
}
You can iterate over a dictionary and grab an index with for-in and enumerate (like others have said, there is no guarantee it will come out ordered like below)
let dict = ["c": 123, "d": 045, "a": 456]
for (index, entry) in enumerate(dict) {
println(index) // 0 1 2
println(entry) // (d, 45) (c, 123) (a, 456)
}
If you want to sort first..
var sortedKeysArray = sorted(dict) { $0.0 < $1.0 }
println(sortedKeysArray) // [(a, 456), (c, 123), (d, 45)]
var sortedValuesArray = sorted(dict) { $0.1 < $1.1 }
println(sortedValuesArray) // [(d, 45), (c, 123), (a, 456)]
then iterate.
for (index, entry) in enumerate(sortedKeysArray) {
println(index) // 0 1 2
println(entry.0) // a c d
println(entry.1) // 456 123 45
}
If you want to create an ordered dictionary, you should look into Generics.
From https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/CollectionTypes.html:
If you need to use a dictionary’s keys or values with an API that takes an Array instance, initialize a new array with the keys or values property:
let airportCodes = [String](airports.keys) // airportCodes is ["TYO", "LHR"]
let airportNames = [String](airports.values) // airportNames is ["Tokyo", "London Heathrow"]
SWIFT 3. Example for the first element
let wordByLanguage = ["English": 5, "Spanish": 4, "Polish": 3, "Arabic": 2]
if let firstLang = wordByLanguage.first?.key {
print(firstLang) // English
}
In Swift 3 try to use this code to get Key-Value Pair (tuple) at given index:
extension Dictionary {
subscript(i:Int) -> (key:Key,value:Value) {
get {
return self[index(startIndex, offsetBy: i)];
}
}
}
SWIFT 4
Slightly off-topic: But here is if you have an
Array of Dictionaries i.e: [ [String : String] ]
var array_has_dictionary = [ // Start of array
// Dictionary 1
[
"name" : "xxxx",
"age" : "xxxx",
"last_name":"xxx"
],
// Dictionary 2
[
"name" : "yyy",
"age" : "yyy",
"last_name":"yyy"
],
] // end of array
cell.textLabel?.text = Array(array_has_dictionary[1])[1].key
// Output: age -> yyy
Here is an example, using Swift 1.2
var person = ["name":"Sean", "gender":"male"]
person.keys.array[1] // "gender", get a dictionary key at specific index
person.values.array[1] // "male", get a dictionary value at specific index
I was looking for something like a LinkedHashMap in Java. Neither Swift nor Objective-C have one if I'm not mistaken.
My initial thought was to wrap my dictionary in an Array. [[String: UIImage]] but then I realized that grabbing the key from the dictionary was wacky with Array(dict)[index].key so I went with Tuples. Now my array looks like [(String, UIImage)] so I can retrieve it by tuple.0. No more converting it to an Array. Just my 2 cents.
I'm using the JSON library floating around for Blackberry and following this answer in "How to parse the JSON response in Blackberry/J2ME?".
The problem I'm having is that I'm getting an error saying JSONObject has to begin with a "{". My JSON string is wrapped in [ ] , which is something the web service does.
Libraries I've used for Android and iPhone stripped that, so I was wondering what is the best way around this problem? I don't think I can just parse out all [ ] because I think those are used in multidimensional JSON strings.
Edit:
Here's an example:
[{"nid":"1","title":"test","image":"m0.jpg","by":"Presented by","by_name":"Inc.","summary":"..."}, {"nid":"6","title":"A","image":".jp[0.0] g","by":"Presented by","by_name":"Theatre","summary":""}]
If you are not sure about the validity of JSON data then use any JSON Validator, e.g. JSONLint.
And you have some unwanted character in your data, i.e. [, and ] in "image":".jp[0.0] g". I think those data are added by Eclipse while printing on console.
Data provided in example isn't represent a JSONObject, butit is an array. So start with constructing a JSONArray from the data and do the parsing. Below is an example code snippet (with modified data set):
String strJSONData = "[{\"nid\":\"1\",\"title\":\"test\"},{\"nid\":\"6\",\"title\":\"A\"}]";
final String CONS_NID = "nid";
final String CONS_TITLE = "title";
try {
JSONArray ja = new JSONArray(strJSONData);
if (ja != null) {
JSONObject arrObj;
for (int i = 0; i < ja.length(); i++) {
arrObj = (JSONObject) ja.get(i);
if (arrObj.has(CONS_NID)) {
System.out.println("ID: " + arrObj.getString(CONS_NID));
}
if (arrObj.has(CONS_TITLE)) {
System.out.println("Title: " + arrObj.getString(CONS_TITLE));
}
}
arrObj = null;
}
ja = null;
} catch (Exception exc) {
}
strJSONData = null;
If you know it is starting and ending with '[' and ']', then you can just check that, and take the substring in between and hand it to the parser.
String myJsonString = ...;
if(myJsonString.charAt(0) == '[' && myJsonString.charAt(myJsonString.length() - 1) == ']') {
realJsonParse(myJsonString.substring(1, myJsonString.length() - 1);
}