Fetching the array name when traversing array items in JSONata - json

I need to fetch the name of the array while traversing the child array items.
for example, if my input looks like
{"title": [
{
"value": "18724-100",
"locale": "en-GB"
},
{
"value": "18724-5",
"locale": "en-GB"
},
{
"value": "18724-99",
"locale": "fr-FR"
}
]}
I need output as
{
"data": [
{
"locale": "en-GB",
"metadata": [
{
"key": "title",
"value": "18724-100"
},
{
"key": "title",
"value": "18724-5"
}
]
},
{
"locale": "fr-FR",
"metadata": {
"key": "title",
"value": "18724-99"
}
}
]
}
I tried following spec in JSONata
{
"data": title{locale: value[]} ~> $each(function($v, $k) {
{
"locale": $k,
"metadata": $v.{"key": ???,"value": $}
}
})
}
Please help me to fill "???" so that I can get the array name

Assuming that the input object will always have a single root-level key you can write your expression like this:
{
"data": title{locale: value[]} ~> $each(function($v, $k) {
{
"locale": $k,
"metadata": $v.{"key": $keys($$)[0],"value": $}
}
})
}
$keys returns an array containing keys in the object. $keys($$) will return all keys in root-level of this array (in this case: "title").
Note that for a following input object:
{"title": [
{
"value": "18724-100",
"locale": "en-GB"
},
{
"value": "18724-5",
"locale": "en-GB"
},
{
"value": "18724-99",
"locale": "fr-FR"
}
],
"foo": 123
}
$keys($$) would return an array of two elements (["title", "foo"]).

Related

Merge Json Array Nodes and roll up a child element

I have a requirement to roll a collection of nodes that uses the current node name (within the collection) and for the value take each child nodes value (single node) into a string array, then use the parents key as the key.
Given.
{
"client": {
"addresses": [
{
"id": "27ef465ef60d2705",
"type": "RegisteredOfficeAddress"
},
{
"id": "b7affb035be3f984",
"type": "PlaceOfBusiness"
},
{
"id": "a8a3bef166141206",
"type": "EmailAddress"
}
],
"links": [
{
"id": "29a9de859e70799e",
"type": "Director",
"name": "Bob the Builder"
},
{
"id": "22493ad4c4fd8ac5",
"type": "Secretary",
"name": "Jennifer"
}
],
"Names": [
{
"id": "53977967eadfffcd",
"type": "EntityName",
"name": "Banjo"
}
]
}
}
from this the output needs to be
{
"client": {
"addresses": [
"RegisteredOfficeAddress",
"PlaceOfBusiness",
"EmailAddress"
],
"links": [
"Director",
"Secretary"
],
"Names": [
"EntityName"
]
}
}
What is the best way to achieve this? Any pointers to what/how to do this would be greatly appreciated.
Ron.
You can iterate over entries of your client object first with the help of the $each function, then get types for each of them, and combine via $merge:
{
"client": client
~> $each(function($list, $key) {{ $key: $list.type }})
~> $merge
}
Live playground: https://stedi.link/OpuRdE9

JQ: Delete key:value pairs anywhere in JSON tree

I am looking for a way to delete key:value pairs (where key is a known string) anywhere in the JSON tree with jq.
Specifically: I would like to delete "type": "Link" and "linkType": "Entry" everywhere (in all parts of the hierarchy, where ever they appear). My JSON file is +800000 rows long and is nested deeply.
Snippet:
{
"entries": [
{
"sys": {
"id": "vcLKKhJ3mZNfGMvVZZi07",
"contentType": {
"sys": {
"id": "page"
}
}
},
"fields": {
"title": {
"de-DE": "Startseite",
"en-US": "Home"
},
"description": {
"en-US": "foo"
},
"keywords": {
"en-US": "bar"
},
"stageModules": {
"en-US": [
{
"sys": {
"type": "Link",
"linkType": "Entry",
"id": "11AfBBuNK8bx3EygAS3WTY"
}
}
]
}
}
}
]
}
I've tried so many different things to iterate through the file to remove these two from the output, I constantly end up with "Cannot index array with string". E.g.
[ .[] | select(.linkType != "Entry", .type != "Link" ) ]
Ideal result:
{
"entries": [
{
"sys": {
"id": "vcLKKhJ3mZNfGMvVZZi07",
"contentType": {
"sys": {
"id": "page"
}
}
},
"fields": {
"title": {
"de-DE": "Startseite",
"en-US": "Home"
},
"description": {
"en-US": "foo"
},
"keywords": {
"en-US": "bar"
},
"stageModules": {
"en-US": [
{
"sys": {
"id": "11AfBBuNK8bx3EygAS3WTY"
}
}
]
}
}
}
]
}
Can someone help me out?
Thank you for your help.
From the jq manual:
The walk(f) function applies f recursively to every component of the
input entity.
Use this to recursively go through all items, check your conditions and use del if necessary:
walk(
if type == "object" and .type == "Link" then del(.type) else . end
)
Edit: If "matching is less important", as you state, then just use del on any object:
walk(if type == "object" then del(.type, .linkType) else . end)

How to parse an array of json in scala play framework?

I have an array of json objects like this
[
{
"events": [
{
"type": "message",
"attributes": [
{
"key": "action",
"value": "withdraw_reward"
},
{
"key": "sender",
"value": "bob"
},
{
"key": "module",
"value": "distribution"
},
{
"key": "sender",
"value": "bob"
}
]
},
{
"type": "credit",
"attributes": [
{
"key": "recipient",
"value": "ross"
},
{
"key": "sender",
"value": "bob"
},
{
"key": "amount",
"value": "100"
}
]
},
{
"type": "rewards",
"attributes": [
{
"key": "amount",
"value": "100"
},
{
"key": "validator",
"value": "sarah"
}
]
}
]
},
{
"events": [
{
"type": "message",
"attributes": [
{
"key": "action",
"value": "withdraw_reward"
},
{
"key": "sender",
"value": "bob"
},
{
"key": "module",
"value": "distribution"
},
{
"key": "sender",
"value": "bob"
}
]
},
{
"type": "credit",
"attributes": [
{
"key": "recipient",
"value": "ross"
},
{
"key": "sender",
"value": "bob"
},
{
"key": "amount",
"value": "100"
}
]
},
{
"type": "rewards",
"attributes": [
{
"key": "amount",
"value": "200"
},
{
"key": "validator",
"value": "Ryan"
}
]
}
]
}
]
How to traverse through the types, check if it's type equals to rewards and then go through the attributes and verify if the validator equals to sarah and fetch the value of the key amount? Pretty new to scala and play framework. Any help would be great. Thanks
You could parse your JSON into a structure of case classes for easier handling and then extract the wanted field like so:
val json =
"""[
{"events":[
{
"type":"message","attributes":[
{"key":"action","value":"withdraw_reward"},
{"key":"sender","value":"bob"},
{"key":"module","value":"distribution"},
{"key":"sender","value":"bob"}
]},
{
"type":"credit","attributes":[
{"key":"recipient","value":"ross"},
{"key":"sender","value":"bob"},
{"key":"amount","value":"100"}
]},
{
"type":"rewards","attributes":[
{"key":"amount","value":"100"},
{"key":"validator","value":"sara"}
]}
]
},
{"events":[
{
"type":"message","attributes":[
{"key":"action","value":"withdraw_reward"},
{"key":"sender","value":"bob"},
{"key":"module","value":"distribution"},
{"key":"sender","value":"bob"}
]},
{
"type":"credit","attributes":[
{"key":"recipient","value":"ross"},
{"key":"sender","value":"bob"},
{"key":"amount","value":"100"}
]},
{
"type":"rewards","attributes":[
{"key":"amount","value":"200"},
{"key":"validator","value":"Ryan"}
]}
]
}
]
"""
case class EventWrapper(events: Seq[Event])
case class KeyValue(key: String, value: String)
case class Event(`type`: String, attributes: Seq[KeyValue])
import play.api.libs.json._
implicit val kvReads: Reads[KeyValue] = Json.reads[KeyValue]
implicit val eventReads: Reads[Event] = Json.reads[Event]
implicit val eventWrapperReads: Reads[EventWrapper] = Json.reads[EventWrapper]
val rewardAmountsValidatedBySara = Json
.parse(json)
.as[Seq[EventWrapper]]
.flatMap {
_.events.collect {
case Event(t, attributes) if t == "rewards" && attributes.contains(KeyValue("validator", "sara")) =>
attributes.collect {
case KeyValue("amount", value) => value
}
}.flatten
}
val amount = rewardAmountsValidatedBySara.head
For your example, rewardAmountsValidatedBySara would yield a List of Strings containing only the String "100". Which you could retrieve (potentially unsafe) with .head as shown above.
Normally you would not do this, as it could throw an exception on an empty List, so it would be better to use .headOption which returns an Option which you can then handle safely.
Note that the implicit Reads are Macros, which automatically translate into Code, that instructs the Play Json Framework how to read the JsValue into the defined case classes, see the documentation for more info.

Read Array Value Using Dataweave in Mule

I am trying to use dataweave in Mule to read specific data values from an incoming payload. My sample payload looks like below:
{
"source": [
{
"uri": "entities/1R6xV",
"createdBy": "API_USER",
"createdTime": 1562504739146,
"attributes": {
"label": "000000000002659654",
"value": {
"Name": [
{
}
],
"Id": [
{
}
],
"Number": [
{
"type": "config/Types/Number/attributes/Number",
"ov": true,
"value": "000000000002659654",
"uri": "entities/1R6xV/attributes/Num/1ZtyT/Number/60pvN6"
}
]
}
}
}
]
}
If I need to read the "label", I can achieve that by
label: payload.source.attributes.label
Similarly, how can I read the "value" under attributes > Number. It doesn't work by:
Value: payload.source.attributes.Number.value
I am new to Dataweave. Please advise.
The problem is that the dot selector (.) works on object and on array of objects. When it is applied to an array it will apply the dot selector to all the elements of the array that are of type object and return that result.
Lets go part by part
payload.source
Returns
[
{
"uri": "entities/1R6xV",
"createdBy": "API_USER",
"createdTime": 1562504739146,
"attributes": {
"label": "000000000002659654",
"value": {
"Name": [
{
}
],
"Id": [
{
}
],
"Number": [
{
"type": "config/Types/Number/attributes/Number",
"ov": true,
"value": "000000000002659654",
"uri": "entities/1R6xV/attributes/Num/1ZtyT/Number/60pvN6"
}
]
}
}
}
]
So far so good as payload is an Object it returns the value of source that is an array
payload.source.attributes
Returns
[
{
"label": "000000000002659654",
"value": {
"Name": [
{
}
],
"Id": [
{
}
],
"Number": [
{
"type": "config/Types/Number/attributes/Number",
"ov": true,
"value": "000000000002659654",
"uri": "entities/1R6xV/attributes/Num/1ZtyT/Number/60pvN6"
}
]
}
}
]
Works ok because the result of payload.source was ended an Array of object so it will do that selection over those objects.
Now when you execute
payload.source.attributes.value.Number
It returns
[
[
{
"type": "config/Types/Number/attributes/Number",
"ov": true,
"value": "000000000002659654",
"uri": "entities/1R6xV/attributes/Num/1ZtyT/Number/60pvN6"
}
]
]
That is an array of arrays and here is where it is broken.
My Solution
You have two alternatives here
Use flatten function
flatten(payload.source.attributes.value.Number).value
Use descendant selector
payload.source.attributes.value.Number..value
Since Number is an array, you need to specify the index you want. In this case, the zeroth element:
Value: payload.source[0].attributes.value.Number[0].value
If you have multiple numbers, it would look something like this:
%dw 1.0
%output application/json
---
values: payload.source[0].attributes.value.Number map {
value: $.value
}

Filter only part of input using select

Given input like this:
{
"type": "collection",
"foo": "bar",
"children": [
{
"properties": {
"country": "GB"
},
"data": "..."
},
{
"properties": {
"country": "PL"
},
"data": "..."
}
]
}
How can I use jq to retain all of the JSON structure, but filter out some of the children using select(). For instance, If I wanted to return only children with country GB, I would expect the following output:
{
"type": "collection",
"foo": "bar",
"children": [
{
"properties": {
"country": "GB"
},
"data": "..."
}
]
}
If I only want the children, this is easy with .children[] | select(.properties.country == "GB"), but does not retain the rest of the JSON.
The key is to use |=. In the present case, you could use the following pattern:
.children |= map(select(...))