karate framework - how to count number of arrays in json response - arrays

there
I have a JSON response as below:
"log": [{
"a": 0.40,
"b": "ED",
}, {
"c": 82,
"d": "ABC",
}, {
"e": 36,
"f": 23,
}, {
"g": 12,
"h": 40,
}
]
I need to count a number of lists in a 'log' object to be able to address the last one and find a specific element in it. The response is dynamic and has different amount of lists in it (in this case 4)
I tried log[-1], and examples form js-arrays.feature as in the link below:
https://github.com/intuit/karate/blob/master/karate-junit4/src/test/java/com/intuit/karate/junit4/demos/js-arrays.feature#L83
It is easy to find a number of elements in a list, but I frequently have variable amount of lists and I cant make it work
Many Thanks,

Take into account that log[-1] would return you undefined due to there is no index -1 in the array. For get the number of elements in your array you must do
log.length
Also about the link you've posted
log[log.length-1]; //This will return the last element of the array in this case { "g": 12, "h": 40, }

Related

Get consistent byte array output from json.Marshal

I'm working on a hashing function for a map[string]interface{}
Most of the hashing libs required []byte as input to compute the hash.
I tried to Marshal using the json.Marshal for simple maps it works correct but when i add some complexity and shuffled the items then json.Marshal fails to give me a consistent byte array output
package main
import (
"encoding/json"
"fmt"
)
func main() {
data := map[string]interface{}{
"id": "124",
"name": "name",
"count": 123456,
"sites": []map[string]interface{}{
{
"name": "123445",
"count": 234324,
"id": "wersfs",
},
{
"id": "sadcacasca",
"name": "sdvcscds",
"count": 22,
},
},
"list": []int{5, 324, 123, 123, 123, 14, 34, 52, 3},
}
data1 := map[string]interface{}{
"name": "name",
"id": "124",
"sites": []map[string]interface{}{
{
"id": "sadcacasca",
"count": 22,
"name": "sdvcscds",
},
{
"count": 234324,
"name": "123445",
"id": "wersfs",
},
},
"count": 123456,
"list": []int{123, 14, 34, 52, 3, 5, 324, 123, 123},
}
jsonStr, _ := json.Marshal(data)
jsonStr1, _ := json.Marshal(data1)
fmt.Println(jsonStr)
fmt.Println(jsonStr1)
for i := 0; i < len(jsonStr); i++ {
if jsonStr[i] != jsonStr1[i] {
fmt.Println("Byte arrays not equal")
}
}
}
This is what I have tried and it fails to give me a consistent output.
Moreover i was thinking to write a function which will do the sorting of the map and values as well, but then got stuck on how do I sort the
"sites": []map[string]interface{}
I tried json.Marshal and also sorting the map but got stuck
Your data sructures are not equivalent. According to JSON rules arrays are ordered, therefore [123, 14, 34, 52, 3, 5, 324, 123, 123] is not the same as [5, 324, 123, 123, 123, 14, 34, 52, 3]. No wonders the hashes are different. If you need different arrays with the same elements to produce the same hash, you need to canonicalize the arrays before hashing. E.g. sort them.
Here is how it could be done: https://go.dev/play/p/OHq7jsX_cNw
Before serilizing it recursively gos down the maps and arrays and prepares all arrays:
// Prepares data by sorting arrays in place
func prepare(data map[string]any) map[string]any {
for _, value := range data {
switch v := value.(type) {
case []int:
prepareIntArray(v)
case []string:
prepareStringArray(v)
case []map[string]any:
prepareMapArrayById(v)
for _, obj := range v {
prepare(obj)
}
case map[string]any:
prepare(v)
}
}
return data
}
// Sorts int array in place
func prepareIntArray(a []int) {
sort.Ints(a)
}
// Sorts string array in place
func prepareStringArray(a []string) {
sort.Strings(a)
}
// Sorts an array of objects by "id" fields
func prepareMapArrayById(mapSlice []map[string]any) {
sort.Slice(mapSlice, func(i, j int) bool {
return getId(mapSlice[i]) < getId(mapSlice[j])
})
}
// Extracts "id" field from JSON object. Returns empty string if there is no "id" or it is not a string.
func getId(v map[string]any) string {
idAny, ok := v["id"]
if !ok {
return ""
}
idStr, ok := idAny.(string)
if ok {
return idStr
} else {
return ""
}
}
As both the marshaled outputs are basically string representations of the same map in different sequences, if you sort their characters, they become equal.
following this logic, if you sort both jsonStr and jsonStr1, the sorted []byte(s) will be exactly equal. which then you can use to formulate your hash value.
check my solution here

Karate match two JSON files: array of objects which contains inner shuffled arrays of object

Task:
Have 3 endpoints:
1st returns random data for request to two other endpoints.
Two other endpoints operates with different DBs but on, more or less, similar data.
I need to validate that response from one endpoint is matching response from another, both endpoints send the same data but in different order.
e.g.:
endpoint 'A':
{
"items": [
{
"field1": 111,
"array1": [
{
"field11": 102,
"field22": 231
},
{
"field11": 103,
"field22": 231
},
{
"field11": 102,
"field22": 256
},
{
"field11": 104,
"field22": 256
}
],
"field2": 122,
"array2": [
1,
2,
3
]
},
{
"field1": 211,
"field2": 222,
"field3": 233
}
]
}
endpoint 'B':
{
"items": [
{
"field1": 211,
"field2": 222,
"field3": 233
},
{
"field1": 111,
"array1": [
{
"field11": 104,
"field22": 256
},
{
"field11": 102,
"field22": 256
},
{
"field11": 103,
"field22": 231
},
{
"field11": 102,
"field22": 231
}
],
"field2": 222,
"array2": [
1,
2,
3
]
}
]
}
endpointA_response.items contains only endpointB_response.items verification fails because it considers inner array1 from one response doesn't match array1 from another response because array items are in different order.
endpointA_response.items[0].array1 contains only endpointB_response.items[1].array1 works OK though, but it is not what I need. I need to compare whole response regardless of ordering.
Is there any possibility to apply contains only verification to array of objects which contains arrays which could have items in different order?
You were on the right track with endpointA_response.items[0].array1 contains only endpointB_response.items[1].array1
I need to compare whole response regardless of ordering.
Then you have to do some "extra" work. There is no magic.
I would suggest sorting. In Karate 1.0, there is a karate.sort() API. For example, if you are sure that the value of field1 is a unique-identfier
* def expected = karate.sort(responseA, x => x.field1)
Then hopefully it aligns things so that you can do a contains match.
Read this answer for other ideas: https://stackoverflow.com/a/53120851/143475

Groovy - FindAll - unique record - condition declared by field

I have a json as below
{
"Animals": [
{
"Name": "monkey",
"Age": 4
},
{
"Name": "lion",
"Age": 3
},
{
"Name": "lion",
"Age": 3,
"Misc": "001"
}
]
}
2 elements out of 3 inside json array has the Name and Age. The only difference is that 3rd element has Misc and the 2nd does not have Misc.
How to get the record having Misc when there are 2 records with same Name and Age?
Below is what I tried
parsedJson?.Animals = parsedJson?.Animals?.unique().findAll{animal -> animal?.Misc?.trim() ? animal?.Misc?.trim() : site?.Name?.trim() };
Looks like I missed one more statement or I missed something inside unique()
I also tried
parsedJson?.Animals = parsedJson?.Animals?.unique{a1,a2 -> a1?.Misc <=> a2?.Misc}
but still not get what I want
What I want is
{
"Animals": [
{
"Name": "monkey",
"Age": 4
},
{
"Name": "lion",
"Age": 3,
"Misc": "001"
}
]
}
One way to go about this is by grouping the elements and then just merge
the maps.
groupBy is used to group the elements by their "primary key" -- lets
assume, that this is Name and Age. The resulting data structure is
a map with [it.Name, it.Age] tuples and keys and a list of elements,
that hold that property.
Next reduce over the list of maps and just merge them. This assumes,
that the information there does not contradict itself (e.g. only adds to
the result). Otherwise the last map would just win.
def data = [["Name": "monkey", "Age": 4],
["Name": "lion", "Age": 3],
["Name": "lion", "Age": 3, "Misc": "001"]]
println data.groupBy{[it.Name, it.Age]}.collect{ _, xs -> xs.inject{ acc, x -> acc.putAll x; acc } }
// → [[Name:monkey, Age:4], [Name:lion, Age:3, Misc:001]]

Karate - how to check if array contains values?

Given
I have defined array
* def array = [
{"code": "codeA", "boolValue": false, "a": 5, "c": false},
{"code": "codeA", "boolValue": true, "a": 7, "c": true},
{"code": "codeB", "boolValue": true, "a": 1, "c": false}
]
And variable
* def expected = { "code": "codeB", "boolValue": true }
Issues
In the last element of array there is expected value.
But it contains some additional values and that's why it failed all the time I try to check that.
How to check if array contains expected?
This will work in 0.9.6.RC4:
* match array contains deep expected
In prior versions:
* match array contains '#(^expected)'
Please read the docs: https://github.com/intuit/karate/tree/develop#schema-validation

How to sort in array of object - CouchBase

Hi currently I want to sort array of object, I use ARRAY_SORT function, it will use the first field of object to sort & it work well if every object has the same JSON structure. If one element in array has different JSON structure, the result is incorrect.
The query I use :
SELECT ARRAY_SORT(c.student) as student FROM Class c
Result :
"student": [
{
"id": 3,
"name": "Kenny35"
},
{
"id": 6,
"name": "Kenny35"
},
{
"id": 7,
"name": "Kenny35"
},
{
"id": 8,
"name": "Kenny35"
},
{
"hobby": "video game",
"id": 5,
"name": "Kenny35"
}
]
How can I specify property of object in array for ARRAY_SORT function ?
dev,
Objects are first compared by length/size of the object, then followed by fields in the object.
http://developer.couchbase.com/documentation/server/4.5/n1ql/n1ql-language-reference/comparisonops.html
That is the only collation supported now.
-Prasad
You can issue a query and use ORDER BY.
SELECT *
FROM Class c
UNNEST c.student s
ORDER BY ...

Resources