Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacing underscores in JSON using JQ

I'm working with the woocommerce API to retrieve and store information. Currently our setup is designed to use camel case instead of underscores. I'm using jq to process our information, but I'm curious how I can use the sub(regex, tostring) function to replace the underscores in my JSON with camelCase?

Here's an example of the code

"line_items": [
    {
     "id": xxxx,
     "name": "xxxx",
     "sku": "xxxx",
     "product_id": xxxx,
    }

For example, according to another answer on SO that I found, this works: curl https://www.testsite.com/wp-json/wc/v1/orders -u user:pass | jq '.[] | with_entries( if .key | contains("_") then .key |= sub("_";"") else . end)' and remove the underscores.

The result is:

"lineitems": [
    {
     "id": xxxx,
     "name": "xxxx",
     "sku": "xxxx",
     "productid": xxxx,
    }

However, when I try curl https://www.testsite.com/wp-json/wc/v1/orders -u user:pass | jq '.[] | with_entries( if .key | contains("_") then .key |= sub("(\\_)([a-z])";"$2\u") else . end)' I don't get the results I would expect.

The expected results would be:

"lineItems": [
    {
     "id": xxxx,
     "name": "xxxx",
     "sku": "xxxx",
     "productId": xxxx,
    }

I don't have a lot of experience using jq so I'm not sure what I'm doing wrong. Is there a better solution to this problem?

like image 275
chipoglesby Avatar asked Nov 01 '16 18:11

chipoglesby


2 Answers

Here's a jq function that will convert "a_bcd_ef" to "aBcdEf", which seems to be what you want:

def camel:
  gsub( "_(?<a>[a-z])"; .a|ascii_upcase);

Example usage:

"a_bcd_ef" | camel

If you want a simple one-liner to process JSON strings from STDIN:

$ jq 'gsub( "_(?<a>[a-z])"; .a|ascii_upcase)'

If you only want the first occurrence of "_[a-z]" converted, then of course you'd use sub. And so on.

To apply this function to ALL keys in an object, you could write:

with_entries( .key |= camel )

To change ALL keys in ALL objects within a JSON entity, you could use walk/1:

walk(if type == "object" then with_entries(.key |= camel) else . end)

If your jq does not have walk/1 then you can simply include its definition (easily found by googling), either before it is invoked, or perhaps in your ~/.jq file.

like image 194
peak Avatar answered Oct 11 '22 01:10

peak


Although not as concise as the gsub solution of @peak, this one is easier on the eye and easier for beginners to understand IMHO.

You can put this into a script called 'snake_to_camel.jq' and chmod +x snake_to_camel.jq

#!/usr/bin/env jq -f
def head:
  .[0:1];

def tail:
  .[1:];

def capitalize:
  (head | ascii_upcase) + tail;

def snake_to_camel:
  split("_") |
  head + (tail | map(capitalize)) |
  join("");

def map_keys(mapper):
  walk(if type == "object" then with_entries(.key |= mapper) else . end);

map_keys(snake_to_camel)

Example usage:

curl https://example.com/input.json | ./snake_to_camel.jq

Some of the jq features I've used here:

  • slices for head and tail
  • ascii_upcase
  • addition for array concatenation
  • split and join
  • walk
  • with_entries
like image 28
reegnz Avatar answered Oct 11 '22 01:10

reegnz