Parsing JSON Ansible returns undefined value - json

This is the JSONcontent I registered:
results:[{
"json": {
"item": {
"param": [
[
{
"metrics1": "A",
"metrics2": "B"
"metrics3": "C"
},
{
"metrics1": "D",
"metrics2": "E"
"metrics3": "F"
},
{
"metrics1": "G",
"metrics2": "H"
"metrics3": "I"
}
],
[
{
"metrics1": "J",
"metrics2": "K"
"metrics3": "L"
},
{
"metrics1": "M",
"metrics2": "N"
"metrics3": "O"
},
{
"metrics1": "P",
"metrics2": "Q"
"metrics3": "R"
}
],
],
}
}
}
]
I want to get metrics2 and metrics3 as a table where metrics2 value as column1 and metrics3 value as column2
I try parsing metrics2 first but it returns undefined value like this
"parseJSON_list": "[AnsibleUndefined, AnsibleUndefined, AnsibleUndefined]"
What I tried is:
parseJSON: "{{ JSONcontent['results'] | map(attribute='json') | map(attribute='item') | map(attribute='param') | map(attribute= 'metrics2') | list }}"
It works fine until map(attribute= 'metrics2')
What should I do?

Q: "Table where metrics2 value as column1 and metrics3 value as column2."
A: For example,
tab_m2_m3: "{{ param|json_query('[][].[metrics2, metrics3]') }}"
gives
tab_m2_m3:
- [B, C]
- [E, F]
- [H, I]
- [K, L]
- [N, O]
- [Q, R]
Example of a complete playbook
- hosts: localhost
vars:
param:
- - {metrics1: A, metrics2: B, metrics3: C}
- {metrics1: D, metrics2: E, metrics3: F}
- {metrics1: G, metrics2: H, metrics3: I}
- - {metrics1: J, metrics2: K, metrics3: L}
- {metrics1: M, metrics2: N, metrics3: O}
- {metrics1: P, metrics2: Q, metrics3: R}
tab_m2_m3: "{{ param|json_query('[][].[metrics2, metrics3]') }}"
tasks:
- debug:
var: tab_m2_m3

Related

Using jq to merge two arrays into one array of objects

I am very beginner into jq and I want to reshape my JSON file.
I have ve got JSON structured like this:
{
"a": [1, 2, 3, 4 ...],
"b": [
{
"x": 1000,
"value": 1
},
{
"x": 1000,
"value": 2
},
{
"x": 1000,
"value": 3
}
...
]
}
I am wondering how I can achieve result like this with jq:
[
{
"value": 1,
"from": "a",
},
{
"value": 2,
"from": "a"
},
...
{
"value": 1,
"from": "b"
},
{
"value": 2,
"from": "b"
}
...
]
Here is a very slightly generic, but hardly robust, solution:
map_values( if type == "array"
then map(if type == "object" then .value else . end)
else . end)
| [ keys_unsorted[] as $k
| .[$k][] as $v
| { value: $v, from: $k } ]
Create two lists, one from .a and one from .b, and merge them with +.
In the first list, create objects with value: set to the original content and add from: "a"; in the second list, remove .x from elements of .b and add the from again.
jq '[.a[] | {value:(.), from: "a"}] + [.b[] | del(.x) + {from: "b"}]

Conditional transform of JSON value based on key and type with jq

I have a json file where I'd like to transform the value of some named keys from numeric (0/1) to boolean (0=>false, 1=>true) using jq.
The transform should be re-entrant. If I run it on a (partially) transformed file, already converted keys should stay the same.
Given this json file:
{
"foo": {
"bar": {
"color": "green",
"visible": 0
},
"baz": {
"fruit": "banana",
"tasty": true
}
},
"fubar": {
"string": "hi",
"value": "world",
"enabled": 1,
"sub": {
"valuable": true,
"empty": false
}
},
"running": 1
}
and the following list of keys:
.foo.bar.visible
.foo.baz.tasty
.fubar.enabled
.fubar.sub.empty
.running
I'd like jq to transform the above into this:
{
"foo": {
"bar": {
"color": "green",
"visible": false
},
"baz": {
"fruit": "banana",
"tasty": true
}
},
"fubar": {
"string": "hi",
"value": "world",
"enabled": true,
"sub": {
"valuable": true,
"empty": false
}
},
"running": true
}
I've come up with this (using the first two keys only to keep it short):
cat in.json | jq '.foo.bar.visible = (if .foo.bar.visible | type == "boolean" then .foo.bar.visible elif .foo.bar.visible == 1 then true else false end) | .foo.baz.tasty = (if .foo.baz.tasty | type == "boolean" then .foo.baz.tasty elif .foo.baz.tasty == 1 then true else false end)' > out.json
but there has to be a better way?
Also tried to put it into a def but that didn't work:
def numerictobool(key):
$key = (if $key | type == "boolean" then $key elif $key == 1 then true else false end)
numerictobool(.network.eth0.enabled)
def numerictobool:
if type == "boolean" then . else . == 1 end;
.
| .foo.bar.visible |= numerictobool
| .foo.baz.tasty |= numerictobool
| .fubar.enabled |= numerictobool
| .fubar.sub.empty |= numerictobool
| .running |= numerictobool
...emits as output, given your input:
{
"foo": {
"bar": {
"color": "green",
"visible": false
},
"baz": {
"fruit": "banana",
"tasty": true
}
},
"fubar": {
"string": "hi",
"value": "world",
"enabled": true,
"sub": {
"valuable": true,
"empty": false
}
},
"running": true
}

jq update list elements based on values

Is it possible to use jq to turn the following JSON data
[
{
"a": null,
"b": [
{
"c": "cc",
"d": "dd1"
},
{
"c": "cc",
"d": "dd1",
"e": "ee",
"f": "ff"
}
]
},
{
"b": [
{
"c": "cc",
"d": "dd2",
"e": "ee",
"f": "ff"
}
]
}
]
into
[
{
"a": null,
"b": [
[
"cc", "d1"
],
[
"cc", "d1", "ff"
]
]
},
{
"b": [
[
"cc", "d2", "ff"
]
]
}
]
?
Note that the purpose is to reduce the b list with certain elements of its items based on a condition. The condition assigns the string d1 if the value of d is dd1, otherwise d2 is assigned if dd2 is present.
The following unsuccessful attempt demonstrates the idea:
$ jq -r '.[].b[] = [.[].b[].c, ?, .[].b[].f?]'
This is probably not the best way to do what you want, but it does get the desired output and it has the conditional you asked about.
jq '.[].b |= map(
[ .c,
(if .d == "dd1" then "d1" elif .d == "dd2" then "d2" else . end),
.f // empty
] )'
Without changing the value of the d key, you would use this jq filter:
jq '.[].b[]|=(to_entries|map(.value))' file
That updates the b array into all values of the inner objects.
If you want to update the d, you could use this filter:
jq '.[].b[] |= (to_entries|
map(
if(.key=="d") then
.value|=sub("d+";"d")
else .
end
|.value)
)' file
that adds a check if the key is d the associated value is updated to remove the duplicated d character from the string. This requires jq to support for regex (to be able to use the sub function).

jq recursively update values for certain elements

The intent for the JSON data below is to update the value of the field dst with the value of src within all elements of type t, regardless of depth within the tree, while at the same time preserving the whole structure of the data.
Is this possible with jq? My several attempts have boiled down to the following command that is not working to achieve the intended purpose:
$ jq -r 'map_values(select(.. | .type? == "t" |= (.dst = .src)))'
{
"a": "b",
"c": [
{
"type": "t",
"src": "xx",
"dst": "zz"
},
{
"type": "t",
"src": "xx",
"dst": "zz"
}
],
"d": [
{
"e": [
{
"type": "t",
"src": "xx",
"dst": "zz"
}
]
},
{
"type": "t2",
"src": "xx",
"dst": "zz"
}
]
}
Is this possible with jq?
jq is Turing-complete :-)
Here's a simple solution:
walk( if type == "object" and .type == "t" then .dst = .src else . end)
If your jq does not have walk/1, then it might be a good time to upgrade (to jq 1.6); otherwise, you can snarf its def from the web, e.g. by googling: jq "def walk"
Alternatively ...
reduce paths as $x (.;
if (getpath($x)|.type? // false) == "t"
then setpath( $x + ["dst"]; getpath( $x + ["src"] ))
else . end)

Create struct for complex JSON array in Golang

I have the following JSON array that I'm trying to convert to a struct.
[
{
"titel": "test 1",
"event": "some value",
"pair": "some value",
"condition": [
"or",
[
"contains",
"url",
"/"
]
],
"actions": [
[
"option1",
"12",
"1"
],
[
"option2",
"3",
"1"
]
]
}, {
"titel": "test 2",
"event": "some value",
"pair": "some value",
"condition": [
"or",
[
"contains",
"url",
"/"
]
],
"actions": [
[
"option1",
"12",
"1"
],
[
"option2",
"3",
"1"
]
]
}
]
This is the struct I've got so far:
type Trigger struct {
Event string `json:"event"`
Pair string `json:"pair"`
Actions [][]string `json:"actions"`
Condition []interface{} `json:"condition"`
}
type Triggers struct {
Collection []Trigger
}
However, this does not really cover the "Condition" part. Ideally id like to have a structure for that as well.
Assuming that there can be only a single condition per item in the root array, you can try a struct below. This could make using Condition clear.
https://play.golang.org/p/WxFhBjJmEN
type Trigger struct {
Event string `json:"event"`
Pair string `json:"pair"`
Actions [][]string `json:"actions"`
Condition Condition `json:"condition"`
}
type Condition []interface{}
func (c *Condition) Typ() string {
return (*c)[0].(string)
}
func (c *Condition) Val() []string {
xs := (*c)[1].([]interface{})
ys := make([]string, len(xs))
for i, x := range xs {
ys[i] = x.(string)
}
return ys
}
type Triggers struct {
Collection []Trigger
}