The goal of this section is to provide you a set of easy-to-understand examples on how to use JData constructs to represent real-world data. Please note that
light-green-shaded data snippets
indicate the use of JData-defined annotations, but the outcome data files are 100% compatible with existing JSON and UBJSON parsers
light-blue-shaded data snippets
indicate the use of JData-extended optimized array header, and such data must be parsed using JData-compliant UBJSON parsers, but may produce an error if your parser does not support this extended syntax
Numerical values are directly supported by either JSON or UBJSON specifications. A numerical value is typically unchanged when converting to the JData annotation. When storing as files, they are directly stored in the JSON/UBJSON numerical value forms. For example
Native data | text-JData/JSON form | binary-JData(UBJSON) | |
---|---|---|---|
a=3 | => | {"a":3} | [{] [U][1][a][U][3] [}] |
Similar for a floating point number
Native data | text-JData/JSON form | binary-JData(UBJSON) † | |
---|---|---|---|
a=3.14159 | => | {"a":3.14159} | [{] [U][1][a][D][3.15169] [}] |
There are a few special constants, namely "NaN", "Infinity" and "-Infinity", they are encoded as special string keywords when stored in the JSON/text-JData formats, but stay unchanged when stored in the binary JData format
Native data | text-JData/JSON form | binary-JData(UBJSON) † | |
---|---|---|---|
a=nan | => | {"a":"_NaN_"} | [{] [U][1][a][D][nan] [}] |
a=inf | => | {"a":"_Inf_"} | [{] [U][1][a][D][inf] [}] |
a=-inf | => | {"a":"-_Inf_"} | [{] [U][1][D][-inf] [}] |
Three special constants are directly supported by the JSON/UBJSON specifications, these constants are logical "True", logical "False" and "Null", they are stored as
Native data | text-JData/JSON form | binary-JData(UBJSON) | |
---|---|---|---|
a=true | => | {"a":true} | [{] [U][1][a][T] [}] |
a=false | => | {"a":false} | [{] [U][1][a][F] [}] |
a=null | => | {"a":null} | [{] [U][1][a][Z] [}] |
Strings are natively supported in both JSON and UBJSON, therefore, when converting to the JData annotations, they are mostly unchanged. For JSON strings, 9 special characters can be escaped to represent control sequences (\",\\,\/,\b,\f,\n,\r,\t,\uXXXX
). For example
Native data | text-JData/JSON form | binary-JData(UBJSON) | |
---|---|---|---|
a="JData will prevail" | => | {"a":"JData will prevail"} | [{] [U][1][a][S][U][18][JData will prevail] [}] |
a="Tabs\tNewlines\n" | => | {"a":"Tabs\tNewlines\n"} | [{] [U][1][a][S][U][14][Tabs\tNewlines\n] [}] |
a="中文UTF-8格式" | => | {"a":"中文UTF-8格式"} | [{] [U][1][a][S][U][17][中文UTF-8格式] [}] |
We want to mention that in JData, "non-strict" JSON string mode is allowed: that means one can store ASCII values 0-31 as part of the string without escaping.
Hierarchical structures are often needed when representing metadata or simple lists with named-members. Because "structure" data-type can be directly mapped to the "object" construct in JSON and UBJSON, therefore, they do not need to be converted when using the JData annotation.
Native data | text-JData/JSON form | binary-JData(UBJSON) | |
---|---|---|---|
a=struct( | => | { | [{] |
Simple 1-dimensional vectors are supported in both JSON and UBJSON using the "array" construct. For example
Native data | text-JData/JSON form | binary-JData(UBJSON) | |
---|---|---|---|
a=[1,2,3] | => | {"a":[1,2,3]} | [{] |
when array element have the same type, binary JData/UBJSON can use the "optimized" array header to save space. For the array above, one can write in the right form, where [$][U] indicates array element type is "unsigned byte" [U] and [#][U][3] indicates that there are 3 elements | [{] | ||
a=[2.0,"str",nan] | => | {"a":[2.0,"str","_NaN_"]} | [{] |
To distinguish column from row vectors, we use embedded array constructs in JSON/UBJSON to represent a column vector
Native data | text-JData/JSON form | binary-JData(UBJSON) | |
---|---|---|---|
a=[[1],[2],[3]] | => | {"a":[[1],[2],[3]]} | [{] |
a=[[2.0],["str"],[nan]] | => | {"a":[[2.0],["str"],["_NaN_"]]} | [{] |
Simple 1-dimensional vectors are supported in both JSON and UBJSON using the "array" construct. For example
Native data | text-JData/JSON form | binary-JData(UBJSON) | |
---|---|---|---|
a=[ | => | { | [{] |
similar to the 1-D raow vector example above, we can use the type [$] and count [#] markers to simplify this array in the binary form | [{] | ||
to simplify this further, in the JData Specification, we further extended UBJSON array count marker [#] to accept a 1-D array count-type, representing the dimension vector of an N-D array, in this case [2,3] for a 2x3 matrix | [{] |
In JData specification, we introduced a light-weight data annotation approach to allow one to specify additional information, such as data type, data size and compression, in the stored data record. This is achieved using a "structure-like" data container (a structure is supported in almost all programming language) with JData-specified human-readable subfield keywords. This construct is also easily serialized using many of the existing JSON/UBJSON libraries.
For example, the above 2-D array can be alternatively stored using the annotated format to allow fine-grained data storage
Native data | text-JData/JSON form | binary-JData(UBJSON) | |
---|---|---|---|
a=[ | => | { | [{] |
Please note that the binary JData format already has built-in support to strongly-typed binary data, therefore, it is not very beneficial, despite it is permitted, to use the above annotated format compared to the packed-array format with optimized header shown in the above section.
One can use either the direct-format or annotated-format for storing higher dimensional arrays, as natively supported by both JSON/UBJSON, but the benefit of using the annotated format for text-based JData, and the packed-array optimized format for binary-JData becomes more advantageous due to faster processing speed.
Native data | text-JData/JSON form | binary-JData(UBJSON) | |
---|---|---|---|
a=[ [ [1,9,6,0], [2,9,3,1], [8,0,9,6] ], [ [6,4,2,7], [8,5,1,2], [3,3,2,6] ] ] | => |
{ "a":[ [ [1,9,6,0], [2,9,3,1], [8,0,9,6] ], [ [6,4,2,7], [8,5,1,2], [3,3,2,6] ] ] } | [{] [U][1][a] [[] [[] [[] [U][1][U][9][U][6][u][0] []] [[] [U][2][U][9][U][3][u][1] []] [[] [U][8][U][0][U][9][u][6] []] []] [[] [[] [U][6][U][4][U][2][u][7] []] [[] [U][8][U][5][U][1][u][2] []] [[] [U][3][U][3][U][2][u][6] []] []] []] [}] |
More efficient alternative formats using JData annotations |
{ "a":{ "_ArrayType_":"uint8", "_ArraySize_":[2,3,4], "_ArrayType_":[1,9,6,0,2,9,3,1,8,0,9,6,6,4,2, 7,8,5,1,2,3,3,2,6] } } | [{] [U][1][a] [[] [$][U] [#][[] [$][U][#][3] [2][3][4] [1][9][6][0][2][9][3][1][8][0][9][6][6][4][2] [7][8][5][1][2][3][3][2][6] [}] |
JData annotations allows easy extensions. One of the immediate benefits is to introduce data compression to save space. To permit data compression, several additional keywords can be used, including "_ArrayZipType" - the compression method used, "_ArrayZipSize_" - the dimension vector of the "preprocessed" data stored in the "_ArrayData_" construct before compression, and "_ArrayZipData_" - the compressed data byte-stream. For example
Native data | text-JData/JSON form | binary-JData(UBJSON) | |
---|---|---|---|
a=[ | => | { | [{] |