MariaDB parse a JSON String - parsing

This works in SQL Server:
declare #jstring varchar(max)='{
"FieldInfo": {
"Template": "Babies",
"Groups": [{
"Group": "Required",
"SortOrder": 1,
"BackgroundColor": "FCD5B4",
"Fields": [{
"fieldName": "feed_product_type",
"SortOrder": 1,
"labelName": "Product Type",
"defaultValue": "",
"columnIndex": 0
}, {
"fieldName": "item_sku",
"SortOrder": 2,
"labelName": "Seller SKU",
"defaultValue": "",
"columnIndex": 1
}, {
"fieldName": "brand_name",
"SortOrder": 3,
"labelName": "Brand",
"defaultValue": "",
"columnIndex": 2
}, {
"fieldName": "item_name",
"SortOrder": 4,
"labelName": "Title",
"defaultValue": "",
"columnIndex": 3
}, {
"fieldName": "external_product_id",
"SortOrder": 5,
"labelName": "Product ID",
"defaultValue": "",
"columnIndex": 4
}, {
"fieldName": "external_product_id_type",
"SortOrder": 6,
"labelName": "Product ID Type",
"defaultValue": "",
"columnIndex": 5
}, {
"fieldName": "item_type",
"SortOrder": 7,
"labelName": "Item Type Keyword",
"defaultValue": "",
"columnIndex": 6
}, {
"fieldName": "model",
"SortOrder": 8,
"labelName": "Model Number",
"defaultValue": "",
"columnIndex": 7
}, {
"fieldName": "manufacturer",
"SortOrder": 9,
"labelName": "Manufacturer",
"defaultValue": "",
"columnIndex": 8
}, {
"fieldName": "part_number",
"SortOrder": 10,
"labelName": "Part Number",
"defaultValue": "",
"columnIndex": 9
}, {
"fieldName": "mfg_minimum",
"SortOrder": 11,
"labelName": "Minimum Manufacturer Age Recommended",
"defaultValue": "",
"columnIndex": 10
}, {
"fieldName": "standard_price",
"SortOrder": 12,
"labelName": "Standard Price",
"defaultValue": "",
"columnIndex": 11
}, {
"fieldName": "quantity",
"SortOrder": 13,
"labelName": "Quantity",
"defaultValue": "",
"columnIndex": 12
}, {
"fieldName": "main_image_url",
"SortOrder": 14,
"labelName": "Main Image URL",
"defaultValue": "",
"columnIndex": 13
}]
}, {
"Group": "Images",
"SortOrder": 2,
"BackgroundColor": "FFFF00",
"Fields": [{
"fieldName": "other_image_url1",
"SortOrder": 15,
"labelName": "Other Image URL1",
"defaultValue": "",
"columnIndex": 14
}, {
"fieldName": "other_image_url2",
"SortOrder": 16,
"labelName": "Other Image URL2",
"defaultValue": "",
"columnIndex": 15
}, {
"fieldName": "other_image_url3",
"SortOrder": 17,
"labelName": "Other Image URL3",
"defaultValue": "",
"columnIndex": 16
}, {
"fieldName": "other_image_url4",
"SortOrder": 18,
"labelName": "Other Image URL4",
"defaultValue": "",
"columnIndex": 17
}, {
"fieldName": "other_image_url5",
"SortOrder": 19,
"labelName": "Other Image URL5",
"defaultValue": "",
"columnIndex": 18
}, {
"fieldName": "other_image_url6",
"SortOrder": 20,
"labelName": "Other Image URL6",
"defaultValue": "",
"columnIndex": 19
}, {
"fieldName": "other_image_url7",
"SortOrder": 21,
"labelName": "Other Image URL7",
"defaultValue": "",
"columnIndex": 20
}, {
"fieldName": "other_image_url8",
"SortOrder": 22,
"labelName": "Other Image URL8",
"defaultValue": "",
"columnIndex": 21
}, {
"fieldName": "swatch_image_url",
"SortOrder": 23,
"labelName": "Swatch Image URL",
"defaultValue": "",
"columnIndex": 22
}]
}, {
"Group": "Variation",
"SortOrder": 3,
"BackgroundColor": "FF8080",
"Fields": [{
"fieldName": "parent_child",
"SortOrder": 24,
"labelName": "Parentage",
"defaultValue": "",
"columnIndex": 23
}, {
"fieldName": "relationship_type",
"SortOrder": 25,
"labelName": "Relationship Type",
"defaultValue": "",
"columnIndex": 24
}, {
"fieldName": "parent_sku",
"SortOrder": 26,
"labelName": "Parent SKU",
"defaultValue": "",
"columnIndex": 25
}, {
"fieldName": "variation_theme",
"SortOrder": 27,
"labelName": "Variation Theme",
"defaultValue": "",
"columnIndex": 26
}]
}, {
"Group": "Basic",
"SortOrder": 4,
"BackgroundColor": "F8A45E",
"Fields": [{
"fieldName": "update_delete",
"SortOrder": 28,
"labelName": "Update Delete",
"defaultValue": "",
"columnIndex": 27
}, {
"fieldName": "product_description",
"SortOrder": 29,
"labelName": "Description",
"defaultValue": "",
"columnIndex": 28
}, {
"fieldName": "care_instructions",
"SortOrder": 30,
"labelName": "Care Instructions",
"defaultValue": "",
"columnIndex": 29
}, {
"fieldName": "target_gender",
"SortOrder": 31,
"labelName": "Target Gender",
"defaultValue": "",
"columnIndex": 30
}, {
"fieldName": "edition",
"SortOrder": 32,
"labelName": "Edition",
"defaultValue": "",
"columnIndex": 31
}]
}]
}
}'
--drop table #AmazonTemplateInfo
;with FieldInfo as (
select Template,[Group],GroupSort,BackgroundColor,FieldName,FieldSort,LabelName,DefaultValue,ColumnIndex from OPENJSON(#jstring,'$.FieldInfo')
With (
Template varchar(50)
,Groups nvarchar(max) as json
) as Template
cross apply openjson (Template.Groups)
with (
[Group] varchar(50)
,BackgroundColor varchar(6)
,GroupSort int '$.SortOrder'
,Fields nvarchar(max) as json
) Groups
cross apply openjson(Groups.Fields)
with (
fieldName varchar(50)
,FieldSort int '$.SortOrder'
,labelName varchar(50)
,defaultValue varchar(max)
,columnIndex int
) Fields
)
select * from FieldInfo
This generates a table in the form
Template
Group
GroupSort
BackgroundColor
FieldName
FieldSort
LabelName
DefaultValue
ColumnIndex
Babies
Required
1
FCD5B4
feed_product_type
1
Product Type
0
I need to create the same functionality in MariaDB v10. I've tried using JSON_EXTRACT and the LATERAL statement for my cross applies, but nothing is working. JSON_EXTRACT seems to just create a JSON string from the selected columns.
Is this pointless in MariaDB? Is there any way to do this?

MySQL has a feature that is close to what you're describing, it's the JSON_TABLE() function. See documentation and examples:
https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html
https://mysqlserverteam.com/json_table-the-best-of-both-worlds/
However, you said you're using "v10" which is a MariaDB version number, not a MySQL version number. MariaDB and MySQL are different software products. They had a common origin of source code in 2010, but they have been slowly diverging since then. In that way, it's a bit like comparing Microsoft SQL Server to Sybase.
No version of MariaDB at least up to 10.5 implements the JSON_TABLE() function. Perhaps some future version will.
https://mariadb.com/kb/en/json_table-support/

Well...finally. In case anyone is interested, this is how I parsed a multi-level JSON string in MariaDB:
set #jstring='{"FieldInfo": {"Template": "Babies","Groups": [{"Group": "Required","SortOrder": 1,"BackgroundColor": "FCD5B4","Fields": [{"fieldName": "feed_product_type","SortOrder": 1,"labelName": "Product Type","defaultValue": "","columnIndex": 0}, {"fieldName": "item_sku","SortOrder": 2,"labelName": "Seller SKU","defaultValue": "","columnIndex": 1}, {"fieldName": "brand_name","SortOrder": 3,"labelName": "Brand","defaultValue": "","columnIndex": 2}, {"fieldName": "item_name","SortOrder": 4,"labelName": "Title","defaultValue": "","columnIndex": 3}, {"fieldName": "external_product_id","SortOrder": 5,"labelName": "Product ID","defaultValue": "","columnIndex": 4}, {"fieldName": "external_product_id_type","SortOrder": 6,"labelName": "Product ID Type","defaultValue": "","columnIndex": 5}, {"fieldName": "item_type","SortOrder": 7,"labelName": "Item Type Keyword","defaultValue": "","columnIndex": 6}, {"fieldName": "model","SortOrder": 8,"labelName": "Model Number","defaultValue": "","columnIndex": 7}, {"fieldName": "manufacturer","SortOrder": 9,"labelName": "Manufacturer","defaultValue": "","columnIndex": 8}, {"fieldName": "part_number","SortOrder": 10,"labelName": "Part Number","defaultValue": "","columnIndex": 9}, {"fieldName": "mfg_minimum","SortOrder": 11,"labelName": "Minimum Manufacturer Age Recommended","defaultValue": "","columnIndex": 10}, {"fieldName": "standard_price","SortOrder": 12,"labelName": "Standard Price","defaultValue": "","columnIndex": 11}, {"fieldName": "quantity","SortOrder": 13,"labelName": "Quantity","defaultValue": "","columnIndex": 12}, {"fieldName": "main_image_url","SortOrder": 14,"labelName": "Main Image URL","defaultValue": "","columnIndex": 13}]}, {"Group": "Images","SortOrder": 2,"BackgroundColor": "FFFF00","Fields": [{"fieldName": "other_image_url1","SortOrder": 15,"labelName": "Other Image URL1","defaultValue": "","columnIndex": 14}, {"fieldName": "other_image_url2","SortOrder": 16,"labelName": "Other Image URL2","defaultValue": "","columnIndex": 15}, {"fieldName": "other_image_url3","SortOrder": 17,"labelName": "Other Image URL3","defaultValue": "","columnIndex": 16}, {"fieldName": "other_image_url4","SortOrder": 18,"labelName": "Other Image URL4","defaultValue": "","columnIndex": 17}, {"fieldName": "other_image_url5","SortOrder": 19,"labelName": "Other Image URL5","defaultValue": "","columnIndex": 18}, {"fieldName": "other_image_url6","SortOrder": 20,"labelName": "Other Image URL6","defaultValue": "","columnIndex": 19}, {"fieldName": "other_image_url7","SortOrder": 21,"labelName": "Other Image URL7","defaultValue": "","columnIndex": 20}, {"fieldName": "other_image_url8","SortOrder": 22,"labelName": "Other Image URL8","defaultValue": "","columnIndex": 21}, {"fieldName": "swatch_image_url","SortOrder": 23,"labelName": "Swatch Image URL","defaultValue": "","columnIndex": 22}]}, {"Group": "Variation","SortOrder": 3,"BackgroundColor": "FF8080","Fields": [{"fieldName": "parent_child","SortOrder": 24,"labelName": "Parentage","defaultValue": "","columnIndex": 23}, {"fieldName": "relationship_type","SortOrder": 25,"labelName": "Relationship Type","defaultValue": "","columnIndex": 24}, {"fieldName": "parent_sku","SortOrder": 26,"labelName": "Parent SKU","defaultValue": "","columnIndex": 25}, {"fieldName": "variation_theme","SortOrder": 27,"labelName": "Variation Theme","defaultValue": "","columnIndex": 26}]}, {"Group": "Basic","SortOrder": 4,"BackgroundColor": "F8A45E","Fields": [{"fieldName": "update_delete","SortOrder": 28,"labelName": "Update Delete","defaultValue": "","columnIndex": 27}, {"fieldName": "product_description","SortOrder": 29,"labelName": "Description","defaultValue": "","columnIndex": 28}, {"fieldName": "care_instructions","SortOrder": 30,"labelName": "Care Instructions","defaultValue": "","columnIndex": 29}, {"fieldName": "target_gender","SortOrder": 31,"labelName": "Target Gender","defaultValue": "","columnIndex": 30}, {"fieldName": "edition","SortOrder": 32,"labelName": "Edition","defaultValue": "","columnIndex": 31}]}]}}'
;with t1 as (
select JSON_Value(#jstring,'$.FieldInfo.Template') as Template, JSON_Query(#jstring,'$.FieldInfo.Groups') as jStringGroups
)
SELECT Template,jstringGroups into #Template, #jStringGroups from t1;
set #numGroups=JSON_Length(#jStringGroups,'$');
SET max_recursive_iterations=100000;
;with recursive t2 as (
select 0 curLev
, #Template Template
, #jStringGroups jStringGroups
, JSON_Value(#jStringGroups,'$[0].Group') 'Group'
,cast(JSON_Value(#jStringGroups,'$[0].SortOrder') as int) GroupSort
,JSON_Value(#jStringGroups,'$[0].BackgroundColor') BackgroundColor
,JSON_Query(#jStringGroups,'$[0].Fields') fieldString
,CAST(JSON_Length(JSON_Query(#jStringGroups,'$[0].Fields'),'$') as int) numFields
UNION
select t2.curLev+1
, t2.Template
, t2.jStringGroups
, JSON_Value(t2.jStringGroups,CONCAT('$[',t2.curLev+1,'].Group'))
, JSON_Value(t2.jStringGroups,CONCAT('$[',t2.curLev+1,'].SortOrder'))
, JSON_Value(t2.jStringGroups,CONCAT('$[',t2.curLev+1,'].BackgroundColor'))
, JSON_Query(t2.jStringGroups,CONCAT('$[',t2.curLev+1,'].Fields'))
, JSON_Length(JSON_Query(t2.jStringGroups,CONCAT('$[',t2.curLev+1,'].Fields')),'$')
from t2 where t2.curLev+1<#numGroups
),
t3 as (
select 0 curLev
,Template
,t2.Group
,GroupSort
,BackgroundColor
,fieldString
,numfields
,JSON_Value(t2.fieldString,'$[0].fieldName') fieldName
,cast(JSON_Value(t2.fieldString,'$[0].SortOrder') as int) fieldSort
,JSON_Value(t2.fieldString,'$[0].labelName') labelName
,JSON_Value(t2.fieldString,'$[0].defaultValue') defaultValue
,cast(JSON_Value(t2.fieldString,'$[0].columnIndex') as int) columnIndex
from t2
UNION
select t3.curLev+1
,t3.Template
,t3.Group
,t3.GroupSort
,t3.BackgroundColor
,t3.fieldString
,t3.numfields
,JSON_Value(t3.fieldString,CONCAT('$[',t3.curLev+1,'].fieldName'))
,JSON_Value(t3.fieldString,CONCAT('$[',t3.curLev+1,'].SortOrder'))
,JSON_Value(t3.fieldString,CONCAT('$[',t3.curLev+1,'].labelName'))
,JSON_Value(t3.fieldString,CONCAT('$[',t3.curLev+1,'].defaultValue'))
,JSON_Value(t3.fieldString,CONCAT('$[',t3.curLev+1,'].columnIndex'))
from t3
where t3.curLev+1<t3.numFields
)
select Template,t3.Group,GroupSort,FieldName,FieldSort,LabelName,DefaultValue,ColumnIndex from t3 order by GroupSort, fieldSort;
This got me my table.

Related

Account Query. Where does the query go? GET /v3/company/<clientID>/query?query=<selectStatement>&minorversion=59 Does it go in the <selectStatement>?

I am using QuickBooks Online API.
Documentation for the Account Object is here: https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/account
Sample Request URL:
GET /v3/company/<clientID>/query?query=<selectStatement>&minorversion=59
Content type:text/plain
Production Base URL:https://quickbooks.api.intuit.com
Sandbox Base URL:https://sandbox-quickbooks.api.intuit.com
Sample Query:
select * from Account where Metadata.CreateTime > '2014-12-31'
Sample Return:
{
"QueryResponse": {
"startPosition": 1,
"Account": [
{
"FullyQualifiedName": "Canadian Accounts Receivable",
"domain": "QBO",
"Name": "Canadian Accounts Receivable",
"Classification": "Asset",
"AccountSubType": "AccountsReceivable",
"CurrencyRef": {
"name": "United States Dollar",
"value": "USD"
},
"CurrentBalanceWithSubAccounts": 0,
"sparse": false,
"MetaData": {
"CreateTime": "2015-06-23T09:38:18-07:00",
"LastUpdatedTime": "2015-06-23T09:38:18-07:00"
},
"AccountType": "Accounts Receivable",
"CurrentBalance": 0,
"Active": true,
"SyncToken": "0",
"Id": "92",
"SubAccount": false
},
{
"FullyQualifiedName": "MyClients",
"domain": "QBO",
"Name": "MyClients",
"Classification": "Asset",
"AccountSubType": "AccountsReceivable",
"CurrencyRef": {
"name": "United States Dollar",
"value": "USD"
},
"CurrentBalanceWithSubAccounts": 0,
"sparse": false,
"MetaData": {
"CreateTime": "2015-07-13T12:34:47-07:00",
"LastUpdatedTime": "2015-07-13T12:34:47-07:00"
},
"AccountType": "Accounts Receivable",
"CurrentBalance": 0,
"Active": true,
"SyncToken": "0",
"Id": "93",
"SubAccount": false
},
{
"FullyQualifiedName": "MyJobs",
"domain": "QBO",
"Name": "MyJobs",
"Classification": "Asset",
"AccountSubType": "AccountsReceivable",
"CurrencyRef": {
"name": "United States Dollar",
"value": "USD"
},
"CurrentBalanceWithSubAccounts": 0,
"sparse": false,
"MetaData": {
"CreateTime": "2015-01-13T10:29:27-08:00",
"LastUpdatedTime": "2015-01-13T10:29:27-08:00"
},
"AccountType": "Accounts Receivable",
"CurrentBalance": 0,
"Active": true,
"SyncToken": "0",
"Id": "91",
"SubAccount": false
}
],
"maxResults": 3
},
"time": "2015-07-13T12:35:57.651-07:00"
}
In the Sample Request URL, I need to determine what to type into the "selectStatement".
I have found additional documentation here:
https://developer.intuit.com/app/developer/qbo/docs/develop/explore-the-quickbooks-online-api/data-queries
You have an example of what to put for selectStatement in your post:
select * from Account where Metadata.CreateTime > '2014-12-31'
e.g.:
/v3/company/<clientID>/query?query=select * from Account where Metadata.CreateTime > '2014-12-31'&minorversion=59

how to create a codable structure for the following API data

{
"status": true,
"error": false,
"data": [
{
"type": 0,
"title": "",
"description": "",
"data": [
{
"images": "/media/banner/Glocart%20Shop/Glocart-Shop_moVB3tP.jpg",
"is_external": true,
"link": "https://www.wikipedia.org/",
"product_id": null,
"offer_list_id": null,
"product_list_id": null
},
{
"images": "/media/banner/Grocery/Grocery_m341Az6.jpg",
"is_external": false,
"link": null,
"product_id": null,
"offer_list_id": 1,
"product_list_id": null
}
]
},
{
"type": 1,
"title": "Categories",
"description": "",
"data": [
{
"category_name": "Beverages",
"category_id": 5,
"category_icon": "/media/category_icon_upload_location/61ho157I07L._SX522__WpWCZ7h.jpg"
},
{
"category_name": "Tea & Coffee",
"category_id": 6,
"category_icon": "/media/category_icon_upload_location/Lipton-Green-Tea-Bags-250-SDL859816919-1-31762.jpg"
},
{
"category_name": "Body Care",
"category_id": 3,
"category_icon": "/media/category_icon_upload_location/nourishing-body-lotion-100ml_1024x1024.png"
},
{
"category_name": "Personal Hygiene",
"category_id": 7,
"category_icon": "/media/category_icon_upload_location/lif0259_2_1.jpg"
},
{
"category_name": "Jams & Preserves",
"category_id": 32,
"category_icon": "/media/category_icon_upload_location/kissan-mixed-fruit-jam-500-gm-164390973-ymzpr.jpg"
},
{
"category_name": "Air Fresheners",
"category_id": 28,
"category_icon": "/media/category_icon_upload_location/Godrej_Aer_Home_Air_Freshener_Spray_Morning_Misty_Meadows__fkCoOzL.jpg"
},
{
"category_name": "Chocolate",
"category_id": 35,
"category_icon": "/media/category_icon_upload_location/71p5hUVC0eL._SX569_.jpg"
},
{
"category_name": "Cosmetics & Makeup",
"category_id": 27,
"category_icon": "/media/category_icon_upload_location/81oA3GcTyRL._SL1500_.jpg"
},
{
"category_name": "Personal care",
"category_id": 26,
"category_icon": "/media/category_icon_upload_location/gillette-guard-3-card-blade-pack_rixer.png"
}
]
},
{
"type": 2,
"title": "Fresh Deals",
"description": "Today's Arrival",
"data": [
{
"product_id": 193,
"image_url": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"product_name": "UNILEVER KSN PINEAPPLE SQH 750ML-RS.15 PRICE OFF",
"offer_price": 14500,
"normal_price": 15500,
"is_in_cart": false,
"cart_qty": 0
},
{
"product_id": 193,
"image_url": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"product_name": "UNILEVER KSN PINEAPPLE SQH 750ML-RS.15 PRICE OFF",
"offer_price": 14500,
"normal_price": 15500,
"is_in_cart": false,
"cart_qty": 0
},
{
"product_id": 193,
"image_url": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"product_name": "UNILEVER KSN PINEAPPLE SQH 750ML-RS.15 PRICE OFF",
"offer_price": 14500,
"normal_price": 15500,
"is_in_cart": false,
"cart_qty": 0
}
]
},
{
"type": 3,
"title": "",
"description": "",
"data": [
{
"images": "/media/banner/Glocart%20Shop/Glocart-Shop_moVB3tP.jpg",
"is_external": true,
"link": "https://www.wikipedia.org/",
"product_id": null,
"offer_list_id": null,
"product_list_id": null
},
{
"images": "/media/banner/Glocart%20Shop/Glocart-Shop_moVB3tP.jpg",
"is_external": true,
"link": "https://www.wikipedia.org/",
"product_id": null,
"offer_list_id": null,
"product_list_id": null
}
]
},
{
"type": 4,
"title": "",
"description": "",
"data": [
{
"images": "/media/banner/Glocart%20Shop/Glocart-Shop_moVB3tP.jpg",
"is_external": true,
"link": null,
"product_id": null,
"offer_list_id": null,
"product_list_id": null
}
]
},
{
"type": 5,
"title": "Discount Range",
"description": "",
"data": [
{
"banner": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"discount_from": 50,
"discount_to": 60
},
{
"banner": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"discount_from": 40,
"discount_to": 100
}
]
},
{
"type": 2,
"title": "Top Deals",
"description": "Upto 45% discount",
"data": [
{
"product_id": 193,
"image_url": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"product_name": "UNILEVER KSN PINEAPPLE SQH 750ML-RS.15 PRICE OFF",
"offer_price": 14500,
"normal_price": 15500,
"is_in_cart": false,
"cart_qty": 0
},
{
"product_id": 193,
"image_url": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"product_name": "UNILEVER KSN PINEAPPLE SQH 750ML-RS.15 PRICE OFF",
"offer_price": 14500,
"normal_price": 15500,
"is_in_cart": false,
"cart_qty": 0
},
{
"product_id": 193,
"image_url": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"product_name": "UNILEVER KSN PINEAPPLE SQH 750ML-RS.15 PRICE OFF",
"offer_price": 14500,
"normal_price": 15500,
"is_in_cart": false,
"cart_qty": 0
}
]
},
{
"type": 0,
"title": "",
"description": "",
"data": {
"images": "/media/banner/Glocart%20Shop/Glocart-Shop_moVB3tP.jpg",
"is_external": true,
"link": "https://www.wikipedia.org/",
"product_id": null,
"offer_list_id": null,
"product_list_id": null
}
},
{
"type": 6,
"title": "Suggested for you",
"description": "",
"data": [
{
"product_id": 193,
"image_url": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"product_name": "UNILEVER KSN PINEAPPLE SQH 750ML-RS.15 PRICE OFF",
"offer_price": 14500,
"normal_price": 15500,
"is_in_cart": false,
"cart_qty": 0
},
{
"product_id": 193,
"image_url": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"product_name": "UNILEVER KSN PINEAPPLE SQH 750ML-RS.15 PRICE OFF",
"offer_price": 14500,
"normal_price": 15500,
"is_in_cart": false,
"cart_qty": 0
},
{
"product_id": 193,
"image_url": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"product_name": "UNILEVER KSN PINEAPPLE SQH 750ML-RS.15 PRICE OFF",
"offer_price": 14500,
"normal_price": 15500,
"is_in_cart": false,
"cart_qty": 0
}
]
},
{
"type": 0,
"title": "",
"description": "",
"data": {
"images": "/media/banner/Grocery/Grocery_m341Az6.jpg",
"is_external": false,
"link": null,
"product_id": null,
"offer_list_id": 1,
"product_list_id": null
}
},
{
"type": 7,
"title": "",
"description": "",
"data": [
{
"banner": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"category_name": "Personal Care",
"category_id": 1,
"have_sub_category": true,
"sub_categories": [
{
"category_name": "Chocolate",
"category_id": 35,
"category_icon": "/media/category_icon_upload_location/71p5hUVC0eL._SX569_.jpg"
},
{
"category_name": "Chocolate",
"category_id": 35,
"category_icon": "/media/category_icon_upload_location/71p5hUVC0eL._SX569_.jpg"
}
]
}
]
},
{
"type": 7,
"title": "",
"description": "",
"data": [
{
"banner": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"category_name": "Beauty Care",
"category_id": 2,
"have_sub_category": false,
"sub_categories": []
}
]
},
{
"type": 7,
"title": "",
"description": "",
"data": [
{
"banner": "/media/product_images/61XJQZV8YxL._SX425_.jpg",
"category_name": "Home Care",
"category_id": 3,
"have_sub_category": false,
"sub_categories": []
}
]
}
],
"cart_items_count": 5,
"total_cart_price": 30000,
"is_authenticated": true,
"new_notification_count": 1,
"cash_back_msg": "Dummy Cash Back message"
}
I have created one..but shows error
struct Home2Response: Codable {
var status, error: Bool?
var data: [Home2ResponseDatum]?
var cartItemsCount, totalCartPrice: Int?
var isAuthenticated: Bool?
var newNotificationCount: Int?
var cashBackMsg: String?
enum CodingKeys: String, CodingKey {
case status, error, data
case cartItemsCount = "cart_items_count"
case totalCartPrice = "total_cart_price"
case isAuthenticated = "is_authenticated"
case newNotificationCount = "new_notification_count"
case cashBackMsg = "cash_back_msg"
}
}
// MARK: - Home2ResponseDatum
struct Home2ResponseDatum: Codable {
var type: Int?
var title: String?
var datumDescription: String?
var data: [Any]?
enum CodingKeys: String, CodingKey {
case type, title
case datumDescription = "description"
case data
}
}
The name in JSON should match the one in your struct.
Change datumDescription into description.
struct Data : Codable {
let type : Int?
let title : String?
let description : String?
let data : [Data]?

How to add dashboard configuration json file in Grafana image?

I have a grafana docker image which have hawkular-datasource pre-configured using configuration files.
After after running grafana instance, I have a json given by teammate, which can be imported inside grafana and that json file creates dashboard when imported.
How do I make that dashboards appear by default in Grafana instance?
I tried copying the json file to /etc/grafana/provisioning/dashboards/ folder and created a new docker image. But when I run the image, the instance doesn't contain the dashboard at the homepage or anywhere in it.
How do I add this json file in docker image. Am I following the correct way?
I tried this http://docs.grafana.org/administration/provisioning/ But it didn't help out much. Any suggestion?
Here is the json file.
{
"id": null,
"title": "Openshift Metrics",
"tags": [],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"sharedCrosshair": false,
"rows": [
{
"collapse": false,
"editable": true,
"height": "322px",
"panels": [
{
"content": "<center><p style='font-size: 40pt'>$app</p></center>",
"editable": true,
"error": false,
"id": 23,
"isNew": true,
"links": [],
"mode": "html",
"repeatIteration": 1476706310439,
"scopedVars": {},
"span": 2,
"style": {
"font-size": "36pt"
},
"title": "",
"type": "text"
},
{
"aliasColors": {},
"bars": false,
"datasource": "Hawk-DS",
"editable": true,
"error": false,
"fill": 1,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 9,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"repeatIteration": 1476706310439,
"scopedVars": {},
"seriesOverrides": [],
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"queryBy": "tags",
"rate": false,
"refId": "A",
"seriesAggFn": "none",
"tags": [
{
"name": "container_name",
"value": "$app"
},
{
"name": "descriptor_name",
"value": "memory/usage"
}
],
"target": "select metric",
"tagsQL": "container_name IN [$app] AND descriptor_name='memory/usage'",
"timeAggFn": "avg",
"type": "gauge"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Memory usage",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"cacheTimeout": null,
"colorBackground": true,
"colorValue": false,
"colors": [
"rgba(50, 172, 45, 0.97)",
"rgba(237, 129, 40, 0.89)",
"rgba(245, 54, 54, 0.9)"
],
"datasource": "Hawk-DS",
"editable": true,
"error": false,
"format": "bytes",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"height": "100px",
"id": 12,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"repeatIteration": 1476706310439,
"scopedVars": {},
"span": 2,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"queryBy": "tags",
"rate": false,
"refId": "A",
"seriesAggFn": "sum",
"tags": [
{
"name": "container_name",
"value": "$app"
},
{
"name": "descriptor_name",
"value": "memory/usage"
}
],
"target": "select metric",
"tagsQL": "container_name IN [$app] AND descriptor_name='memory/usage'",
"timeAggFn": "live",
"type": "gauge"
}
],
"thresholds": "140000000,180000000",
"title": "Live, all pods",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": true,
"colorValue": false,
"colors": [
"rgba(50, 172, 45, 0.97)",
"rgba(237, 129, 40, 0.89)",
"rgba(245, 54, 54, 0.9)"
],
"datasource": "Hawk-DS",
"editable": true,
"error": false,
"format": "bytes",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"height": "100px",
"id": 15,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"repeatIteration": 1476706310439,
"scopedVars": {},
"span": 2,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"queryBy": "tags",
"rate": false,
"refId": "A",
"seriesAggFn": "avg",
"tags": [
{
"name": "container_name",
"value": "$app"
},
{
"name": "descriptor_name",
"value": "memory/usage"
}
],
"target": "select metric",
"tagsQL": "container_name IN [$app] AND descriptor_name='memory/usage'",
"timeAggFn": "live",
"type": "gauge"
}
],
"thresholds": "140000000,180000000",
"title": "Live per pod",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": true,
"colorValue": false,
"colors": [
"rgba(50, 172, 45, 0.97)",
"rgba(237, 129, 40, 0.89)",
"rgba(245, 54, 54, 0.9)"
],
"datasource": "Hawk-DS",
"editable": true,
"error": false,
"format": "bytes",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"height": "100px",
"id": 10,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"repeatIteration": 1476706310439,
"scopedVars": {},
"span": 2,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"queryBy": "tags",
"rate": false,
"refId": "A",
"seriesAggFn": "sum",
"tags": [
{
"name": "container_name",
"value": "$app"
},
{
"name": "descriptor_name",
"value": "memory/usage"
}
],
"target": "select metric",
"tagsQL": "container_name IN [$app] AND descriptor_name='memory/usage'",
"timeAggFn": "avg",
"type": "gauge"
}
],
"thresholds": "140000000,180000000",
"title": "Average, all pods",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": true,
"colorValue": false,
"colors": [
"rgba(50, 172, 45, 0.97)",
"rgba(237, 129, 40, 0.89)",
"rgba(245, 54, 54, 0.9)"
],
"datasource": "Hawk-DS",
"editable": true,
"error": false,
"format": "bytes",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"height": "100px",
"id": 13,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"repeatIteration": 1476706310439,
"scopedVars": {},
"span": 2,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"queryBy": "tags",
"rate": false,
"refId": "A",
"seriesAggFn": "avg",
"tags": [
{
"name": "container_name",
"value": "$app"
},
{
"name": "descriptor_name",
"value": "memory/usage"
}
],
"target": "select metric",
"tagsQL": "container_name IN [$app] AND descriptor_name='memory/usage'",
"timeAggFn": "avg",
"type": "gauge"
}
],
"thresholds": "140000000,180000000",
"title": "Average per pod",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": true,
"colorValue": false,
"colors": [
"rgba(50, 172, 45, 0.97)",
"rgba(237, 129, 40, 0.89)",
"rgba(245, 54, 54, 0.9)"
],
"datasource": "Hawk-DS",
"editable": true,
"error": false,
"format": "bytes",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"height": "100px",
"id": 11,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"repeatIteration": 1476706310439,
"scopedVars": {},
"span": 2,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"queryBy": "tags",
"rate": false,
"refId": "A",
"seriesAggFn": "sum",
"tags": [
{
"name": "container_name",
"value": "$app"
},
{
"name": "descriptor_name",
"value": "memory/usage"
}
],
"target": "select metric",
"tagsQL": "container_name IN [$app] AND descriptor_name='memory/usage'",
"timeAggFn": "max",
"type": "gauge"
}
],
"thresholds": "140000000,180000000",
"title": "Max, all pods",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": true,
"colorValue": false,
"colors": [
"rgba(50, 172, 45, 0.97)",
"rgba(237, 129, 40, 0.89)",
"rgba(245, 54, 54, 0.9)"
],
"datasource": "Hawk-DS",
"editable": true,
"error": false,
"format": "bytes",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"height": "100px",
"id": 14,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"repeatIteration": 1476706310439,
"scopedVars": {},
"span": 2,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"queryBy": "tags",
"rate": false,
"refId": "A",
"seriesAggFn": "avg",
"tags": [
{
"name": "container_name",
"value": "$app"
},
{
"name": "descriptor_name",
"value": "memory/usage"
}
],
"target": "select metric",
"tagsQL": "container_name IN [$app] AND descriptor_name='memory/usage'",
"timeAggFn": "max",
"type": "gauge"
}
],
"thresholds": "140000000,180000000",
"title": "Max per pod",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
}
],
"repeat": "app",
"scopedVars": {
"app": {
"text": "aloha",
"value": "aloha",
"selected": true
}
},
"title": "New row"
}
],
"time": {
"from": "now-30m",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"templating": {
"list": [
{
"current": {},
"datasource": "Hawk-DS",
"hide": 0,
"includeAll": true,
"label": "Application",
"multi": true,
"name": "app",
"options": [],
"query": "tags/container_name:*",
"refresh": 1,
"regex": "",
"type": "query"
}
]
},
"annotations": {
"list": []
},
"schemaVersion": 12,
"version": 32,
"links": [],
"gnetId": null
}
You should put a YAML file pointing to the JSON files in that folder. For example write /etc/grafana/provisioning/dashboards/local.yml:
apiVersion: 1
providers:
- name: 'default'
orgId: 1
folder: ''
type: file
disableDeletion: false
updateIntervalSeconds: 10 #how often Grafana will scan for changed dashboards
options:
path: /var/lib/grafana/dashboards
And then write your JSON file to /var/lib/grafana/dashboards/openshift.json.
Before Grafana 5, my previous solution was to wrap the whole Docker process in a script that uses the API to create the dashboard once the Docker container is up. You can use the GF_SECURITY_ADMIN_PASSWORD environment variable to set the password. You can also use GF_AUTH_ANONYMOUS_ENABLED, but you'll need to make sure it's not accessible to the outside world.
docker run -p 3000:3000 -e GF_AUTH_ANONYMOUS_ENABLED=true grafana/grafana ...
sleep 10 # wait for grafana to load (use a better method of waiting in production)
curl -skfS -XPOST --header "Content-Type: application/json" "http://localhost:3000/grafana/api/dashboards/db" --data-binary #dashboard.json
I've faced with the same problem and I hope the next solution could help somebody.
For example, you have the next working directory:
├── docker-compose.yml
├── grafana
│ ├── Dockerfile
│ ├── dashboards
│ │ └── exported_dashboard.json #dashboard you saved before
│ └── provisioning
│ │
│ ├── dashboards
│ │ └── all.yml
│ └── datasources
│ └── all.yml
In the Dockerfile you have to add provisioning and dashboards folders into grafana container:
FROM grafana/grafana:latest
ADD ./provisioning /etc/grafana/provisioning
ADD ./dashboards /var/lib/grafana/dashboards
In provisioning/dashboards/all.yml you have to set path to imported dashboards folder
where exported_dashboard.json is actually placed:
- name: 'default'
org_id: 1
folder: ''
type: 'file'
options:
folder: '/var/lib/grafana/dashboards'
[Optional] Then you also can configure default datasourse in provisioning/datasources/all.yml, prometheus, for example:
datasources:
- access: 'proxy'
editable: true
is_default: true
name: 'prom1'
org_id: 1
type: 'prometheus'
url: 'http://prometheus:9090'
version: 1
Finally, docker-compose.yml:
version: '3.8'
services:
grafana:
image: awesome-grafana:latest
build:
context: grafana
ports:
- "3000:3000"
environment:
GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH: "/var/lib/grafana/dashboards/exported_dashboard.json"
As for HOME dashborad, you can use the GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH evironment variable, as shown above.
More about GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH evironment variable you can read in that github issue.
The full comprehensive code example you can find in the next github repository
Original article with explanation there.

Swift 4 - How to structure a json object and using decodable in switch (not working)

I'm trying to create a structure for the following json object using swift decodable.
{
"template": [
{
"id": 8,
"question": "Favorite Color?",
"category": "Color",
"section": "Favorite Colors",
"is_active": 1,
},
[
{
"id": 14,
"question_id": 8,
"option_name": "Red",
"is_active": 1,
},
{
"id": 16,
"question_id": 8,
"option_name": "Orange",
"is_active": 1,
}
],
{
"id": 9,
"question": "What cars do you drive?",
"category": "Cars",
"section": "Favorite Cars",
"is_active": 1,
},
[
{
"id": 15,
"question_id": 9,
"option_name": "Toyota",
"is_active": 1,
},
{
"id": 18,
"question_id": 9,
"option_name": "Honda",
"is_active": 1,
},
{
"id": 19,
"question_id": 9,
"option_name": "BMW",
"is_active": 1,
}
]
]
}
I have some like:
public struct GameTemplate:Decodable {
question:String?
}
public struct Game:Decodable {
let template[GameTemplate]
}
For some reason when i tried to parse it doesn't work i get an error stating that struct is not a dictionary. I have tried casting the struct value but that didn't work either at this point just need to get a nice and clean json object after is decoded.
your JSON format is not consistent.
Just take the first category of color :
{
"template": [
{
"id": 8,
"question": "Favorite Color?",
"category": "Color",
"section": "Favorite Colors",
"is_active": 1,
},
[
{
"id": 14,
"question_id": 8,
"option_name": "Red",
"is_active": 1,
},
{
"id": 16,
"question_id": 8,
"option_name": "Orange",
"is_active": 1,
}
],
]
}
The template is an array having dictionary on 0 index and array on 1 index.
It is decodable in a different way but that's an extra effort.
If possible make the JSON data consistent and club categories in one index of array as :
{
"template": [
{
"id": 8,
"question": "Favorite Color?",
"category": "Color",
"section": "Favorite Colors",
"is_active": 1,
"subCategory": [
{
"id": 14,
"question_id": 8,
"option_name": "Red",
"is_active": 1,
},
{
"id": 16,
"question_id": 8,
"option_name": "Orange",
"is_active": 1,
}
]
}
]
}
and the same way club the different category of cars.
It will be easy for you to decode as:
public struct GameTemplate:Decodable {
question: String?
subCategory: [SubCategory]
}
public struct SubCategory:Decodable {
option_name: String?
}
public struct Game:Decodable {
let template: [GameTemplate]
}
Hope you get it what I am trying to explain.

Rails serialization of sibling objects into groups by name (active model serializers)

I'm not sure the best way of approaching this; I've got a generic system for requesting data objects from an api and returning json by using ActiveModelSerializers and it's been fantasticly simple.
For one class I need to return data formatted differently than the normal class serilization and unsure of the best approach. No matter what it seems that I'm going to have to have an 'elsif' for this one class in the return (which pains me a little bit).
Currently I've got a class named 'curve' which a user has_many of, and so requesting a user's curve gets me something like this:
[{
"id": 7,
"name": "A",
"angle": 30,
"date": "2017-05-23T01:52:00.589-04:00",
"direction": "left",
"top": "C1",
"bottom": "C4",
"risser": 3,
"sanders": 8
}, {
"id": 8,
"name": "B",
"angle": 0,
"date": "2017-05-23T01:52:56.107-04:00",
"direction": "right",
"top": "C5",
"bottom": "C6",
"risser": null,
"sanders": null
}, {
"id": 9,
"name": "A",
"angle": 22,
"date": "2017-05-25T01:56:00.656-04:00",
"direction": "right",
"top": "C3",
"bottom": "C5",
"risser": null,
"sanders": null
}, {
"id": 11,
"name": "C",
"angle": 3,
"date": "2017-05-26T01:57:08.078-04:00",
"direction": "right",
"top": "C4",
"bottom": "C7",
"risser": null,
"sanders": null
}]
But I actually need each to be grouped by the name like so:
[{
"name": "A",
"points": [{
"id": 7,
"name": "A",
"angle": 30,
"date": "2017-05-23T01:52:00.589-04:00",
"direction": "left",
"top": "C1",
"bottom": "C4",
"risser": 3,
"sanders": 8
}, {
"id": 9,
"name": "A",
"angle": 22,
"date": "2017-05-25T01:56:00.656-04:00",
"direction": "right",
"top": "C3",
"bottom": "C5",
"risser": null,
"sanders": null
}]
},
{
"name": "B",
"points": [{
"id": 8,
"name": "B",
"angle": 0,
"date": "2017-05-23T01:52:56.107-04:00",
"direction": "right",
"top": "C5",
"bottom": "C6",
"risser": null,
"sanders": null
}]
},
{
"name": "C",
"points": [{
"id": 11,
"name": "C",
"angle": 3,
"date": "2017-05-26T01:57:08.078-04:00",
"direction": "right",
"top": "C4",
"bottom": "C7",
"risser": null,
"sanders": null
}]
}
]
Now I know I can do group_by and fiddle with the response - but even then it won't be using the default Curve serializer. I could also create a custom GroupedCurve serializer and possibly process each curve with a CurveSerializer - but at that point isn't it just like getting the default array and doing a map, and constructing my own hashes?
What's the best / cleanest way of processing data from top to the bottom format?
UPDATE:
Something like the following 'does the job' but the job is dirty:
def self.reformat_curves(curves)
arr = []
curves.each do |i|
alpha = i[:name]
entry = arr.find{|chunk| chunk[:name]==alpha}
if entry.nil?
arr << {name: alpha, points: [i]}
else
entry[:points] << i
end
end
return arr
end

Resources