#!/usr/bin/env tcsh # # Author: Georg Voell - georg.voell@oracle.com # Version: @(#)convert-json 3.0.1 11.06.2020 (c)2020 Oracle # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ # #@ Reads a JSON file and converts it to a human readable file. #@ #@Usage: convert-json [options] [filename] #@ Options: #@ -h, --help : Displays helptext. #@ -v, --version : Displays the version of the script. #@ -n, --noheader : Don't write a header (needed for concatenating files) in output "text". #@ -s, --strict : Analyze JSON strictly. It takes more time but finds more errors in JSON. #@ -o, --output : Output format: can be "list" (default), "json", "text" or "table". #@ -f, --fields : Select the fields you want to convert. Use a comma separated string for fields. #@ Filename: Optional - if not specified, read from stdin. #@ #@Examples: #@ get-platform --output json | convert-json #@ Converts JSON output from get-platform into a vertical list format. #@ get-platform --output json | convert-json --fields "pretty_name,machine,bit" --output table #@ pretty_name machine bit #@ ---------------------- ------- --- #@ macOS 10.14.6 (Mojave) x86_64 64 # # Exit codes: # 01: Unknown or wrong parameter. # 02: **jq** not found. This script needs **jq** to perform. # 03: JSON error. # 04: Key in JSON contains dash ("-") or blank. **jq** does not work here. # 05: JSON does not start with "{". # 06: JSON contains less then 2 lines. # 07: **jq** join error. # 99: User interrupt. # # See also: # **print-table**(1), **install-scripts**(1) # # Update history: # # V 1.0.0 17.10.2017 New version # V 2.0.0 22.06.2019 Some fixes # V 3.0.0 02.06.2020 Redesign (was an include) # V 3.0.1 11.06.2020 Using library # set script = ${0} # Name of this script set progstr = `basename "$script"` # Basename of the script set progdir = `dirname "$script"` # Dirname of the script source "${progdir}/lib.tcsh" # Include default tcsh library setenv PATH "$WORKPATH" # Set PATH to something useful # Preset set filename = '' set fieldsstr = '' # List of fields we have to convert from json set formatstr = 'list' # Vertical oriented list # Check parameters while ($#argv >= 1) set pname = ${1} switch ($1) case '-h': case '--help': set showhelp shift breaksw case '-v': case '--version': set showversion shift breaksw case '-n': case '--noheader': set noheader shift breaksw case '-s': case '--strict': set strict shift breaksw case '-o': case '--output': shift if ("$1" != "") then set formatstr = `echo $1 | tolower` # Check if we have a valid format if (("$formatstr" != "list") && ("$formatstr" != "json") && ("$formatstr" != "text") && ("$formatstr" != "table")) then set errstr = "Please use values 'list', 'json', 'text' or 'table' with option '$pname'." endif shift else set errstr = "Please specify a format ('text', 'json', 'table' or 'list') after parameter '$pname'." endif breaksw case '-f': case '--fields': shift if ("$1" != "") then set fieldsstr = `\echo $1 | sed 's| ||g' | sed 's|,| |g'` shift else set errstr = "Please select the fields you want to convert after parameter '$pname'." endif breaksw default: set paramck = `echo "\\$1" | grep '^\\-'` # Types don't begin with '-' if (("$filename" == "") && ("$paramck" == "")) then set filename = $1 else set errstr = "Unknown parameter '$1'." endif shift endsw end # Display help or error message if ($?showversion || $?showhelp || "$errstr" != "") then # Get the version of the script set commentvers = `head -n 5 $script | grep '^# Version: @(' | cut -d')' -f2-` set namestr = `echo "$commentvers" | cut -d' ' -f1` # Name from comment set cversion = `echo "$commentvers" | cut -d' ' -f2` # Version from comment set cright = `echo "$commentvers" | cut -d' ' -f4` # Copyright set cowner = `echo "$commentvers" | cut -d' ' -f5-` # Copyright owner if ($?showversion) then printf "%s\n" "$cversion" else # Build version string set versstr = "$namestr ${cversion}, ${cright} $cowner" printf "\n$versstr\n" grep '^#@' $script | sed 's|^#@||' printf "\n" # Display error message and exit with return code 1 if (! $?showhelp && "$errstr" != "") then set exitcode = 1 errormsg $exitcode "$errstr" endif endif # Exit with exitcode exit $exitcode endif # Check if jq is in PATH set jqinstalled = `filecheck -x jq` if ("$jqinstalled" == "") then set exitcode = 2 errormsg $exitcode "($progstr) Tool 'jq' not found in '$PATH'." exit $exitcode endif # Convert JSON if ("$filename" == "") then # Filename was not specified - read from stdin cat /dev/stdin | jq -M . >&! $scratchfile set stat = $status else cat "$filename" | jq -M . >&! $scratchfile set stat = $status endif # Check if the conversion succeeded if ($stat > 0) then set exitcode = 3 set errrsn = "`head -n 2 $scratchfile`" errormsg $exitcode "($progstr) Tool 'jq' detected an error in JSON file." "$errrsn" goto Cleanup endif # Check if we have a format we could work with set result = `filecheck -sl "$scratchfile"` if ( "$result" == "$scratchfile") then set head1 = "`head -n 1 $scratchfile`" set head2 = "`head -n 2 $scratchfile | tr -d '\n' | tr -s ' '`" # Check if this is an output from opc (result) or oci (data) tool - then we have to convert is first if ("$head2" == '{ "result": [' || "$head2" == '{ "data": [') then cat $scratchfile | jq -M .[][] >! ${scratchfile}.jq mv ${scratchfile}.jq $scratchfile set head1 = "`head -n 1 $scratchfile`" # else # # Check if this is an output from oci (response) tool - then we have to convert is first # if ("$head2" == '{ "data": {') then # cat $scratchfile | browse-json data --output json | jq -M .[] >! ${scratchfile}.jq # # cat $scratchfile | jq -M .[] | sed 's|^".*||' >! ${scratchfile}.jq # mv ${scratchfile}.jq $scratchfile # set head1 = "`head -n 1 $scratchfile`" # endif endif # Check if first line of JSON starts with '[' if ("$head1" == '[') then cat $scratchfile | jq -M .[] >! ${scratchfile}.jq mv ${scratchfile}.jq $scratchfile set head1 = "`head -n 1 $scratchfile`" endif # Convert all keys that have a "-" in it's name if ($?strict) then foreach line ("`cat $scratchfile`") set grepres = `\echo "$line" | grep ":"` if ("$grepres" != "") then set key = `\echo "$line" | cut -d':' -f1 | sed 's|"||g'` set value = `\echo "$line" | cut -d':' -f2-` set grepres = `\echo "$key" | grep " "` if ("$grepres" != "") then set exitcode = 4 echo "Tool '$progstr' detected a key '$key' that contains a blank.\n" endif endif end # Check if we got an error so far if ($exitcode > 0) then errormsg $exitcode "($progstr) Errors detected in JSON input." goto Cleanup endif endif # Check if first line of JSON starts with '{' if ("$head1" != '{') then set exitcode = 5 errormsg $exitcode "($progstr) JSON file does not start with '{' in firstline. Instead: '$head1'." goto Cleanup endif else set exitcode = 6 errormsg $exitcode "($progstr) JSON file has less then 2 lines." goto Cleanup endif if ("$formatstr" == "json") then cat $scratchfile else # If fieldsstr is empty, select all fields if ("$fieldsstr" == "") then set savedfield = "" foreach line ("`cat $scratchfile`") if ("$line" == '{') set havestart if ("$line" == '}') set havestop if ($?havestart && ! $?havestop) then # Check if we have to insert saved fieldname if ("$savedfield" != "") then if ("$line" == ' {' || "$line" == ' [') then set savedfield = "" else set field = `\echo "$line" | cut -d':' -f1 | sed 's|"||g'` set value = `\echo "$line" | cut -d':' -f2 | sed 's|"||g'` if ("$value" != '{' && "$value" != '[') then # Insert savedfield if ("$fieldsstr" == "") then set fieldsstr = "$savedfield" else set fieldsstr = "$fieldsstr $savedfield" endif endif set savedfield = "" endif endif # Only get values if they are not nested set grepres = `echo "$line" | grep '^ "'` if ("$grepres" != "") then # Only search for key pair values set grepres = `echo "$line" | grep ':'` if ("$grepres" != "") then set field = `\echo "$line" | cut -d':' -f1 | sed 's|"||g'` set value = `\echo "$line" | cut -d':' -f2 | sed 's|"||g'` if ("$field" != "" && "$value" != "") then if ("$value" == '{' || "$value" == '[') then set savedfield = "$field" else if ("$fieldsstr" == "") then set fieldsstr = "$field" else set fieldsstr = "$fieldsstr $field" endif endif endif endif endif endif end endif # echo "fieldsstr: '$fieldsstr'.\n" # exit set headerstr = "" foreach label ($fieldsstr) if ("$headerstr" == "") then set headerstr = "$label" \echo '{"'${label}'": ' >>! ${scratchfile}.jq else set headerstr = "$headerstr $label" \echo ', "'${label}'": ' >>! ${scratchfile}.jq endif echo '(if (."'$label'" | type) == "array" then (."'$label'" | join(", ")) elif (."'$label'" | type) == "object" then (."'$label'" | join(", ")) else (."'$label'" | tostring) end)' >>! ${scratchfile}.jq end echo '} | join("^")\n' >>! ${scratchfile}.jq if (! $?noheader) then echo "$headerstr\n" >! ${scratchfile}.text endif # | sed 's|\[\d128-\d255\]||g' cat $scratchfile | sed 's|[ ]null,| "None",|g' | sed 's|[ ]null| "None"|g' | sed 's|" *"|"None"|g' | sed 's|\[ *\]|"None"|g' | sed 's|{ *}|"None"|g' \ | sed 's|"false"|false|g' | sed 's|[ ]false| "No"|g' | sed 's|"true"|true|g' | sed 's|[ ]true| "Yes"|g' \ | sed 's| \([-.][0-9*]*\),| "\1",|g' | sed 's| \([-.][0-9*]*\)$| "\1"|g' | sed 's| \([0-9*]*\),$| "\1",|g' | sed 's| \([0-9*]*\)$| "\1"|g' \ | jq -f ${scratchfile}.jq | sed 's|"||g' | sed 's|\^| |g' >>! ${scratchfile}.text set stat = $status # Check if the conversion succeeded if ($stat > 0) then set exitcode = 7 errormsg $exitcode "($progstr) Joining of fields failed. You may don't want to select nested fields." "Tool 'jq' quit joining with errors" goto Cleanup endif if ("$formatstr" == "text") then cat ${scratchfile}.text else if ("$formatstr" == "table") then print-table ${scratchfile}.text else print-table --list ${scratchfile}.text endif endif endif # Normal exit goto Cleanup # Interrupt Catch: set exitcode = 99 printf "\n" # Print new line errormsg $exitcode "Script '$progstr' aborted by user." # Cleanup Cleanup: filecheck -rm ${scratchfile}.text filecheck -rm ${scratchfile}.jq filecheck -rm $scratchfile exit $exitcode