Dynamically Parse Child Nodes in JSON - json.net

I have a deserialized object that I want to dynamically loop through to return the related results. The response package looks like so:
{"RatingResponse":
{"Success":"true",
"Message":"",
"QuoteID":"57451",
"LoadNum":"57451",
"Rates":
{"Rate":
[
{"SCAC":"test1",
"CarrierName":"TEST1",
"TransitTime":"1",
"ServiceLevel":"D",
"TotalCost":"1,031.82",
"ThirdPartyCharge":"1,031.82",
"Accessorials":
{"Accessorial":
[
{"Code":"400",
"Cost":"1,655.55",
"Description":"Freight"
},
{"Code":"DSC",
"Cost":"-952.77",
"Description":"Discount"
},
{"Code":"FUE",
"Cost":"329.04",
"Description":"Fuel Surcharge"
}
]
},
"QuoteNumber":""
},
{"SCAC":"test2",
"CarrierName":"TEST2",
"TransitTime":"1",
"ServiceLevel":"D",
"TotalCost":"1,031.82",
"ThirdPartyCharge":"1,031.82",
"Accessorials":
{"Accessorial":
[
{"Code":"400",
"Cost":"1,655.55",
"Description":"Freight"
},
{"Code":"DSC",
"Cost":"-952.77",
"Description":"Discount"
},
{"Code":"FUE",
"Cost":"329.04",
"Description":"Fuel Surcharge"
}
]
},
"QuoteNumber":""
}
]
},
"AverageTotalCost":"1,031.82"
}
}
I have parsed the response data so that there is less information to work with, especially since I only need the Accessorial Costs. The parsed response looks like
[
{
"SCAC": "test1",
"CarrierName": "TEST1",
"TransitTime": "1",
"ServiceLevel": "D",
"TotalCost": "1,031.82",
"ThirdPartyCharge": "1,031.82",
"Accessorials": {
"Accessorial": [
{
"Code": "400",
"Cost": "1,655.55",
"Description": "Freight"
},
{
"Code": "DSC",
"Cost": "-952.77",
"Description": "Discount"
},
{
"Code": "FUE",
"Cost": "329.04",
"Description": "Fuel Surcharge"
}
]
},
"QuoteNumber": ""
},
{
"SCAC": "test2",
"CarrierName": "TEST2",
"TransitTime": "1",
"ServiceLevel": "D",
"TotalCost": "1,031.82",
"ThirdPartyCharge": "1,031.82",
"Accessorials": {
"Accessorial": [
{
"Code": "400",
"Cost": "1,655.55",
"Description": "Freight"
},
{
"Code": "DSC",
"Cost": "-952.77",
"Description": "Discount"
},
{
"Code": "FUE",
"Cost": "329.04",
"Description": "Fuel Surcharge"
}
]
},
"QuoteNumber": ""
}
]
The problem I am facing is that I will never know how many Rate items will come back in the response data, nor will I know the exact amount of Accessorial Costs. I'm hoping to capture the Rate child node counts and the Accessorial child node counts per Rate. Here's what I have so far.
Root rootObject = Newtonsoft.Json.JsonConvert.DeserializeObject<Root>(responseFromServer);
//rate stores the parsed response data
JArray rate = (JArray)JObject.Parse(responseFromServer)["RatingResponse"]["Rates"]["Rate"];
var rate2 = rate.ToString();
//this for loop works as expected. it grabs the number of Rate nodes (in this example, 2)
for (int i = 0; i < rate.Count(); i++)
{
dynamic test2 = rate[i];
//this is where I'm struggling
dynamic em = (JArray)JObject.Parse(test2)["Accessorials"]["Accessorial"].Count();
for (int j = 0; j < em; j++)
{
string test3 = test2.Accessorials.Accessorial[j].Cost;
System.IO.File.AppendAllText(logPath, Environment.NewLine + test3 + Environment.NewLine);
}
}
I apologize in advance for the bad formatting and odd variable names - I'm obviously still testing the functionality, so I've been using random variables.
Where I'm struggling (as notated above) is getting to the Accessorial node to count how many items are in its array. I was thinking I could parse the first array (starting with SCAC data) and extend down to the Accessorial node, but I'm not having any luck.
Any help is GREATLY appreciated, especially since I am new to this type of code and have spent the majority of the day trying to resolve this.

you can try this
var rates = (JArray)JObject.Parse(json)["RatingResponse"]["Rates"]["Rate"];
var costs = rates.Select(r => new
{
CarrierName = r["CarrierName"],
Costs = ((JArray)((JObject)r["Accessorials"])["Accessorial"])
.Where(r => (string)r["Description"] != "Discount")
.Select(r => (double)r["Cost"]).Sum()
}).ToList();
result
[
{
"CarrierName": "TEST1",
"Costs": 1984.59
},
{
"CarrierName": "TEST2",
"Costs": 1984.59
}
]

Related

Best way to retrieve document with nested JSON and limit

Suppose we have a structure:
{
"nested_items": [
{
"nested_sample0": "1",
"nested_sample1": "test",
"nested_sample2": "test",
"nested_sample3": {
"type": "type"
},
"nested_sample": null
},
{
"nested_sample0": "1",
"nested_sample1": "test",
"nested_sample2": "test",
"nested_sample3": {
"type": "type"
},
"nested_sample1": null
},
...
],
"sample1": 1233,
"id": "ed68ca34-6b59-4687-a557-bdefc9ec2f4b",
"sample2": "",
"sample3": "test",
"sample4": "test",
"_ts": 1656503348
}
I want to retrieve documents by id by with limit of "nested_items" field .As I know limit and offset not supported in sub queries. Any way to do this except of divide into two queries? Maybe some udf or else?
You can use the function ARRAY_SLICE assuming the array is ordered.
Example data:
{
"name": "John",
"details": [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
}
]
}
Example queries
-- First 2 items from nested array
SELECT c.name, ARRAY_SLICE(c.details, 0, 2) as details
FROM c
-- Last 2 items from nested array
SELECT c.name, ARRAY_SLICE(c.details, ARRAY_LENGTH(c.details) - 2, 2) as details
FROM c

aws glue job to import dynamodb data

We are trying to do DynamoDB migration from prod account to stage account.
In the source account, we are making use of "Export" feature of DDB to put the compressed .json.gz files into destination S3 bucket.
We have written a glue script which will read the exported .json.gz files and writes it to DDB table.
We are making the code generic, so we should be able to migrate any DDB table from prod to stage account.
As part of that process, while testing we are facing issues when we are trying to write a NUMBER SET data to target DDB table.
Following is the sample snippet which is raising ValidationException when trying to insert into DDB
from decimal import Decimal
def number_set(datavalue):
# datavalue will be ['0', '1']
set_of_values = set()
for value in datavalue:
set_of_values.add(Decimal(value))
return set_of_values
When running the code, we are getting following ValidationException
An error occurred while calling o82.pyWriteDynamicFrame. Supplied AttributeValue is empty, must contain exactly one of the supported datatypes (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException; Request ID: UKEU70T0BLIKN0K2OL4RU56TGVVV4KQNSO5AEMVJF66Q9ASUAAJG; Proxy: null)
However, if instead of Decimal(value) if we use int(value) then no ValidationException is being thrown and the job succeeds.
I feel that write_dynamic_frame_from_options will try to infer schema based on the values the element contains, if the element has "int" values then the datatype would be "NS", but if the element contains all "Decimal type" values, then it is not able to infer the datatype.
The glue job we have written is
dyf = glue_context.create_dynamic_frame_from_options(
connection_type="s3",
connection_options={
"paths": [file_path]
},
format="json",
transformation_ctx = "dyf",
recurse = True,
)
def number_set(datavalue):
list_of_values = []
for value in datavalue:
list_of_values.append(Decimal(value))
print("list of values ")
print(list_of_values)
return set(list_of_values)
def parse_list(datavalue):
list_of_values = []
for object in datavalue:
list_of_values.append(generic_conversion(object))
return list_of_values
def generic_conversion(value_dict):
for datatype,datavalue in value_dict.items():
if datatype == 'N':
value = Decimal(datavalue)
elif datatype == 'S':
value = datavalue
elif datatype == 'NS':
value = number_set(datavalue)
elif datatype == 'BOOL':
value = datavalue
elif datatype == 'M':
value = construct_map(datavalue)
elif datatype == 'B':
value = datavalue.encode('ascii')
elif datatype == 'L':
value = parse_list(datavalue)
return value
def construct_map(row_dict):
ddb_row = {}
for key,value_dict in row_dict.items():
# value is a dict with key as N or S
# if N then use Decimal type
ddb_row[key] = generic_conversion(value_dict)
return ddb_row
def map_function(rec):
row_dict = rec["Item"]
return construct_map(row_dict)
mapped_dyF = Map.apply(frame = dyf, f = map_function, transformation_ctx = "mapped_dyF")
datasink2 = glue_context.write_dynamic_frame_from_options(
frame=mapped_dyF,
connection_type="dynamodb",
connection_options={
"dynamodb.region": "us-east-1",
"dynamodb.output.tableName": destination_table,
"dynamodb.throughput.write.percent": "0.5"
},
transformation_ctx = "datasink2"
)
can anyone help us in how can we unblock from this situation?
Record that we are trying to insert
{
"region": {
"S": "to_delete"
},
"date": {
"N": "20210916"
},
"number_set": {
"NS": [
"0",
"1"
]
},
"test": {
"BOOL": false
},
"map": {
"M": {
"test": {
"S": "value"
},
"test2": {
"S": "value"
},
"nestedmap": {
"M": {
"key": {
"S": "value"
},
"nestedmap1": {
"M": {
"key1": {
"N": "0"
}
}
}
}
}
}
},
"binary": {
"B": "QUFBY2Q="
},
"list": {
"L": [
{
"S": "abc"
},
{
"S": "def"
},
{
"N": "123"
},
{
"M": {
"key2": {
"S": "value2"
},
"nestedmaplist": {
"M": {
"key3": {
"S": "value3"
}
}
}
}
}
]
}
}

How to get rid of the rows using sequelize

I am using sequelize.
const total = await models.parcel.findAndCountAll({ group: ['status'] });
And I got this response. But I want to only count not rows on response.
How can I make this? Thank you so much for reading my question.
{
"count": [
{
"status": null,
"count": 8
},
{
"status": "1",
"count": 5
},
{
"status": "2",
"count": 3
}
],
"rows": [
{
"id": 3,
"companyName": "hy",
"invoice": "904103",
"receiverName": "Rtrt",
},......
]
}
Just Change findAndCountAll to count:
await models.parcel.findAndCountAll({ group: ['status'] });
To :
await models.parcel.count({ group: ['status'] });

Is there an R function for getting the values that are on the brackets in a JSON file?

I'm trying to get some data in a JSON file using R, but it does not work when the data is under brackets and keys, I'm getting a lot of data, the problem is actually getting the value of the "released" parameter. example:
{
"index": [
{
"id": "a979eb2b85d6c13086b29a21bdc421b2673379a4",
"date": "2019-03-22T01:20:01-0300",
"status": "OK",
"sensor": [
{
"id": "15",
"number": 127,
"callback": {
"released": true #it is not possible to return this data
}
}
]
},
{
"id": "db2890f501a3a49ed74aeb065168e057c3fd51d2",
"date": "2019-03-25T01:20:01-0300",
"status": "NOK",
"sensor": [
{
"id": "15",
"number": 149,
"callback": {
"released": false #it is not possible to return this data
}
}
]
}
]
}
Follow the code:
library(jsonlite)
data <- fromJSON("Desktop/json/file.json")
pagination <- list()
for(i in 0:10){
pagination[[i+1]] <- data$index$sensor$callback
}
data_org <- rbind_pages(pagination)
nrow(data_org)
length <- nrow(data_org)
data_org[1:length, c("released")]
The response was being:
nrow(data_org)
# [1] 0
data_org[1:length, c("released")]
# NULL

How to insert into mongoDB from HTML page

var productDB = new Meteor.Collection('products'); //Want to insert into this DB
var ProductParameters = nodeDB.find({"ACTIVE" : 1, "VARIENTS.ACCESS" : "PUBLIC"}, { "VARIENTS.NAME": 1, _id : 0 } ); //Taking Paramters from Another DB
Template.dpVar.events = {
'click .addProduct' : function (e) {
e.preventDefault();
ProductParameters.forEach(function(){ **//This is my Question.How to insert into productDB the key values as {ProductParameters: Val of ProductParameters}**
console.log(ProductParameters);
var pvariable = {
pvariable: tmpl.find("#ProductParameters").value
};
productDB.insert(pvariable);
});
}
};
Problem:
I have created form from the Parameters of nodeDB.
I want to store the data from this new form in a new DB productDB.
I want to run a loop where all the ProductParameters are read from nodeDB and their corresponding values inserted in form by user are pushed into ProductDB as new Entry.
EDIT:
NodeDB has Templates:
db.nodes.insert([
{
"GEOLOCATION": {
"GEO_CODE": [],
"ACTIVE_GEOLOCATION": false
},
"META": {
"CATEGORY": "levis",
"DESCRIPTION": "dsad",
"PRIVACY": "PUBLIC",
"TEMPLATE_NAME": "B",
"TEMPLATE_GROUP": "Product",
"KEYWORDS": [
"sda"
],
"CREATEDBY": "",
"SUBCATEGORY": "Blue",
"PRODUCT_TEMPLATE_TYPE": "Consumable",
"UOM": "",
"TEMPLATE_SUBGROUP": ""
},
"VARIENTS": [
{
"COMMENT": "Demo",
"INDEX": 0,
"NAME": "Brand",
"IS_PARENT": false,
"DATATYPE": "Text",
"ACCESS": "PUBLIC",
"PARENT_VARIENT": "Parem",
"TYPE": "PERMANENT"
}
]
}
])
The form is generated only from the VARIENTS
The ProductDB would be {key,value} ={VARIENTS.NAME,value from UI}
There can be multiple VARIENTS as this contains only one "Brand"
instead of
var ProductParameters = nodeDB.find({"ACTIVE" : 1, "VARIENTS.ACCESS" : "PUBLIC"}, { "VARIENTS.NAME": 1, _id : 0 } );
add .fetch() at the end
var ProductParameters = nodeDB.find({"ACTIVE" : 1, "VARIENTS.ACCESS" : "PUBLIC"}, { "VARIENTS.NAME": 1, _id : 0 } ).fetch();

Resources