Qt: Multi Array (from JSON); get to specific child - json

I have the following json
(from Google Distance API)
{
"destination_addresses" : [ "Hannover, Deutschland" ],
"origin_addresses" : [ "Wolfsburg, Deutschland" ],
"rows" : [
{
"elements" : [
{
"distance" : {
"text" : "88,8 km",
"value" : 88790
},
"duration" : {
"text" : "58 Minuten",
"value" : 3473
},
"status" : "OK"
}
]
}
],
"status" : "OK"
}
And like to get the distance values ('text' and 'value')
When I iterate that QJsonArray like that:
QJsonDocument document = QJsonDocument::fromJson(bts);
QJsonObject jsonObject = document.object();
QJsonArray jsonArray = jsonObject["rows"].toArray();
foreach (const QJsonValue & value, jsonArray) {
QJsonObject obj = value.toObject();
foreach (const QJsonValue & v, obj["elements"].toArray()) {
QJsonObject obj2 = v.toObject();
qDebug() << obj2["distance"];
//returns [1]
qDebug() << obj2["distance"].toArray();
//returns [2]
}
}
I should be able to get the values.
However, instead I get this:
[1] =
QJsonValue(object, QJsonObject({"text":"88,8 km","value":88790}) )
[2] =
QJsonArray()
which seems to be empty.
I dont know why that is. Because I do it just like that when getting to the "elements" array from that Json.

About [1]:
obj2["distance"] is an object, not array, with fields "text" and "value", and you should do one more job:
QJsonObject obj2 = v.toObject();
QJsonObject finalObject = obj2["distance"].toObject();
QString text = finalObject["text"].toString();
int value = finalObject["value"].toInt();
or something like this.
[{}, {}, {}] - an array of objects
{"a":{}, "b":{},"c":{}} - object which contains 3 objects.

Related

Modifying JSON in Groovy (or JOLT)

I've a simple JSON look like:
{
"account_login" : "google#gmail.com",
"view_id" : 1868715,
"join_id" : "utm_campaign=toyota&utm_content=multiformat_sites&utm_medium=cpc&utm_source=facebook",
"start_date" : "2020-02-03",
"end_date" : "2020-08-30"
}
With following Groovy script (from this answer):
def content = """
{
"account_login" : "google#gmail.com",
"view_id" : 1868715,
"join_id" : "utm_campaign=toyota&utm_content=multiformat_sites&utm_medium=cpc&utm_source=facebook",
"start_date" : "2020-02-03",
"end_date" : "2020-08-30"
}
"""
def slurped = new JsonSlurper().parseText(content)
def builder = new JsonBuilder(slurped)
builder.content.join_id = builder.content.join_id.split("\\s*&\\s*") //# to array
.collectEntries{
//# convert each item to map entry
String[] utmMarks = it.trim().split("\\s*=\\s*")
utmMarks[0] = [
"utm_medium" : "ga:medium",
"utm_campaign" : "ga:campaign",
"utm_source" : "ga:source",
"utm_content" : "ga:adContent",
"utm_term" : "ga:keyword",
].get( utmMarks[0] )
utmMarks
}
.findAll{
k,v-> k && v!=null //# filter out empty/null keys
}
//builder.content.filters = ...
println(builder.toPrettyString())
I'll get:
{
"account_login": "google#gmail.com",
"view_id": 1868715,
"join_id": {
"ga:campaign": "toyota",
"ga:adContent": "multiformat_sites",
"ga:medium": "cpc",
"ga:source": "facebook"
},
"start_date": "2020-02-03",
"end_date": "2020-08-30"
}
I want to update this script (or write new) and add new property: array filters to modified json above. Expected output:
{
"account_login":"google#gmail.com",
"view_id":1868715,
"join_id":{
"ga:campaign":"toyota",
"ga:adContent":"multiformat_sites",
"ga:medium":"cpc",
"ga:source":"facebook"
},
"start_date":"2020-02-03",
"end_date":"2020-08-30",
"converted_utm_marks":"ga:campaign=toyota&ga:adContent=multiformat_sites&ga:medium=cpc&ga:source=facebook",
"filters":[
{
"dimensionName":"ga:medium",
"operator":"EXACT",
"expressions":[
"cpc"
]
},
{
"dimensionName":"ga:adContent",
"operator":"EXACT",
"expressions":[
"multiformat_sites"
]
},
{
"dimensionName":"ga:campaign",
"operator":"EXACT",
"expressions":[
"toyota"
]
},
{
"dimensionName":"ga:source",
"operator":"EXACT",
"expressions":[
"facebook"
]
}
]
}
But the problem is that the set of filters for each JSON will be different. This set depends directly on the join_id set. If JSON join_id will contain:
"join_id": {
"ga:campaign": "toyota",
"ga:keyword": "car"
}
filters array should be:
[
{
"dimensionName":"ga:campaign",
"operator":"EXACT",
"expressions":[
"toyota"
]
},
{
"dimensionName":"ga:keyword",
"operator":"EXACT",
"expressions":[
"car"
]
}
]
operator is always equals EXACT. Property dimensionName - is a join_id.propety name. Expressions is a join_id.property value. So, property filters based on join_id and I need to loop through join_id property and build filters array with described structure. How to achieve expected output? JOLT configuration appreciated also.
I can't even simple iterate through join_id map:
slurped.join_id.each { println "Key: $it.key = Value: $it.value" }
I got the error:
/home/jdoodle.groovy: 24: illegal colon after argument expression;
solution: a complex label expression before a colon must be parenthesized # line 24, column 28.
.collect { [it.ga:campaign] }
UPDATE
I found out how to build this array:
def array =
[
filters: slurped.join_id.collect {key, value ->
[
dimensionName: key,
operator: "EXACT",
expressions: [
value
]
]
}
]
Seems like i got it:
def slurped = new JsonSlurper().parseText(content)
def builder = new JsonBuilder(slurped)
builder.content.filters = builder.content.join_id.collect {key, value ->
[
dimensionName: key,
operator: "EXACT",
expressions: [
value
]
]
}
Are there any better solutions?
def slurped = new JsonSlurper().parseText(content)
def builder = new JsonBuilder(slurped)
builder.content.filters = builder.content.join_id.collect {key, value ->
[
dimensionName: key,
operator: "EXACT",
expressions: [
value
]
]
}

Splitting Json to multiple jsons in NIFI

I have the below json file which I want to split in NIFI
Input:
[ {
"id" : 123,
"ticket_id" : 345,
"events" : [ {
"id" : 3322,
"type" : "xyz"
}, {
"id" : 6675,
"type" : "abc",
"value" : "sample value",
"field_name" : "subject"
}, {
"id" : 9988,
"type" : "abc",
"value" : [ "text_file", "json_file" ],
"field_name" : "tags"
}]
}]
and my output should be 3 different jsons like below:
{
"id" : 123,
"ticket_id" : 345,
"events.id" :3322,
"events.type":xyz
}
{
"id" : 123,
"ticket_id" : 345,
"events.id" :6675,
"events.type":"abc",
"events.value": "sample value"
"events.field_name":"subject"
}
{
"id" : 123,
"ticket_id" : 345,
"events.id" :9988,
"events.type":"abc",
"events.value": "[ "text_file", "json_file" ]"
"events.field_name":"tags"
}
I want to know can we do it using splitjson? I mean can splitjson split the json based on the array of json objects present inside the json?
Please let me know if there is a way to achieve this.
If you want 3 different flow files, each containing one JSON object from the array, you should be able to do it with SplitJson using a JSONPath of $ and/or $.*
Using reduce function:
function split(json) {
return json.reduce((acc, item) => {
const events = item.events.map((evt) => {
const obj = {id: item.id, ticket_id: item.ticket_id};
for (const k in evt) {
obj[`events.${k}`] = evt[k];
}
return obj;
});
return [...acc, ...events];
}, []);
}
const input = [{"id":123,"ticket_id":345,"events":[{"id":3322,"type":"xyz"},{"id":6675,"type":"abc","value":"sample value","field_name":"subject"},{"id":9988,"type":"abc","value":["text_file","json_file"],"field_name":"tags"}]}];
const res = split(input);
console.log(res);

How to parse Json array object?

I am trying to parse a Json file in Qt application but i am stuck at a point.
I have a json file which contains arrays with properties. Now i have a Category class which has a items as member variable. Now i would like to parse the properties and create a unique category object and under this object i would like to add the properties.
I have achieved this by hardcoding:
auto vehicle = std::make_unique<Item>("Coordinates");
vehicle->addEntry(std::make_unique<PropertyItem>("TODO", "x", PropertyType::FLOAT, false, *vehicle.get()));
categories.emplace_back(std::move(vehicle));
void Model::loadItemsFromJson() {
// Vehicle
QJsonObject obj = loadJsonFile(":/jsonfiles/vehicle.json");
QJsonArray properties = obj["properties"].toArray();
/// No idea here how to achive
}
Should i change the Json for better handling or could this be achieved easily?
Thank you
--------------------------EDIT---------------------
Now my json looks like this:
{
"General": [{
"Address": "TODO",
"Readonly": false
},
],
"Coordinates": [{
"Address": "TODO",
"Readonly": false
}
]
]
}
and my implementation:
QJsonObject obj = loadJsonFile(":/jsonfiles/vehicle.json");
QVariantMap map = obj.toVariantMap();
for (auto& m : map.keys()) {
// How to create objects??
}
If you structure your JSON like your objects, e.g.
{
"Categories" : {
"General" : [{
"Address" : "TODO",
"Name" : "Name",
"Type" : "string",
"ReadOnly" : "true"
}, ...],
"World Coordinates" : [...]
}
Then you just parse out each CategoryItem and ScenarioPropertyItem
PropertyType toPropertyType(QJsonValue value); // forward declare
void ScenarioPropertiesModel::loadItemsFromJson() {
// Vehicle
QJsonObject obj = loadJsonFile(":/jsonfiles/vehicle.json")["Categories"].toObject();
for (auto & cat : obj)
{
auto category = std::make_unique<CategoryItem>(cat.name());
for (auto & prop : cat.value().toArray())
{
auto address = prop["Address"].toString();
auto name = prop["Name"].toString();
auto type = toPropertyType(prop["Type"]);
auto readonly = prop["Readonly"].toBool();
category->addEntry(std::make_unique<ScenarioPropertyItem>(address, name, type, readonly));
}
categories.emplace_back(std::move(category));
}
}
PropertyType toPropertyType(QJsonValue value)
{
static std::map<QString, PropertyType> propertyTypes =
{
{ "float", PropertyType::FLOAT },
{ "string", PropertyType::String },
// etc
}
return propertyTypes[value.toString()];
}

Adding a attribute to json file

I have a json file of the format
{"latitude":28.488069,"longitude":-81.407208,"data":[{"time":1462680000,"summary":"Clear"},{"time":1462683600,"summary":"Clear",},{"time":1462694400,"summary":"Clear"}]}}
I want to add id attribute inside data
val result = jsonfile
val Jsonobject = Json.parse(result).as[JsObject]
val res = Jsonobject ++ Json.obj("id" -> 1234)
println(Json.prettyPrint(res))
the output should be
{"latitude":28.488069,"longitude":-81.407208,"data":[{"time":1462680000,"summary":"Clear","id":"1234"},{"time":1462683600,"summary":"Clear","id":"1235"},{"time":1462694400,"summary":"Clear","id":"1236"}]}
but my output is
{"latitude":28.488069,"longitude":-81.407208,"data":[{"time":1462680000,"summary":"Clear"},{"time":1462683600,"summary":"Clear",},{"time":1462694400,"summary":"Clear"}]},"id":"1234"}
There are a couple of things missing in your code. In your expected output, the ID is incremented, how do you expect to just add "id" -> 1234 and have it automatically incremented ?
Anyway, you have to loop through the elements in data and set it for each one of them. Something like this works:
val j = Json.parse(result).as[JsObject]
val res = j ++ Json.obj("data" ->
// get the 'data' array and loop inside
(j \ "data").as[JsArray].value.zipWithIndex.map {
// zipWithIndex lets us use the index to increment the ID
case (x,i) => x.as[JsObject] + ("id" , JsString((1234 + i).toString)) })
println(Json.prettyPrint(res))
{
"latitude" : 28.488069,
"longitude" : -81.407208,
"data" : [ {
"time" : 1462680000,
"summary" : "Clear",
"id" : "1234"
}, {
"time" : 1462683600,
"summary" : "Clear",
"id" : "1235"
}, {
"time" : 1462694400,
"summary" : "Clear",
"id" : "1236"
} ]
}

How do I transform json using scala lift?

How do I transform the json below using scala lift based on a sibling attribute?
In the json below, I want to encode the value in "value" attribute if sibling attribute "type" is "html"
val json = """
{
"id" : "1B23423B",
"payload" : {
"list" : [ {
"name" : "test",
"data" : [ {
"value" : "Some html",
"type" : "html",
}, {
"value" : "some text",
"type" : "text"
}]
}]
}
}
"""
def encode(s:String):String = s + "encoded"
val newjson = js.transform {
case x if x == JField("type",JString("html")) => // somehow encode value??
}
println(newjson)
Here is on of the possible solutions:
1) first find data json with type html
2) transform json value child
val parsed = JsonParser.parse(jsonString)
def encode(s:String):String = s + "encoded"
private def encodeValue(dataObject: JValue) = dataObject.transform{
case JField("value", JString(value)) => JField("value", JString(encode(value)))
}
val newJson = parsed.transform({
case dataObject # JObject(fields) if fields.contains(JField("type", JString("html"))) => encodeValue(dataObject)
})