Split a JSON file with hierarchical data to multiple CSV files
Perl
Latest commit bf2a40a Jan 11, 2016 @jsnell Fix error reporting for paths nested inside a :table handler
Should use *, not the actual field name.

README.pod

NAME

json-to-multicsv.pl - Split a JSON file with hierarchical data to multiple CSV files

SYNOPSIS

json-to-multicsv.pl [ --path pathspec:handler ... ] [ --file input-file ] [ --table name ]

DESCRIPTION

Read in a JSON file, process it according as specified by the --path arguments, and output one or multiple CSV files with the same data in tabular format.

EXAMPLE

Assuming the following input file:

   {
       "item 1": {
           "title": "The First Item",
           "genres": ["sci-fi", "adventure"],
           "rating": {
               "mean": 9.5,
               "votes": 190
           }
       },
       "item 2": {
           "title": "The Second Item",
           "genres": ["history", "economics"],
           "rating": {
               "mean": 7.4,
               "votes": 865
           },
           "sales": [
               { "count": 76, "country": "us" },
               { "count": 13, "country": "de" },
               { "count": 4, "country": "fi" }
           ]
       }
   }

And the following command line flags:

   --path /:table:item
   --path /*/rating:column
   --path /*/sales:table:sales
   --path /*/genres:table:genres

You'd get the following output files, which can be joined together using the *._key fields.

item.csv:

   item._key,item.rating.mean,item.rating.votes,item.title
   "item 1",9.5,190,"The First Item"
   "item 2",7.4,865,"The Second Item"

item.genres.csv:

   genres,item._key,item.genres._key
   sci-fi,"item 1",1
   adventure,"item 1",2
   history,"item 2",1
   economics,"item 2",2

item.sales.csv:

   item._key,item.sales._key,sales.count,sales.country
   "item 2",1,76,us
   "item 2",2,13,de
   "item 2",3,4,fi

OPTIONS

--file input-file

Read the JSON input from input-file.

--path pathspec:table:name

Values matching pathspec should be used to open a new table, with the specified name. The value should be either an object or an array. For an object, each field of the object will be used to output a row in the CSV file corresponding to the new table. The name of the field stored in the tablename._key column. For an array, each element of the array will be used to output a row, with the index of the element (starting from 1) stored in the tablename._key column.

If multiple tables are nested, the key columns of all outer tables will be also emitted in the inner tables.

--path pathspec:column

Values matching pathspec should be used to emit one or more columns in the CSV file matching the innermost currently open table, on the currently open row. (If no table is currently open).

If the value is a scalar, that value will be output to a column named after the field containing the value as the column name. Note: Scalar values have an implicit column handler.

If the value is an object, each of the fields of the object will be used to to output a column with the name being based on both the name of that field, and the name of the field that contained the object.

--path pathspec:row

The values matching pathspec will be emitted as new rows. The value must be an object. The name of the field containing the value will be ignored. This is generally only useful for the toplevel JSON value.

--path pathspec:ignore

Values matching pathspec (and any of their subvalues) will not be processed at all.

--table name

Specifies the name of the toplevel table, assuming the toplevel JSON value is not used to define a table but row data. You will probably want to use a row handler for the toplevel element.

PATHS AND PATHSPECS

The path to a specific JSON value is determined by the following rules:

- The path of the root element is / - The path of a value that's directly contained inside an object is the concatenation of: a) the path of the parent object, b) the '/', c) the field in the object that this value is for. - The path of a value that's directly contained inside an array is the concatenation of: a) the path of the parent object, b) the '/', c) the 1-based index in the array of the value.

Paths are matched against with pathspecs. In a pathspec any of the elements of the path can instead be replaced with a *, which will match any element in that position (but not multiple adjacent ones). That is, the pathspec /a/*/c will match a/b/c but not a/b/b/c.

AUTHOR

Juho Snellman, <jsnell@iki.fi>

LICENSE

Standard MIT license