Modify nested arrays in JSON (Groovy) - json

I'm trying to parse and mofidy JSON with Groovy. Source JSON from REST API looks like:
[
{
"id":27858,
"type":"ad",
"stats":[
{
"day":"2021-01-21",
"sex":[
{
"impressions_rate":0.349,
"value":"f"
},
{
"impressions_rate":0.621,
"value":"m",
"clicks_rate":0.22
}
],
"age":[
{
"impressions_rate":0.217,
"value":"18-21"
}
]
},
{
"day":"2021-02-25",
"sex":[
{
"impressions_rate":0.349,
"value":"f"
},
{
"impressions_rate":0.651,
"value":"m"
}
],
"age":[
{
"impressions_rate":0.217,
"value":"18-21"
}
]
}
]
},
{
"id":565291,
"type":"ad",
"stats":[
{
"day":"2021-03-21",
"sex":[
{
"impressions_rate":0.78,
"value":"f",
"clicks_rate":0.33
},
{
"impressions_rate":0.551,
"value":"m"
}
],
"age":[
{
"impressions_rate":0.17,
"value":"18-21"
}
]
}
]
}
]
It's an array with some ids and data for them. I want to grab id, day inside stats array and elements from sex array. After all manipulations my JSON should be like this:
[
{
"id": 27858,
"day": "2021-01-21",
"impression_rate": 0.349,
"value": "f"
},
{
"id": 27858,
"day": "2021-01-21",
"impression_rate": 0.621,
"value": "f",
"clicks_rate": 0.22
},
{
"id": 27858,
"day": "2021-02-25",
"impressions_rate":0.349,
"value":"f"
},
{
"id": 27858,
"day": "2021-02-25",
"impressions_rate":0.651,
"value":"m"
},
{
"id": 565291,
"day": "2021-03-21",
"impressions_rate":0.78,
"value":"f",
"clicks_rate":0.33
},
{
"id": 565291,
"day": "2021-03-21",
"impressions_rate":0.78,
"value":"f",
"clicks_rate":0.33
}
]
So, the main goal is - loop through all ids -> elements in sex array (for each id) and add to these elements day and id mapped fields. I tried to start with empty map with inject, but after 1 hour of debugging i still can't achieve desired output, so maybe better to loop through existed values in array? But I can't even reach sex array.
import groovy.json.*
def json = new JsonSlurper().parseText '''...'''
List expected = json.inject([]){ r, e ->
Map ids = e.findAll {
k, v -> k == "id"
}
e.each{ k, v ->
if( (v.any{ it.sex } )
v.each{ r << ids + it }
}
return r
}

If you have nested structures, that contain again nested structures,
a good option, to get a flat result, is to use collectMany; like
collect it transforms each item of the iterated container, but the
results gets concated.
E.g. you can collectMany on your outer data, then again on the
stats, and finally just collect over sex.
def data = new groovy.json.JsonSlurper().parse("data.json" as File)
println data.collectMany{ o ->
o.stats.collectMany{ i ->
i.sex.collect{ it + [id: o.id, day: i.day] }
}
}
// [[impressions_rate:0.349, value:f, id:27858, day:2021-01-21],
// [impressions_rate:0.621, value:m, clicks_rate:0.22, id:27858, day:2021-01-21],
// [impressions_rate:0.349, value:f, id:27858, day:2021-02-25],
// [impressions_rate:0.651, value:m, id:27858, day:2021-02-25],
// [impressions_rate:0.78, value:f, clicks_rate:0.33, id:565291, day:2021-03-21],
// [impressions_rate:0.551, value:m, id:565291, day:2021-03-21]]

Related

How do I add values I got from forEach? (reactjs)

I'm trying to extract data from an API call that gives me this JSON:
JSON data:
{ "rows":
[
{
"customer_id": 1,
"customer_details":
{
"customer_id": 1,
"email":"john#mail.com",
"first_name":"John",
"last_name":"Doe",
"phone":"+123456123"
},
"order_items":[
{
"name": "random name",
"quantity":1
}
]
},
{
"customer_id": 2,
"customer_details":
{
"customer_id": 2,
"email":"johnny#mail.com",
"first_name":"Johnny",
"last_name":"Silverhand",
"phone":"+123456123"
},
"order_items":[
{
"name": "random name",
"quantity":1
},
{
"name": "another random name",
"quantity":1
}
]
}
]
}
I am actually able to get the the quantity but when there are two or more objects from the array in order_items like with customer 2, what I get is 11 how do I add the values I get? Sorry if this is really a simple problem but I am really confused.
here is my code for how I get them:
function getQuantity(qty) {
if(qty.length === 1) {
return qty[0].quantity;
} else {
let tempArr = [];
qty.forEach(prod => {
// console.log(prod.quantity)
tempArr.push(prod.quantity)
});
return tempArr;
}
}
I mapped used the function I made inside a map here is part of the code:
{orderList.map((val, key) => {
if (val.status_id !== 5) {
return (
<tr key={key} className='text-center'>
<td className='tdata'>{getQuantity(val.order_items)}</td>
Your getQuantity function returns an array of numbers (given this json data) and when you set the value of your td to be, as in your example, [1, 1] it will output "1""1" which will look like "11".
If you want to display the sum of your values, you could do something like this (although there is probably a better way to sum all the numbers in an array)
function getQuantity(qty) {
if(qty.length === 1) {
return qty[0].quantity;
} else {
let tempVal = 0;
qty.forEach(prod => {
tempVal += prod.quantity
});
return tempVal;
}
}

Unnesting JSON String stored in a column [BigQuery]

I have a table with one of the columns containing a raw JSON string as follows:
Sample JSON stored in order_lines:
{
"STR_BLK_002":{
"amount":167,
"type":"part spare",
"total_discount":0,
"color":"Black",
"is_out_of_stock":false,
"variable_fields":{
"Size":"XL",
"trueColor":"Black"
},
"category_id":"44356721",
"status_list":[
{
"id":1,
"time":"2021-04-01T15:01:54.746Z",
"status":"ORDER PLACED"
},
{
"id":2,
"time":"2021-04-02T10:31:00.397Z",
"status":"PACKED"
},
{
"id":3,
"time":"2021-04-04T10:31:01.719Z",
"status":"SHIPPED"
},
{
"id":3,
"time":"2021-04-04T18:12:06.896Z",
"status":"SHIPPED"
}
],
"product_id":270,
"price_per_quantity":167,
"quantity":1,
"maximum_quantity":10,
"variant_name":"Helmet strap",
"current_status":30,
"estimated_delivery":"09 Apr 2021",
"total_before_discount":167,
"delivery_statuses":[
{
"time":"2021-04-01T15:10:13.594Z",
"status":"FULFILLABLE"
},
{
"time":"2021-04-02T10:31:00.397Z",
"status":"PACKED"
},
{
"time":"2021-04-03T10:31:01.197Z",
"status":"READY_TO_SHIP"
},
{
"time":"2021-04-04T10:31:01.719Z",
"status":"DISPATCHED"
},
{
"time":"2021-04-04T18:12:06.896Z",
"status":"SHIPPED"
}
],
"sku_code":"STR_BLK_002"
}
}
I want to unnest this string so that the key value pairs can be accessed individually. Also the sku_code, ('STR_BLK_002' in the sample shared above) is not available in any other column and the string can contain more a single sku, so if there are 2 sku(s) corresponding to an order then the JSON string would be:
{
"STR_BLK_002":{
"amount":167,
"type":"part spare",
"total_discount":0,
"color":"Black",
"is_out_of_stock":false,
"variable_fields":{
"Size":"XL",
"trueColor":"Black"
},
"category_id":"44356721",
"status_list":[
{
"id":1,
"time":"2021-04-01T15:01:54.746Z",
"status":"ORDER PLACED"
},
{
"id":2,
"time":"2021-04-02T10:31:00.397Z",
"status":"PACKED"
},
{
"id":3,
"time":"2021-04-04T10:31:01.719Z",
"status":"SHIPPED"
},
{
"id":3,
"time":"2021-04-04T18:12:06.896Z",
"status":"SHIPPED"
}
],
"product_id":270,
"price_per_quantity":167,
"quantity":1,
"maximum_quantity":10,
"variant_name":"Helmet strap",
"current_status":3,
"estimated_delivery":"09 Apr 2021",
"total_before_discount":167,
"delivery_statuses":[
{
"time":"2021-04-01T15:10:13.594Z",
"status":"FULFILLABLE"
},
{
"time":"2021-04-02T10:31:00.397Z",
"status":"PACKED"
},
{
"time":"2021-04-03T10:31:01.197Z",
"status":"READY_TO_SHIP"
},
{
"time":"2021-04-04T10:31:01.719Z",
"status":"DISPATCHED"
},
{
"time":"2021-04-04T18:12:06.896Z",
"status":"SHIPPED"
}
],
"sku_code":"STR_BLK_002"
},
"STR_BLK_008":{
"amount":590,
"type":"accessory",
"total_discount":0,
"color":"blue",
"is_out_of_stock":false,
"variable_fields":{
"Size":"XL",
"trueColor":"prussian blue"
},
"category_id":"65577970",
"status_list":[
{
"id":1,
"time":"2021-04-06T15:01:54.746Z",
"status":"ORDER PLACED"
},
{
"id":2,
"time":"2021-04-07T10:31:00.397Z",
"status":"PACKED"
},
{
"id":3,
"time":"2021-04-07T10:31:01.719Z",
"status":"SHIPPED"
},
{
"id":3,
"time":"2021-04-08T18:12:06.896Z",
"status":"SHIPPED"
}
],
"product_id":276,
"price_per_quantity":590,
"quantity":1,
"maximum_quantity":5,
"variant_name":"Car Perfume",
"current_status":3,
"estimated_delivery":"09 Apr 2021",
"total_before_discount":590,
"delivery_statuses":[
{
"time":"2021-04-06T15:10:13.594Z",
"status":"FULFILLABLE"
},
{
"time":"2021-04-07T10:31:00.397Z",
"status":"PACKED"
},
{
"time":"2021-04-07T10:31:01.197Z",
"status":"READY_TO_SHIP"
},
{
"time":"2021-04-08T10:31:01.719Z",
"status":"DISPATCHED"
},
{
"time":"2021-04-10T18:12:06.896Z",
"status":"SHIPPED"
}
],
"sku_code":"STR_BLK_008"
}
}
I want to break this information into separate columns, so that I can fetch the corresponding values for each SKU.
So basically what I think you want to do is first transform your column into a array of structs so that instead of having this:
{
"STR_BLK_002": {...},
"STR_BLK_003": {...}
}
You have something like this:
[
{
"amount":167,
"type":"part spare",
"total_discount":0,
...
},
{
"amount":590,
"type":"accessory",
"total_discount":0,
...
}
]
With the data in that format you can leverage UNNEST to make each entry into its own row, and then use JSON functions to pull out fields into their own columns, for example JSON_EXTRACT_SCALAR
In order to do this, I built a Javascript UDF that find the keys in the object and then iterates through each key to create an array of structs.
CREATE TEMP FUNCTION format_json(str STRING)
RETURNS ARRAY<STRING>
LANGUAGE js AS r"""
var obj = JSON.parse(str);
var keys = Object.keys(obj);
var arr = [];
for (i = 0; i < keys.length; i++) {
arr.push(JSON.stringify(obj[keys[i]]));
}
return arr;
""";
SELECT
JSON_EXTRACT_SCALAR(formatted_json,'$.amount') as amount
,JSON_EXTRACT_SCALAR(formatted_json,'$.type') as type
,JSON_EXTRACT_SCALAR(formatted_json,'$.total_discount') as total_discount
,JSON_EXTRACT_SCALAR(formatted_json,'$.color') as color
,JSON_EXTRACT_SCALAR(formatted_json,'$.is_out_of_stock') as is_out_of_stock
,JSON_EXTRACT_SCALAR(formatted_json,'$.sku_code') as sku_code
from
testing.json_test
left join unnest(format_json(order_lines)) as formatted_json
Which results in this:
Below should give you good start
select
json_extract_scalar(line, '$.sku_code') as sku_code,
json_extract_scalar(line, '$.amount') as amount,
json_extract_scalar(line, '$.type') as type,
json_extract_scalar(line, '$.total_discount') as total_discount,
json_extract_scalar(line, '$.color') as color,
json_extract_scalar(line, '$.variable_fields.Size') as Size,
json_extract_scalar(line, '$.variable_fields.trueColor') as trueColor,
from `project.dataset.table`,
unnest(split(regexp_replace(regexp_replace(order_lines, r'\s', ''), r'"STR_BLK_\d+":{', '"STR_BLK":{'),'"STR_BLK":')) order_line with offset,
unnest([struct('{' || trim(order_line, ',{}}') || '}' as line)])
where offset > 0
if applied to first example in your question - output is
if applied to second example in your question - output is
Hope, you can extend this example to whatever final goal you have in mind

Put Data in mutlple branch of Array : Json Transformer ,Scala Play

i want to add values to all the arrays in json object.
For eg:
value array [4,2.5,2.5,1.5]
json =
{
"items": [
{
"id": 1,
"name": "one",
"price": {}
},
{
"id": 2,
"name": "two"
},
{
"id": 3,
"name": "three",
"price": {}
},
{
"id": 4,
"name": "four",
"price": {
"value": 1.5
}
}
]
}
i want to transform the above json in
{
"items": [
{
"id": 1,
"name": "one",
"price": {
"value": 4
}
},
{
"id": 2,
"name": "two",
"price": {
"value": 2.5
}
},
{
"id": 3,
"name": "three",
"price": {
"value": 2.5
}
},
{
"id": 4,
"name": "four",
"price": {
"value": 1.5
}
}
]
}
Any suggestions on how do i achieve this. My goal is to put values inside the specific fields of json array. I am using play json library throughout my application. What other options do i have instead of using json transformers.
You may use simple transformation like
val prices = List[Double](4,2.5,2.5,1.5).map(price => Json.obj("price" -> Json.obj("value" -> price)))
val t = (__ \ "items").json.update(
of[List[JsObject]]
.map(_.zip(prices).map(o => _._1 ++ _._2))
.map(JsArray)
)
res5: play.api.libs.json.JsResult[play.api.libs.json.JsObject] = JsSuccess({"items":[{"id":1,"name":"one","price":{"value":4}},{"id":2,"name":"two","price":{"value":2.5}},{"id":3,"name":"three","price":{"value":2.5}},{"id":4,"name":"four","price":{"value":1.5}}]},/items)
I suggest using classes, but not sure this fits to your project because it's hard to guess how your whole codes look like.
I put new Item manually for simplicity. You can create items using Json library :)
class Price(val value:Double) {
override def toString = s"{value:${value}}"
}
class Item(val id: Int, val name: String, val price: Price) {
def this(id: Int, name: String) {
this(id, name, null)
}
override def toString = s"{id:${id}, name:${name}, price:${price}}"
}
val price = Array(4, 2.5, 2.5, 1.5)
/** You might convert Json data to List[Item] using Json library instead. */
val items: List[Item] = List(
new Item(1, "one"),
new Item(2, "two"),
new Item(3, "three"),
new Item(4, "four", new Price(1.5))
)
val valueMappedItems = items.zipWithIndex.map{case (item, index) =>
if (item.price == null) {
new Item(item.id, item.name, new Price(price(index)))
} else {
item
}
}

How can i Parse JSON data values present at any level in GROOVY

Following is my Parsed JSON with json Surplur . i has been required for SOAPUI scripts to manupulate
{buildInfo={application=RepCatalog, buildDate=Thu Oct 13 17:01:48 IST 2016, version=1.0.0}, data={Reps=[{cascadeCount=0, catalogRep={RepId=48961, RepType=REPORT, initialCreation=10/13/2016 20:39:11, lastAccessed=10/13/2016 20:39:11, lastModified=10/13/2016 20:39:11, parentRep={RepId=48962, RepType=REPORT, initialCreation=10/13/2016 20:39:14, lastAccessed=10/13/2016 20:39:14, lastModified=10/13/2016 20:39:14, status=OPEN, title=REPORT1476371359504}, rights=[{availability=PUBLIC, isDefault=true}], status=OPEN, title=REPORT1476371357505, userTags=[PRIVATE1476371349835]}, status=success}]}, status=success, summary={apiName=Integration Service, partialRepSucceeded=0, totalRepFailed=0, totalRepProccessed=1, totalRepSucceeded=1}, time=6674}
Following is unparsed JSON
{
"summary": {
"apiName": "Integration Service",
"totalRepProccessed": 1,
"totalRepFailed": 0,
"totalRepSucceeded": 1,
"partialRepSucceeded": 0
},
"buildInfo": {
"application": "RepCatalog",
"version": "1.0.0",
"buildDate": "Thu Oct 13 17:01:48 IST 2016"
},
"status": "success",
"data": {"Reps": [ {
"status": "success",
"catalogRep": {
"RepId": 48961,
"RepType": "REPORT",
"title": "REPORT1476371357505",
"rights": [ {
"availability": "PUBLIC",
"isDefault": true
}],
"initialCreation": "10/13/2016 20:39:11",
"lastModified": "10/13/2016 20:39:11",
"lastAccessed": "10/13/2016 20:39:11",
"status": "OPEN",
"parentRep": {
"RepId": 48962,
"RepType": "REPORT",
"title": "REPORT1476371359504",
"status": "OPEN"
},
"userTags": ["PRIVATE1476371349835"]
},
"cascadeCount": 0
}]},
"time": 6674
}
I want to parse it to get values of All RepId in above in groovy SOAPUI
Given your input as a string variable called json, the following script:
def extractRepIds (def tree, def ids = []) {
switch (tree) {
case Map:
tree.each { k, v ->
if (k == "RepId") { ids << v }
extractRepIds(v, ids)
}
return ids
case Collection:
tree.each { e -> extractRepIds(e, ids) }
return ids
default :
return ids
}
}
def extractRepIdsFromJson(def jsonString) {
def tree = new JsonSlurper().parseText(jsonString)
extractRepIds(tree)
}
println extractRepIdsFromJson(json)
produces the following results:
[48961, 48962]
Alternate Solution
The extractRepIds method can be written somewhat more cleanly by using the inject() method:
def extractRepIds (def tree) {
switch (tree) {
case Map:
return tree.inject([]) { list, k, v ->
list + (k == "RepId" ? [v] : extractRepIds(v))
}
case Collection:
return tree.inject([]) { list, e ->
list + extractRepIds(e)
}
default :
return []
}
}
With all else being the same, this yields the same results.
If you want to find all occurrences in a file, you can use regular expressions. It may be useful in this case.
def pattern = ~'\"RepId\":\\s(\\d+)' //"RepId": 48961
def path = "/tmp/data.json"
def data = new File(path).text
def repIds = []
pattern.matcher(data).findAll{fullMatch,repId ->
repIds << repId
}

Loading TreeStore with JSON that has different children fields

I am having a JSON data like below.
{
"divisions": [{
"name": "division1",
"id": "div1",
"subdivisions": [{
"name": "Sub1Div1",
"id": "div1sub1",
"schemes": [{
"name": "Scheme1",
"id": "scheme1"
}, {
"name": "Scheme2",
"id": "scheme2"
}]
}, {
"name": "Sub2Div1",
"id": "div1sub2",
"schemes": [{
"name": "Scheme3",
"id": "scheme3"
}]
}
]
}]
}
I want to read this into a TreeStore, but cannot change the subfields ( divisions, subdivisions, schemes ) to be the same (eg, children).
How can achieve I this?
When nested JSON is loaded into a TreeStore, essentially the children nodes are loaded through a recursive calls between TreeStore.fillNode() method and NodeInterface.appendChild().
The actual retrieval of each node's children field is done within TreeStore.onNodeAdded() on this line:
dataRoot = reader.getRoot(data);
The getRoot() of the reader is dynamically created in the reader's buildExtractors() method, which is what you'll need to override in order to deal with varying children fields within nested JSON. Here is how it's done:
Ext.define('MyVariJsonReader', {
extend: 'Ext.data.reader.Json',
alias : 'reader.varijson',
buildExtractors : function()
{
var me = this;
me.callParent(arguments);
me.getRoot = function ( aObj ) {
// Special cases
switch( aObj.name )
{
case 'Bill': return aObj[ 'children' ];
case 'Norman': return aObj[ 'sons' ];
}
// Default root is `people`
return aObj[ 'people' ];
};
}
});
This will be able to interpret such JSON:
{
"people":[
{
"name":"Bill",
"expanded":true,
"children":[
{
"name":"Kate",
"leaf":true
},
{
"name":"John",
"leaf":true
}
]
},
{
"name":"Norman",
"expanded":true,
"sons":[
{
"name":"Mike",
"leaf":true
},
{
"name":"Harry",
"leaf":true
}
]
}
]
}
See this JsFiddle for fully working code.