monch1962
monch1962

Reputation: 5271

jq - parsing complex JSON into a string

I'm trying to use jq to parse JSON that looks something like:

{
    "username": "billy",
    "hero": {
        "something": "goeshere"
    },
    "bumper": {
        "bumper2": {
            "bumper3": "splodge"
        }
    },
    "morgan": [{
        "abc": 123
    }, 2, 4, {
        "def": 567
    }],
    "password": "issilly"
}

into

request.username == 'billy' && request.hero.something == 'goeshere' && request.bumper.bumper2.bumper3 == 'splodge' && request.morgan[0].abc == 123 && request.morgan[1] == 2 && request.morgan[2] == 4 && request.morgan[3].def == 567 && request.password == 'issilly'

So far I've got to

jq '. | to_entries[] | "request.\(.key) == '\(.value)'"'

which gets me part of the way there, but I can't work out how to "walk down" into the deep-nested elements, nor how to join the strings produced into a single line delimited with ' && '

Upvotes: 2

Views: 225

Answers (2)

oguz ismail
oguz ismail

Reputation: 50750

paths(scalars) outputs arrays representing paths to strings and numbers in ., and using getpath you can get values at those paths. So all you need is a function that converts path representations to path expressions, like:

def pr2pe:
  reduce .[] as $n ([];
    if $n|type == "string"
    then . + [$n]
    elif $n|type == "number" and length
    then .[-1] += "[\($n)]"
    else error("invalid path representation")
    end) | join(".");
[ paths(scalars) as $p
  | "\($p | pr2pe) == \(getpath($p) | tojson)" ]
| join(" && ")

See it on jqplay

Upvotes: 4

Amadan
Amadan

Reputation: 198294

This will give you output that is not quite what you posted, but should be good enough, given that JavaScript does not distinguish foo.bar and foo["bar"].

jq -r '
  . as $data |
  [path(.. | 
    select(type != "object" and type != "array")
  )] |
  map(
    . as $path |
    map("[" + (. | tojson) + "]") |
    join("") |
    "request\(.) = \($data | getpath($path) | tojson)"
  ) |
  join(" && ")
' < test.json

Output:

request["username"] = "billy" &&
request["hero"]["something"] = "goeshere" &&
request["bumper"]["bumper2"]["bumper3"] = "splodge" &&
request["morgan"][0]["abc"] = 123 &&
request["morgan"][1] = 2 &&
request["morgan"][2] = 4 &&
request["morgan"][3]["def"] = 567 &&
request["password"] = "issilly"

Explanation: .. gives us all values; but we want only the leaf values, so we filter out those that are arrays or objects. Then we get the array of paths using [path()]: this will be an array of arrays of path steps, like this: [["username"], ["hero", "something"], ["bumper", "bumper2", "bumper3"], ["morgan", 0, "abc"]... ] etc. Now for each path, we need to enclose each path step in brackets to get something that JavaScript can understand. tojson will give the exact thing that the brackets need, so that string keys of objects get quoted but the integral keys of arrays don't. We can now put together the equality expression: the left side needs a request prepended to the brackety path we constructed, while we can use getpath to get the value that should be on the right hand side (again using tojson to get the correct literal).

Upvotes: 2

Related Questions