skip to Main Content

I have a JSON structure like this:

{
    "misc": {
        "color": "blue",
        "id": "yyyy-12345",
        "birthdate": "2020-12-25",
        "tools": [
            {
                "type": "power",
                "name": "hammer"
            },
            {
                "type": "manual",
                "name": "grinder"
            }
        ]
    },
    "people": [
        {
            "name": "bob",
            "profession": "analyst",
            "clients": [
                {
                    "name": "alice",
                    "instance": "3",
                    "stringA": "prefix1/suffix-stuff"
                },
                {
                    "name": "richard",
                    "instance": "2",
                    "stringA": "prefix1/suffix-stuff-lkasjdlflsd",
                    "stringB": "prefix2/suffix-stuff-lllaksjllsdkf",
                    "stringC": "prefix2/suffix-stuff-jhfhklasldj"
                },
                {
                    "name": "melanie",
                    "instance": "0",
                    "stringA": "prefix1/suffix-stuff-lkalalajskljlklj",
                    "stringB": "prefix2/suffix-stuff-aljksdljsd"
                }
            ]
        },
        {
            "name": "sarah",
            "profession": "developer",
            "clients": [
                {
                    "name": "george",
                    "instance": "0",
                    "stringA": "prefix1/suffix-stuff-xx-klasjdlk",
                    "stringB": "prefix2/suffix-stuff-yy-lkajsldf",
                    "stringC": "prefix3/suffix-stuff-lkklkl-lkasjdl"
                },
                {
                    "name": "anna",
                    "instance": "4",
                    "stringA": "prefix1/suffix-stuff-aa-lkadslkjjj",
                    "stringB": "prefix3/suffix-stuff-bb-lkasdjfl",
                    "stringC": "prefix2/suffix-stuff-cc-lkasjdf"
                },
                {
                    "name": "michael",
                    "instance": "0",
                    "stringA": "prefix1/suffix-stuff-ttlkajlksdfsd",
                    "stringB": "prefix2/suffix-stuff-rrklajsdlf",
                    "stringC": "prefix3/suffix-stuff-lkljlkjxx"
                }
            ]
        }
    ]
}

This is obviously a contrived example but it exactly matches my JSON structure.

My goal is to modify values for "stringA" and "stringB" but only for "people" with name == "bob" and only for "clients" with "instance" != "0".

I’ve read dozens of stackoverflow posts and other google search results, and I’ve tried dozens (maybe 100’s) of variations of jq commands but no luck–not even close. I’ve tried ‘..’ and walk, and various recursive functions that others have written (written prior to the availability of ‘..’ and walk, I think). My difficulty seems to be related to needing the entire JSON printed out but with conditional changes based on .people[].name and .people[].clients[].instance.

I really have no idea how this works, I guess.

If I run the above JSON data through a command such as

jq --arg name bob --arg prefix newprefix 'some-mysterious-jq-statements'

I want the output to be this:

{
    "misc": {
        "color": "blue",
        "id": "yyyy-12345",
        "birthdate": "2020-12-25",
        "tools": [
            {
                "type": "power",
                "name": "hammer"
            },
            {
                "type": "manual",
                "name": "grinder"
            }
        ]
    },
    "people": [
        {
            "name": "bob",
            "profession": "analyst",
            "clients": [
                {
                    "name": "alice",
                    "instance": "3",
                    "stringA": "newprefix/suffix-stuff-xyz-aksdjf"
                },
                {
                    "name": "richard",
                    "instance": "2",
                    "stringA": "newprefix/suffix-stuff-lkasjdlflsd",
                    "stringB": "newprefix/suffix-stuff-lllaksjllsdkf",
                    "stringC": "prefix3/suffix-stuff-jhfhklasldj"
                },
                {
                    "name": "melanie",
                    "instance": "0",
                    "stringA": "prefix1/suffix-stuff-lkalalajskljlklj",
                    "stringB": "prefix2/suffix-stuff-aljksdljsd"
                }
            ]
        },
        {
            "name": "sarah",
            "profession": "developer",
            "clients": [
                {
                    "name": "george",
                    "instance": "0",
                    "stringA": "prefix1/suffix-stuff-xx-klasjdlk",
                    "stringB": "prefix2/suffix-stuff-yy-lkajsldf",
                    "stringC": "prefix3/suffix-stuff-lkklkl-lkasjdl"
                },
                {
                    "name": "anna",
                    "instance": "4",
                    "stringA": "prefix1/suffix-stuff-aa-lkadslkjjj",
                    "stringB": "prefix3/suffix-stuff-bb-lkasdjfl",
                    "stringC": "prefix2/suffix-stuff-cc-lkasjdf"
                },
                {
                    "name": "michael",
                    "instance": "0",
                    "stringA": "prefix1/suffix-stuff-ttlkajlksdfsd",
                    "stringB": "prefix2/suffix-stuff-rrklajsdlf",
                    "stringC": "prefix3/suffix-stuff-lkljlkjxx"
                }
            ]
        }
    ]
}

Notice that the person "bob" has had modifications to the values for stringA and stringB for two of his clients, "alice" and "richard". Nothing else has changed.

Thank you for your time and assistance! 🙂

2

Answers


  1. Traverse to the desired locations using select to filter (I added // empty to ignore fields producing null), then update |= to your desired value using sub for a regex substitution.

    jq --arg name bob --arg prefix newprefix '
      (.people[] | select(.name == $name).clients[]
      | select(.instance != "0") | (.stringA, .stringB) // empty)
      |= sub("^.*/"; $prefix + "/")
    '
    

    Demo

    Login or Signup to reply.
  2. The following has the advantage of being portable, e.g. it works with jaq as well as jq:

    .people |= map(
       if .name == $name
       then .clients |= map(
          if .instance != "0"
          then if .stringA then .stringA |= sub("prefix[^/]*"; $prefix) else . end
          | if .stringB then .stringB |= sub("prefix[^/]*"; $prefix) else . end
          else . end)
       else . end) 
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search