Skip to content

Latest commit

 

History

History

template

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Table of Contents

  1. Introduction
  2. Definition
  3. Examples
  4. Usage
  5. Playground

Introduction

JSON template is used to create target JSON from multiple JSONs (aka JSON pool).

JSON pool

JSON pool is a collection of JSONs, all JSONs in it have a unique name. So, we can use 'name' as key to retrieve JSON from it.

JSON template definition

JSON template must be a valid JSON. It uses 'Extended JSON Pointer' to reference the data source. If the value of one field is string, and the string's first byte is 0x2A('*', in memory of C), the value will be treated as 'Extented JSON Pointer', e.g., "*foo/a/c". If a plain string starts with 0x2A, it must be escaped with double-anti-slash, e.g., "*abc" -> "\\*abc".

Wildcard Extented JSON Pointer

In JSON template, I use '$' as wildcard in EJP to reference each item in array. So, if you have a key '$' in JSON, the key must be encoded in the EJP ($ -> ~2). If one EJP has one wildcard '$', I call it one dimensional EJP (1-D EJP), e.g., 'foo/a/$/c'. If it has N wildcard '$'s, I call it N dimensional EJP (N-D EJP).

Array naming

For the following multi-dimensional array:

               [             [      ...       [ ... ]     ...        ]             ]
               ^             ^                ^
               |             |                |
            1st array     2nd array        Nth array

Iterating wildcard EJP

For 1-D EJP, e.g., 'foo/a/$/x', iterating the EJP means replace the '$' to 0, 1, 2 ... to get the data from source JSON, until no data can be referenced, for example, given JSON 'foo':

{
    "a":[{"x":0},{"x":1}]
}

First, replace '$' to 0, 'foo/a/$/x' -> 'foo/a/0/x', to get data 0 from foo, then, replace '$' to 1, 'foo/a/$/x' -> 'foo/a/1/x', to get data 1 from foo, finaly, replace '$' to 2, 'foo/a/$/x' -> 'foo/a/2/x', but no data can be referenced from foo.

For 2-D EJP, e.g., 'foo/a/$/$/x', iterating the EJP means replace the '$'s to (0,0), (0,1), (0,2) ... (N,M) to get the data from JSON, until no data can be referenced.

For N-D EJP, iterating the EJP means replace the '$'s to (0,0,...,0), ... (A1,A2,...,AN) to get the data from JSON, until no data can be referenced.

Algorithm to generate JSON

  • Rule 1. If template field is plain value, just copy the data to the new JSON.
  • Rule 2. If template field is EJP with no wildcard, just copy the referenced data to the new JSON.
  • Rule 3. If template field is an array which length is greater than 1, after executing the template, the result array length is equal to the template field. If the length is equal to 1, after execute the template, the result array length is determined by the data source.
  • Rule 4. If template field is an array, and the array has only one item (the item can be object, array which length is great than 1, wildcard EJP), and the item has N dimensional EJP (N>=1), then the array is iterable, the target JSON array will be generated by iterating the wildcard EJP.
  • Rule 5. If template field is length-1 array, and the item in the array is length-1 sub-array, the array is iterable if sub-array has 2-D EJP. And so on, from 1st array to Nth array, which are all have one item, the 1st array is iterable if the Nth array has N-D EJP. If Nth array only have (N-1)-D EJP, the 1st array is not iterable, the length of the 1st array will be 1. So, for Mth array, if Nth array has (N-M+1)-D EJP (N>=M), the Mth array is iterable. Or, the Mth array is not iterable, the length of it will be 1.
  • Rule 6. If template field is wildcard EJP but not in the iterable array, the target JSON field will be generated by iterating the wildcard EJP.
  • Rule 7. If Mth & (M+1)th array's length are both 1 and are both iterable, Mth array has X-D EJP, (M+1)th array has Y-D EJP, if (Y-X)< 1, the (M+1)th array will not be genrated.

Simple EJP examples

Given JSON 'moo':

{
    "a": {"a1":1},
    "b": [2,{"b1":3},4]
}
ID Template Field Value Generated Value Note
1 {"x":1} {"x":1} Rule 1
2 "*moo/a" {"a1":1} Rule 2
3 "*moo/a/a1" 1 Rule 2
4 "*moo/b" [2,{"b1":3},4] Rule 2
5 "*moo/b/0" 2 Rule 2
6 "*moo/b/-" 4 Rule 2
7 "*moo/b/1/b1" 3 Rule 2

1 dimensional EJP examples

Given JSON 'moo':

{
    "a": [2,3,4]
}
ID Template Field Value Generated Value Note
1 "*moo/a" [2,3,4] Rule 2
2 "*moo/a/$" [2,3,4] Rule 6
3 ["*moo/a/$"] [2,3,4] Rule 4
4 [{"x":"*moo/a/$"}] [{"x":2},{"x":3},{"x":4}] Rule 4
5 [{"x":"*moo/a/$"},{"y":"*moo/a/$"}] [{"x":[2,3,4]},{"y":[2,3,4]}] Rule 3 & Rule 6
6 [[{"x":"*moo/a/$"}],{"y":"*moo/a/$"}] [[{"x":2},{"x":3},{"x":4}],{"y":[2,3,4]}] Rule 3 & Rule 4 & Rule 6
7 [[{"x":"*moo/a/$"}]] [[{"x":2},{"x":3},{"x":4}]] Rule 4 & Rule 5
8 [{"x":"*moo/a/$","y":["*moo/a/$"]}] [{"x":2},{"x":3},{"x":4}] Rule 7, the outer array has 1-D EJP, field 'y' has 1-D EJP, 1-1<1, so field 'y' cannot be generated

2 dimensional EJP examples

{
    "a": [[1,2,3],[4,5,6],[7]]
}
ID Template Field Value Generated Value Note
1 "*moo/a/$" [[1,2,3],[4,5,6],[7]] Rule 6
2 ["*moo/a/$"] [[1,2,3],[4,5,6],[7]] Rule 4
3 [{"x":"*moo/a/$","y":1}] [{"x":[1,2,3],"y":1},{"x":[4,5,6],"y":1},{"x":[7],"y":1}] Rule 4 & Rule 1
4 [["*moo/a/$"]] [[[1,2,3],[4,5,6],[7]]] Rule 5 & Rule 4
5 "*moo/a/$/$" [1,2,3,4,5,6,7] Rule 6
6 ["*moo/a/$/$"] [[1,2,3],[4,5,6],[7]] Rule 4
7 [["*moo/a/$/$"]] [[1,2,3],[4,5,6],[7]] Rule 4
8 [[["*moo/a/$/$"]]] [[[1,2,3],[4,5,6],[7]]] Rule 5 & Rule 4
9 [{"x":[{"y":"*moo/a/$/$"}]}] [{"x":[{"y":1},{"y":2},{"y":3}]},{"x":[{"y":4},{"y":5},{"y":6}]},{"x":[{"y":7}]}] Rule 4

JSON template examples

Here's one valid JSON templates:

{
    "a": 1,
    "b": "*foo/y/$/y1",
    "c": "*bar/~2/1",
    "d": "\\*comments*"
}
  • Field 'a' is plain data.
  • Field 'b' has a wildcard in EJP, so JSON 'foo' must be an object, and the object should contain a field 'y' which is an array, the items in the array should be object containing field 'y1'.
  • Field 'c' references JSON 'bar', which is an object containing key '$'.
  • Field 'd' is a plain string '*comments*', so it must be escaped to '\\*comments*'.

Given the following JSON pool:

JSON name: foo

{
    "x": [1, 2, {"x1": 3}],
    "y": [{"y1":1}, {"y1":2}, {"y1":3}]
}

JSON name: bar

{
    "$": [1, 2, 3]
}

After executing the template, you'll get:

{
    "a": 1,
    "b": [1, 2, 3],
    "c": 2,
    "d": "*comments*"
}

See more complicated examples here. Note, there's a 'schema.const' field in each test case, it defines the expected JSON after executing the template.

Usage

Package jvalue is here.

    // Create JSON pool.
    pool := template.NewJSONPool()
    
    // Add JSONs to pool.
    j1, err := jvalue.ParseJSON([]byte(...))
    if err != nil {
        return err
    }
    pool.Add("JSON_NAME_J1", j1)
    
    j2, err := jvalue.ParseJSON([]byte(...))
    if err != nil {
        return err
    }
    pool.Add("JSON_NAME_J2", j2)

    // Create template.
    tpl, err := template.Parse([]byte(...))
    if err != nil {
        return err
    }
    
    // Execute template.
    jv, err := tpl.Execute(pool)
    if err != nil {
        return err
    }
    
    // Add the result to pool for later use.
    pool.Add("JSON_NAME_J3", jv)
    
    // Marshal JValue to []byte.
    raw, err := jv.Marshal()
    if err != nil {
        return err
    }
    
    fmt.Println(string(raw))

Playground

template