Combine and rekey multiple objects in jq - json

I'm starting with a JSON file that has multiple objects
{
"name": "foo",
"url": "https://zombo.com"
}
{
"name": "bar",
"url": "https://acme.com"
}
and I'm trying to combine those into a single object with the name attribute as the key:
{
"widgets": {
"foo": {
"url": "https://zombo.com"
},
"bar": {
"url": "https://acme.com"
}
}
}
I've been banging my head against this for a while and I think I'm fairly close with the following query:
{ widgets: (reduce . as $item ({}; . + {($item.name): {url: $item.url}})) }
However, this results in multiple objects and I'm running out of ideas.
jqplay available here: https://jqplay.org/s/SI7XEMb5l9.

I'd do it like this:
jq -s '{ widgets: map( { (.name): {url} } ) | add }'
-s (--slurp) combines the objects to an array of objects
map( { (.name): { url } } ) reshapes each object
add concatenates the array elements into a single object
and finally, I build an object with { widgets: ... }.

Use reduce with inputs:
jq -n '{ widgets : (reduce inputs as $p ({}; . + ($p | { (.name) : { url } }))) }' file

Related

iterate on key to get the contents of the key

IF I have json that looks like
{
"items": [
{
"name": "issue1",
"spec": {
"config": {
"setting1": abc,
"setting2": {
"name": "xyz"
}
},
"files": {
"name": "cde",
"path": "/home"
},
"program": {
"name": "apache"
}
}
}
]
}
and I want to have iteration at .items[0].spec where the key config,files,program's contents can be shown.. something like
config:
{
"setting1": abc,
"setting2": {
"name": "xyz"
}
}
files:
{
"name": "cde",
"path": "/home"
}
program:
{
"name": "apache"
}
and there might be things more or less than config/files/programs for each items.
I know that I can get the list of keys by jq -r '.items[0].spec| to_entries[].key' but not sure about formating the output and getting the contents of .items[].spec.xxxxx like above.
I also got very close by jq -r '{test: .items[0].spec | with_entries(select(.value != null)) }' but I want to seperate each item per key
Also this got me even closer! but how do you get the json syntax of the object to display uncompressed?
jq -r '.items[].spec | keys[] as $k | "\($k):\n \(.[$k])"'
The "," operator is the magic ingredient you seem to be looking for:
.items[].spec | keys[] as $k | $k, .[$k]

Trying to merge 2 JSON documents using JQ

I'm using JQ CLI to merge JSON from document to another. The issue I am facing is that I have select by the value of a property, rather than by a numeric array index
The first file contains a chunk of JSON jqtest.json:
{
"event": [
{
"listen": "test",
"script": {
"exec": [],
"type": "text/javascript"
}
}
]
}
The second file is where I want to merge the JSON into under "accounts" collection.json:
{
"item": [
{
"name": "accounts",
"item": [
{
"name": "Retrieves the collection of Account resources."
}
]
},
{
"name": "accounts mapped",
"item": [
{
"name": "Retrieves the collection of AccountMapped resources."
}
]
}
]
}
What i am trying to do is merge it under "accounts" and under "name": "Retrieves the collection of Account resources." I use the command:
jq -s '
.[0].event += .[1].item |
map(select(.name=="accounts")) |
.[].item
' jqtest.json collection.json
But when executed nothing is outputted. What am doing wrong with JQ or is there another tool i can use to accomplish this?
{
"item": [
{
"name": "accounts",
"item": [
{
"name": "Retrieves the collection of Account resources.",
"event": [
{
"listen": "test",
"script": {
"exec": [],
"type": "text/javascript"
}
}
]
},
{
"name": "accounts mapped",
"item": [
{
"name": "Retrieves the collection of AccountMapped resources."
}
]
}
]
}
]
}
To merge two objects, one can use obj1 + obj2. From this, it follows that obj1 += obj2 can be used to merge an object (obj2) into another existing object (obj1).
Maybe that's what you trying to use. If so, you were missing parens around the expression producing the object to merge into (causing the code to be misparsed), you have the operands to += backwards, you don't actually produce the correct objects on each side of += (or even objects at all), and you didn't narrow down your output (accidentally including jqtest in the output).
Fixed:
jq -s '
( .[1].item[] | select( .name == "accounts" ) | .item[] ) += .[0] | .[1]
' jqtest.json collection.json
Demo on jqplay
I find the following clearer (less mental overhead):
jq -s '
.[0] as $to_insert |
.[1] | ( .item[] | select( .name == "accounts" ) | .item[] ) += $to_insert
' jqtest.json collection.json
Demo
That said, I would avoid slurping in favour of --argfile.
jq --argfile to_insert jqtest.json '
( .item[] | select( .name == "accounts" ) | .item[] ) += $to_insert
' collection.json
Demo on jqplay

How to convert array of object into expected json key value object based on the path value

This is my sample input
Input
[
{
"label": "test1",
"value": 1,
"path": "data/testData/testDataLevel3/testDataLevel3_1/0/testDataLevel3_1_a2"
},
{
"label": "test2",
"value": 2,
"path": "data/testData/testDataLevel1/testDataLevel1_1"
}
]
This input needs to be converted like this using jq
Expected output:
{
"data": {
"testData": {
"testDataLevel1": { //object
"testDataLevel1_1": 2
},
"testDataLevel3": {
"testDataLevel3_1": [ //array
{
"testDataLevel3_1_a2": 1
}
]
}
}
}
}
The path will contain the array index as path, and sometimes the keys will be combined in the path as well
You need to convert each .path to a form setpath can understand. The rest is straightforward.
reduce .[] as {$path, $value} (null;
setpath($path / "/" | map(tonumber? // .); $value)
)
Online demo

How do I add dynamically generated arrays with jq?

I'm using jq 1.6 on Windows 7 and want to add a dynamically generated array to a json file.
That array doesn't yet exist in this file.
I've got the following JSON structure (reduced for reasons of clarity):
{
"policies": {
"SearchBar": "separate",
"SearchEngines": {
"PreventInstalls": false
}
}
}
I'd like to generate an array based on dynamic values and finally create the following output:
{
"policies": {
"SearchBar": "separate",
"SearchEngines": {
"PreventInstalls": false,
"Remove": [
"Twitter",
"Wikipedia (en)"
]
}
}
}
The Remove array's content is stored in a (cmd) %variable%.
I found that the line
jq -n --arg items "%variable%" "{ Remove: $items | split(\",\") }"
produces the array I want:
{
"Remove": [
"Twitter",
"Wikipedia (en)"
]
}
What is the best way to insert this array into the original file?
Given the string input string Twitter,Wikipedia (en), you can use jq to update the JSON data:
<file jq --arg i 'Twitter,Wikipedia (en)' '.policies.SearchEngines += ({ Remove: $i | split(",") })'
{
"policies": {
"SearchBar": "separate",
"SearchEngines": {
"PreventInstalls": false,
"Remove": [
"Twitter",
"Wikipedia (en)"
]
}
}
}

Parse JSON and JSON values with jq

I have an API that returns JSON - big blocks of it. Some of the key value pairs have more blocks of JSON as the value associated with a key. jq does a great job of parsing the main JSON levels. But I can't find a way to get it to 'recurse' into the values associated with the keys and pretty print them as well.
Here is the start of one of the JSON returns. Note it is only a small percent of the full return:
{
"code": 200,
"status": "OK",
"data": {
"PlayFabId": "xxxxxxx",
"InfoResultPayload": {
"AccountInfo": {
"PlayFabId": "xxxxxxxx",
"Created": "2018-03-22T19:23:29.018Z",
"TitleInfo": {
"Origination": "IOS",
"Created": "2018-03-22T19:23:29.033Z",
"LastLogin": "2018-03-22T19:23:29.033Z",
"FirstLogin": "2018-03-22T19:23:29.033Z",
"isBanned": false
},
"PrivateInfo": {},
"IosDeviceInfo": {
"IosDeviceId": "xxxxxxxxx"
}
},
"UserVirtualCurrency": {
"GT": 10,
"MB": 70
},
"UserVirtualCurrencyRechargeTimes": {},
"UserData": {},
"UserDataVersion": 15,
"UserReadOnlyData": {
"DataVersion": {
"Value": "6",
"LastUpdated": "2018-03-22T19:48:59.543Z",
"Permission": "Public"
},
"achievements": {
"Value": "[{\"id\":0,\"gamePack\":\"GAME.PACK.0.KK\",\"marblesAmount\":50,\"achievements\":[{\"id\":2,\"name\":\"Correct Round 4\",\"description\":\"Round 4 answered correctly\",\"maxValue\":10,\"increment\":1,\"currentValue\":3,\"valueUnit\":\"unit\",\"awardOnIncrement\":true,\"marbles\":10,\"image\":\"https://www.jamandcandy.com/kissinkuzzins/achievements/icons/sphinx\",\"SuccessKey\":[\"0_3_4_0\",\"0_5_4_0\",\"0_6_4_0\",\"0_7_4_0\",\"0_8_4_0\",\"0_9_4_0\",\"0_10_4_0\"],\"event\":\"Player_answered_round\",\"achieved\":false},{\"id\":0,\"name\":\"Complete
This was parsed using jq but as you can see when you get to the
"achievements": { "Vales": "[{\"id\":0,\"gamePack\":\"GAME.PACK.0.KK\",\"marblesAmount\":50,\
lq does no further parse the value at is also JSON.
Is there a filter I am missing to get it to parse the values as well as the higher level structure?
Is there a filter I am missing ...?
The filter you'll need is fromjson, but it should only be applied to the stringified JSON; consider therefore using |= as illustrated using your fragment:
echo '{"achievements": { "Vales": "[{\"id\":0,\"gamePack\":\"GAME.PACK.0.KK\",\"marblesAmount\":50}]"}}' |
jq '.achievements.Vales |= fromjson'
{
"achievements": {
"Vales": [
{
"id": 0,
"gamePack": "GAME.PACK.0.KK",
"marblesAmount": 50
}
]
}
}
recursively/1
If you want to apply fromjson recursively wherever possible, then recursively is your friend:
def recursively(f):
. as $in
| if type == "object" then
reduce keys[] as $key
( {}; . + { ($key): ($in[$key] | recursively(f) )} )
elif type == "array" then map( recursively(f) )
else try (f as $f | if $f == . then . else ($f | recursively(f)) end) catch $in
end;
This would be applied as follows:
recursively(fromjson)
Example
{a: ({b: "xyzzy"}) | tojson} | tojson
| recursively(fromjson)
yields:
{
"a": {
"b": "xyzzy"
}
}