NAME
JSON::Transform - arbitrary transformation of JSON-able data
SYNOPSIS
use
JSON::MaybeXS;
my
$transformer
= parse_transform(from_file(
$transformfile
));
to_file(
$outputfile
, encode_json
$transformer
->(decode_json
$json_input
));
DESCRIPTION
Implements a language concisely describing a set of transformations from an arbitrary JSON-able piece of data, to another one. The description language uses JSON Pointer (RFC 6901) for addressing. JSON-able means only strings, booleans, nulls (Perl undef
), numbers, array-refs, hash-refs, with no circular references.
A transformation is made up of an output expression, which can be composed of sub-expressions. The general concept is of expressions, and operations. Operations are generally "applied" to expressions, with the new value coming "from" the operation. That is why the <
character is used for "applying" syntax.
For instance, to transform an array of hashes that each have an id
key, to a hash mapping each id
to its hash:
# [ { "id": 1, "name": "Alice" }, { "id": 2, "name": "Bob" } ]
# ->
""
<@ {
"/$K/id"
:
$V
#`id` }
# ->
# { "1": { "name": "Alice" }, "2": { "name": "Bob" } }
While to do the reverse transformation:
""
<% [
$V
@`id`:
$K
]
The identity for an array:
""
<@ [
$V
]
The identity for an object/hash:
""
<% {
$K
:
$V
}
To get the keys of a hash:
""
<% [
$K
]
To get how many keys in a hash:
""
<%
$C
To get how many items in an array:
""
<@
$C
To move from one part of a structure to another:
"/destination"
<<
"/source"
To copy from one part of a structure to another:
"/destination"
<-
"/source"
To do the same with a transformation (assumes /source
is an array of hashes):
"/destination"
<-
"/source"
<@ [
$V
@`order`:
$K
]
To bind a variable, then replace the whole data structure:
$defs
<-
"/definitions"
""
<-
$defs
A slightly complex transformation, using the jt script:
$ cat
<<EOF | jt '"" <- "/Time Series (Daily)" <% [ .{ `date`: $K, `close`: $V<"/4. close" } ]'
{
"Meta Data": {},
"Time Series (Daily)": {
"2018-10-26": { "1. open": "", "4. close": "106.9600" },
"2018-10-25": { "1. open": "", "4. close": "108.3000" }
}
}
EOF
# produces:
[
{
"date"
:
"2018-10-25"
,
"close"
:
"108.3000"
},
{
"date"
:
"2018-10-26"
,
"close"
:
"106.9600"
}
]
Expression types
JSON pointers
JSON pointers are surrounded by ""
. JSON pointer syntax gives special meaning to the ~
character, as well as to /
. To quote a ~
, say ~0
. To quote a /
, say ~1
. Since a $
has special meaning, to use a literal one, quote it with a preceding \
.
The output type of a JSON pointer is whatever the pointed-at value is.
Transformations
A transformation has a destination, a transformation type operator, and a source-value expression. The destination can be a variable to bind to, or a JSON pointer.
If the source-value expression has a JSON-pointer source, then the destination can be omitted and the JSON-pointer source will be used.
The output type of the source-value expression can be anything.
Transformation operators
<-
-
Copying (including assignment for variable bindings)
<<
-
Moving - error if the source-value is other than a bare JSON pointer
Destination value expressions
These can be either a variable, or a JSON pointer.
Variables
These are expressed as $
followed by a lower-case letter, followed by zero or more letters.
Source value expressions
These can be either a single value including variables, of any type, or a mapping expression.
String value expressions
String value expressions can be surrounded by ``
. They have the same quoting rules as in JSON's "
-surrounded strings, including quoting of `
using \
. Any value inside, including variables, will be concatenated in the obvious way, and numbers will be coerced into strings (be careful of locale). Booleans and nulls will be stringified into [true]
, [false]
, [null]
.
Literal arrays
These are a single value of type array, expressed as surrounded by .[]
, with zero or more comma-separated single values.
Literal objects/hashes
These are a single value of type object/hash, expressed as surrounded by .{}
, with zero or more comma-separated colon pairs (see "Mapping to an object/hash", below).
Mapping expressions
A mapping expression has a source-value, a mapping operator, and a mapping description.
The mapping operator is either <@
, requiring the source-value to be of type array, or <%
, requiring type object/hash. If the input data pointed at by the source value expression is not the right type, this is an error.
The mapping description must be surrounded by either []
meaning return type array, or {}
for object/hash.
The description will be evaluated once for each input value. Within the brackets, $K
and $V
will have special meaning.
For an array input, each input will be each single array value, and $K
will be the zero-based array index.
For an object/hash input, each input will be each pair. $K
will be the object key being evaluated, of type string.
In either case, $V
will be the relevant value, of whatever type from the input. $C
will be of type integer, being the number of inputs.
Mapping to an object/hash
The return value will be of type object/hash, composed of a set of pairs, expressed within {}
as:
Mapping to an array
Within []
, the value expression will be an arbitrary value expression.
Single-value modifiers
A single value can have a modifier, followed by arguments.
@
The operand value must be of type object/hash. The argument must be a pair of string-value, :
, any-value. The return value will be the object/hash with that additional key/value pair.
#
The operand value must be of type object/hash. The argument must be a string-value. The return value will be the object/hash without that key.
<
The operand value must be of type object/hash or array. The argument must be a JSON pointer. The return value will be the value, but having had the JSON pointer applied.
Available system variables
$K
Available in mapping expressions. For each data pair, set to either the zero-based index in an array, or the string key of an object/hash.
$V
Available in mapping expressions. For each data pair, set to the value.
$C
Available in mapping expressions. Set to the integer number of values.
$E
Set to an object/hash which is the Perl %ENV
, i.e. the process environment.
Comments
Any --
sequence up to the end of that line will be a comment, and ignored.
DEBUGGING
To debug, set environment variable JSON_TRANSFORM_DEBUG
to a true value.
EXPORT
parse_transform
On error, throws an exception. On success, returns a function that can be called with JSON-able data, that will either throw an exception or return the transformed data.
Takes arguments:
SEE ALSO
RFC 6902 - JSON Patch - intended to change an existing structure, leaving it (largely) the same shape
AUTHOR
Ed J, <etj at cpan.org>
BUGS
Please report any bugs or feature requests on https://github.com/mohawk2/json-transform/issues.
Or, if you prefer email and/or RT: to bug-json-transform at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=JSON-Transform. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
LICENSE AND COPYRIGHT
Copyright 2018 Ed J.
This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at: