Flattening nested JSON objects with jq
I wanted to take a nested set of JSON objects and import them into a SQLite database using sqlite-utils insert - but I wanted to “flatten” some of the nested rows.
Example data:
{ "status": "success", "data": { "generated": "2021-02-18T20:14:02.288Z", "sites": [ { "id": "full_data", "name": "Moscone Center South (full data)", "active": true, "location": { "address": "747 Howard St, San Francisco, CA 94103", "url": "https://www.google.com/maps/place/Moscone+Center+South,+747+Howard+St,+San+Francisco,+CA+94103", "lng": -122.401253, "lat": 37.78392 }, "info": { "url": "https://sf.gov/location/moscone-center-south-covid-19-vaccine-site" }, "booking": { "url": "https://myturn.ca.gov", "dropins": false, "info": null }, "access": { "wheelchair": true, "languages": { "en": true, "es": true, "zh": true, "fil": false, "vi": false, "ru": false }, "remote_translation": { "available": false, "info": null } }, "access_mode": { "walk": true, "drive": false }, "open_to": { "everyone": true, "text": "Open to the public" }, "appointments": { "available": true, "last_updated": "2021-02-18T20:14:02.288Z" }, "eligibility": { "65_and_over": true, "healthcare_workers": true, "education_and_childcare": false, "agriculture_and_food": false, "emergency_services": false } } ] }}I wanted to turn this into an array of non-nested objects, like this:
[ { "id": "full_data", "name": "Moscone Center South (full data)", "active": true, "location_address": "747 Howard St, San Francisco, CA 94103", "location_url": "https://www.google.com/maps/place/Moscone+Center+South,+747+Howard+St,+San+Francisco,+CA+94103", "location_lng": -122.401253, "location_lat": 37.78392 }]Thanks to this StackOverflow answer I found the following jq fragment:
[leaf_paths as $path | { "key": $path | join("_"), "value": getpath($path)}] | from_entriesThis fragment transforms a nested JSON object into a flat one with "location_address" style keys instead.
I like trying these things out in interactive tools - https://www.jqkungfu.com/ is my current favourite, which runs the original jq in your browser compiled to WebAssembly.
I pasted in the example from above and then used this jq query to confirm that it works - the .data.sites[] | [ ... ] pattern here pulls out the ["data"]["sites"] array and applies the flatten transformation to every item within it:
.data.sites[] | [ [leaf_paths as $path | {"key": $path | join("_"), "value": getpath($path)}] | from_entries ]It worked!
The final, full recipe I used to pull down the JSON, transform and flatten it and insert it into a SQLite database was this:
curl 'https://vaccination-site-microservice.vercel.app/api/v1/sites' | \ jq .data.sites | jq ' [.[] | [leaf_paths as $path | {"key": $path | join("_"), "value": getpath($path)}] | from_entries] ' | \ sqlite-utils insert /tmp/sf.db sites -