From 73bec976254749fc6d5b137fd486f819a564c9f9 Mon Sep 17 00:00:00 2001 From: "Michael Stumpf (ifhcluster)" Date: Tue, 10 Sep 2019 17:20:56 +0200 Subject: [PATCH] added openIndexed method, which allows indexing of tar file from predetermined meta data file --- matlab/+jsonlab/AUTHORS.txt | 63 ++ matlab/+jsonlab/ChangeLog.txt | 140 ++++ matlab/+jsonlab/LICENSE_BSD.txt | 55 ++ matlab/+jsonlab/README.rst | 579 +++++++++++++++ matlab/+jsonlab/README.txt | 565 +++++++++++++++ matlab/+jsonlab/base64decode.m | 39 + matlab/+jsonlab/base64encode.m | 34 + matlab/+jsonlab/examples/demo_jsonlab_basic.m | 181 +++++ matlab/+jsonlab/examples/demo_ubjson_basic.m | 180 +++++ matlab/+jsonlab/examples/example1.json | 23 + matlab/+jsonlab/examples/example2.json | 22 + matlab/+jsonlab/examples/example3.json | 11 + matlab/+jsonlab/examples/example4.json | 35 + .../examples/jsonlab_basictest.matlab | 671 +++++++++++++++++ matlab/+jsonlab/examples/jsonlab_selftest.m | 27 + .../+jsonlab/examples/jsonlab_selftest.matlab | 154 ++++ matlab/+jsonlab/examples/jsonlab_speedtest.m | 21 + .../examples/jsonlab_ubjson_basictest.matlab | 394 ++++++++++ matlab/+jsonlab/gzipdecode.m | 41 ++ matlab/+jsonlab/gzipencode.m | 39 + matlab/+jsonlab/jsonopt.m | 36 + matlab/+jsonlab/license.txt | 35 + matlab/+jsonlab/loadjson.m | 537 ++++++++++++++ matlab/+jsonlab/loadubjson.m | 465 ++++++++++++ matlab/+jsonlab/mergestruct.m | 34 + matlab/+jsonlab/package.json | 21 + matlab/+jsonlab/savejson.m | 686 ++++++++++++++++++ matlab/+jsonlab/saveubjson.m | 663 +++++++++++++++++ matlab/+jsonlab/struct2jdata.m | 126 ++++ matlab/+jsonlab/varargin2struct.m | 41 ++ matlab/+jsonlab/zlibdecode.m | 41 ++ matlab/+jsonlab/zlibencode.m | 40 + matlab/+msgpack/README.md | 89 +++ matlab/+msgpack/dumpmsgpack.m | 226 ++++++ matlab/+msgpack/dumpmsgpack_test.m | 96 +++ matlab/+msgpack/parsemsgpack.m | 194 +++++ matlab/+msgpack/parsemsgpack_test.m | 111 +++ matlab/@ustar/ustar.m | 103 ++- 38 files changed, 6817 insertions(+), 1 deletion(-) create mode 100644 matlab/+jsonlab/AUTHORS.txt create mode 100644 matlab/+jsonlab/ChangeLog.txt create mode 100644 matlab/+jsonlab/LICENSE_BSD.txt create mode 100644 matlab/+jsonlab/README.rst create mode 100644 matlab/+jsonlab/README.txt create mode 100644 matlab/+jsonlab/base64decode.m create mode 100644 matlab/+jsonlab/base64encode.m create mode 100644 matlab/+jsonlab/examples/demo_jsonlab_basic.m create mode 100644 matlab/+jsonlab/examples/demo_ubjson_basic.m create mode 100644 matlab/+jsonlab/examples/example1.json create mode 100644 matlab/+jsonlab/examples/example2.json create mode 100644 matlab/+jsonlab/examples/example3.json create mode 100644 matlab/+jsonlab/examples/example4.json create mode 100644 matlab/+jsonlab/examples/jsonlab_basictest.matlab create mode 100644 matlab/+jsonlab/examples/jsonlab_selftest.m create mode 100644 matlab/+jsonlab/examples/jsonlab_selftest.matlab create mode 100644 matlab/+jsonlab/examples/jsonlab_speedtest.m create mode 100644 matlab/+jsonlab/examples/jsonlab_ubjson_basictest.matlab create mode 100644 matlab/+jsonlab/gzipdecode.m create mode 100644 matlab/+jsonlab/gzipencode.m create mode 100644 matlab/+jsonlab/jsonopt.m create mode 100644 matlab/+jsonlab/license.txt create mode 100644 matlab/+jsonlab/loadjson.m create mode 100644 matlab/+jsonlab/loadubjson.m create mode 100644 matlab/+jsonlab/mergestruct.m create mode 100644 matlab/+jsonlab/package.json create mode 100644 matlab/+jsonlab/savejson.m create mode 100644 matlab/+jsonlab/saveubjson.m create mode 100644 matlab/+jsonlab/struct2jdata.m create mode 100644 matlab/+jsonlab/varargin2struct.m create mode 100644 matlab/+jsonlab/zlibdecode.m create mode 100644 matlab/+jsonlab/zlibencode.m create mode 100644 matlab/+msgpack/README.md create mode 100644 matlab/+msgpack/dumpmsgpack.m create mode 100644 matlab/+msgpack/dumpmsgpack_test.m create mode 100644 matlab/+msgpack/parsemsgpack.m create mode 100644 matlab/+msgpack/parsemsgpack_test.m diff --git a/matlab/+jsonlab/AUTHORS.txt b/matlab/+jsonlab/AUTHORS.txt new file mode 100644 index 0000000..e4c2757 --- /dev/null +++ b/matlab/+jsonlab/AUTHORS.txt @@ -0,0 +1,63 @@ +The author of "jsonlab" toolbox is Qianqian Fang. Qianqian +is currently an Assistant Professor in the Department of Bioengineering, +Northeastern University. + +Address: Qianqian Fang + Department of Bioengineering + Northeastern University + 212A Lake Hall + 360 Huntington Ave, Boston, MA 02115, USA + Office: 503 Holmes Hall + Phone[O]: 617-373-3829 +URL: http://fanglab.org +Email: and + + +The script loadjson.m was built upon previous works by + +- Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713 + date: 2009/11/02 +- François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393 + date: 2009/03/22 +- Joel Feenstra: http://www.mathworks.com/matlabcentral/fileexchange/20565 + date: 2008/07/03 + +The data compression/decompression utilities ({zlib,gzip,base64}{encode,decode}.m) +were copied from + +- "Byte encoding utilities" by Kota Yamaguchi + https://www.mathworks.com/matlabcentral/fileexchange/39526-byte-encoding-utilities + date: 2013/01/04 + + +This toolbox contains patches submitted by the following contributors: + +- Blake Johnson + part of revision 341 + +- Niclas Borlin + various fixes in revision 394, including + - loadjson crashes for all-zero sparse matrix. + - loadjson crashes for empty sparse matrix. + - Non-zero size of 0-by-N and N-by-0 empty matrices is lost after savejson/loadjson. + - loadjson crashes for sparse real column vector. + - loadjson crashes for sparse complex column vector. + - Data is corrupted by savejson for sparse real row vector. + - savejson crashes for sparse complex row vector. + +- Yul Kang + patches for svn revision 415. + - savejson saves an empty cell array as [] instead of null + - loadjson differentiates an empty struct from an empty array + +- Mykhailo Bratukha + (Pull#14) Bug fix: File path is wrongly inerpreted as JSON string + +- Insik Kim + (Pull#12) Bug fix: Resolving bug that cell type is converted to json with transposed data + +- Sertan Senturk + (Pull#10,#11) Feature: Added matlab object saving to savejson and saveubjson + +- Paul Koprowski + (Issue#29) Feature: Added support to save MATLAB tables to json. \ No newline at end of file diff --git a/matlab/+jsonlab/ChangeLog.txt b/matlab/+jsonlab/ChangeLog.txt new file mode 100644 index 0000000..be44dba --- /dev/null +++ b/matlab/+jsonlab/ChangeLog.txt @@ -0,0 +1,140 @@ +============================================================================ + + JSONlab - a toolbox to encode/decode JSON/UBJSON files in MATLAB/Octave + +---------------------------------------------------------------------------- + +JSONlab ChangeLog (key features marked by *): + +== JSONlab 1.9 (codename: Magnus - alpha), FangQ neu.edu> == + + 2019-05-06 [25ad795] unescape string in loadjson.m + 2019-05-04 [2e317c9] explain extra compression fields + 2019-05-02 [1b1be65] avoid side effect of removing singletarray + 2019-05-02*[8360fd1] support zmat based base64 encoding and decoding + 2019-05-01*[c797bb2] integrating zmat, for zlib/gzip data compression + 2019-04-29 [70551fe] remove warnings from matlab + 2019-04-28 [0d61c4b] complete data compression support, close #52 + 2019-04-27 [804115b] avoid typecast error + 2019-04-27 [c166aa7] change default compressarraysize to 100 + 2019-04-27*[3322f6f] major new feature: support array compression and decompression + 2019-03-13*[9c01046] support saving function handles, close #51 + 2019-03-13 [a8fde38] add option to parse string array or convert to char, close #50 + 2019-03-12 [ed2645e] treat string array as cell array in newer matlab + 2018-11-18 [c3eb021] allow saving uint64 integers in saveubjson, fix #49 + +== JSONlab 1.8 (codename: Nominus), FangQ neu.edu> == + + 2018-07-12 [03a6c25] update documentation, bump version to 1.8, tag Nominus + 2018-07-12*[1597106] add patch provided by pjkoprowski to support MATLAB table, add RowNames support, fix #29 + 2018-07-12 [f16cc57] fix #31, throw an error when : array construct is used + 2018-07-12 [956e000] drop octave 3.x support, fix ubjson error in octave + 2018-07-12 [e090f0a] fix octave warning for saveubjson + 2018-07-12*[34284c7] fix issues #34 #39 #44 and #45, support double-quoted strings + 2017-09-06 [474d8c8] Merge pull request #41 from dasantonym/master + 2017-08-07*[38b24fb] added package.json to be able to intall via npm package manager, converted readme to utf-8, added basic .gitignore file + 2017-07-19 [ae7a5d9] Merge pull request #40 from astorfi/master + 2017-07-17 [154ef61] Rename README.txt to README.rst + 2017-03-27 [31b5bdc] simplify condition flow in matching_bracket + 2017-03-27 [86ef12a] avoid error in matlab 2017a, close #34 + 2017-02-18 [4a09ac3] Merge pull request #32 from vrichter/master + 2017-02-14 [e67d3a3] respect integer types + +== JSONlab 1.5 (codename: Nominus - alpha), FangQ neu.edu> == + + 2017/01/02 *use Big-endian format to store floating points (d/D) in saveubjson (Issue #25) + 2017/01/02 *speedup parsing large unstructured data by 2x (Issue #9) + 2017/01/01 make parsing independent of white space (Issue #30) + 2016/08/27 allow to parse array of homogeneous elements (Issue 5) + 2016/08/22 permit [] inside file names in savejson + 2016/01/06 fix a bug that prevents saving to a file in savejson + + +== JSONlab 1.2 (codename: Optimus - Update 2), FangQ neu.edu> == + + 2015/12/16 *replacing string concatenation by str cells to gain 2x speed in savejson (Issue#17) + 2015/12/11 fix FileName option case bug (SVN rev#495) + 2015/12/11 add SingletCell option, add SingletArray to replace NoRowBracket (Issue#15,#8) + 2015/11/10 fix bug for inerpreting file names as JSON string - by Mykhailo Bratukha (Pull#14) + 2015/10/16 fix bug for cell with transposed data - by Insik Kim (Pull#12) + 2015/09/25 support exporting matlab object to JSON - by Sertan Senturk (Pull#10, #11) + +== JSONlab 1.1 (codename: Optimus - Update 1), FangQ neu.edu> == + + 2015/05/05 *massively accelerating loadjson for parsing large collection of unstructured small objects + 2015/05/05 force array bracket in 1x1 struct to maintain depth (Issue#1) + 2015/05/05 parse logicals in loadjson + 2015/05/05 make options case insensitive + 2015/05/01 reading unicode encoded json files (thanks to Sertan Senturk,Issue#3) + 2015/04/30 allow \uXXXX to represent a unicode in a string (Issue#2) + 2015/03/30 save a 0x0 solid real empty array as null and handel empty struct array + 2015/03/30 properly handle escape characters in a string + 2015/01/24 *implement the UBJSON Draft12 new name format + 2015/01/13 correct cell array indentation inconsistency + +== JSONlab 1.0 (codename: Optimus - Final), FangQ neu.edu> == + + 2015/01/02 polish help info for all major functions, update examples, finalize 1.0 + 2014/12/19 fix a bug to strictly respect NoRowBracket in savejson + +== JSONlab 1.0.0-RC2 (codename: Optimus - RC2), FangQ neu.edu> == + + 2014/11/22 show progress bar in loadjson ('ShowProgress') + 2014/11/17 *add Compact option in savejson to output compact JSON format ('Compact') + 2014/11/17 add FastArrayParser in loadjson to specify fast parser applicable levels + 2014/09/18 *start official github mirror: https://github.com/fangq/jsonlab + +== JSONlab 1.0.0-RC1 (codename: Optimus - RC1), FangQ neu.edu> == + + 2014/09/17 fix several compatibility issues when running on octave versions 3.2-3.8 + 2014/09/17 *support 2D cell and struct arrays in both savejson and saveubjson + 2014/08/04 escape special characters in a JSON string + 2014/02/16 fix a bug when saving ubjson files + +== JSONlab 0.9.9 (codename: Optimus - beta), FangQ neu.edu> == + + 2014/01/22 use binary read and write in saveubjson and loadubjson + +== JSONlab 0.9.8-1 (codename: Optimus - alpha update 1), FangQ neu.edu> == + + 2013/10/07 better round-trip conservation for empty arrays and structs (patch submitted by Yul Kang) + +== JSONlab 0.9.8 (codename: Optimus - alpha), FangQ neu.edu> == + 2013/08/23 *universal Binary JSON (UBJSON) support, including both saveubjson and loadubjson + +== JSONlab 0.9.1 (codename: Rodimus, update 1), FangQ neu.edu> == + 2012/12/18 *handling of various empty and sparse matrices (fixes submitted by Niclas Borlin) + +== JSONlab 0.9.0 (codename: Rodimus), FangQ neu.edu> == + + 2012/06/17 *new format for an invalid leading char, unpacking hex code in savejson + 2012/06/01 support JSONP in savejson + 2012/05/25 fix the empty cell bug (reported by Cyril Davin) + 2012/04/05 savejson can save to a file (suggested by Patrick Rapin) + +== JSONlab 0.8.1 (codename: Sentiel, Update 1), FangQ neu.edu> == + + 2012/02/28 loadjson quotation mark escape bug, see http://bit.ly/yyk1nS + 2012/01/25 patch to handle root-less objects, contributed by Blake Johnson + +== JSONlab 0.8.0 (codename: Sentiel), FangQ neu.edu> == + + 2012/01/13 *speed up loadjson by 20 fold when parsing large data arrays in matlab + 2012/01/11 remove row bracket if an array has 1 element, suggested by Mykel Kochenderfer + 2011/12/22 *accept sequence of 'param',value input in savejson and loadjson + 2011/11/18 fix struct array bug reported by Mykel Kochenderfer + +== JSONlab 0.5.1 (codename: Nexus Update 1), FangQ neu.edu> == + + 2011/10/21 fix a bug in loadjson, previous code does not use any of the acceleration + 2011/10/20 loadjson supports JSON collections - concatenated JSON objects + +== JSONlab 0.5.0 (codename: Nexus), FangQ neu.edu> == + + 2011/10/16 package and release jsonlab 0.5.0 + 2011/10/15 *add json demo and regression test, support cpx numbers, fix double quote bug + 2011/10/11 *speed up readjson dramatically, interpret _Array* tags, show data in root level + 2011/10/10 create jsonlab project, start jsonlab website, add online documentation + 2011/10/07 *speed up savejson by 25x using sprintf instead of mat2str, add options support + 2011/10/06 *savejson works for structs, cells and arrays + 2011/09/09 derive loadjson from JSON parser from MATLAB Central, draft savejson.m diff --git a/matlab/+jsonlab/LICENSE_BSD.txt b/matlab/+jsonlab/LICENSE_BSD.txt new file mode 100644 index 0000000..8ddec88 --- /dev/null +++ b/matlab/+jsonlab/LICENSE_BSD.txt @@ -0,0 +1,55 @@ +Copyright 2011-2019 Qianqian Fang neu.edu>. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of the copyright holders. + + + + +For the included compression/decompression utilities (base64encode.m, base64decode.m, +gzipencode.m, gzipdecode.m, zlibencode.m, zlibdecode.m), the author Kota Yamaguchi +requires the following copyright declaration: + +Copyright (c) 2012, Kota Yamaguchi +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/matlab/+jsonlab/README.rst b/matlab/+jsonlab/README.rst new file mode 100644 index 0000000..78af49b --- /dev/null +++ b/matlab/+jsonlab/README.rst @@ -0,0 +1,579 @@ +############################################################################## + JSONLab: An open-source MATLAB/Octave JSON encoder and decoder +############################################################################## + +* Copyright (C) 2011-2019 Qianqian Fang +* License: BSD, License_BSD.txt for details +* Version: 1.9 (Magnus - alpha) + + +################# +Table of Contents +################# +.. contents:: + :local: + :depth: 3 + +============ +What's New +============ + +JSONLab v1.9 is the alpha release of the next milestone - code named "Magnus". +Notable changes are summarized below, key features marked by *: + +- 2019-05-06 [25ad795] unescape string in loadjson.m +- 2019-05-04 [2e317c9] explain extra compression fields +- 2019-05-02 [1b1be65] avoid side effect of removing singletarray +- 2019-05-02*[8360fd1] support zmat based base64 encoding and decoding +- 2019-05-01*[c797bb2] integrating zmat, for zlib/gzip data compression +- 2019-04-29 [70551fe] remove warnings from matlab +- 2019-04-28 [0d61c4b] complete data compression support, close #52 +- 2019-04-27 [804115b] avoid typecast error +- 2019-04-27 [c166aa7] change default compressarraysize to 100 +- 2019-04-27*[3322f6f] major new feature: support array compression and decompression +- 2019-03-13*[9c01046] support saving function handles, close #51 +- 2019-03-13 [a8fde38] add option to parse string array or convert to char, close #50 +- 2019-03-12 [ed2645e] treat string array as cell array in newer matlab +- 2018-11-18 [c3eb021] allow saving uint64 integers in saveubjson, fix #49 + +The biggest change in this release, compared to v1.8 released in July 2018, +is the support of data compression via the 'Compression' option for both +savejson and saveubjson. Two compression methods are currently supported - +"zlib" and "gzip". The compression interfaces, zlibencode/zlibdecode/gzipencode/ +gzipdecode are modified from the "Byte Encoding Utilities" by Kota Yamaguchi [1], +which has built-in support for java-based compression in MATLAB (when jvm is +enabled). To support Octave, as well as MATLAB in "nojvm" mode, a mex-based +data compression/encoding toolbox, ZMat [2], written by Qianqian Fang, takes priority +over the java-based utilities, if installed. For savejson, a 'base64' encoding is +applied to convert the compressed binary stream into a string; 'base64' encoding +is not used in saveubjson. The encoding and restoration of the binary matlab arrays +are automatically handled in save*json/load*json round-trip conversions. + +To save matlab data with compression, one simply append 'Compression', 'method' pair +in the savejson/saveubjson call. For example + +.. code:: matlab + + jsonstr=savejson('',mydata,'compression','zlib'); + data=loadjson(jsonstr); + +In addition, the below features are added to JSONLab + + * save function handles + * support saving "string" class in MATLAB + * fix two bugs in saveubjson + * unescape strings in loadjson + + +- [1] https://www.mathworks.com/matlabcentral/fileexchange/39526-byte-encoding-utilities +- [2] http://github.com/fangq/zmat + +============ +Introduction +============ + +JSONLab is a free and open-source implementation of a JSON/UBJSON encoder +and a decoder in the native MATLAB language. It can be used to convert a MATLAB +data structure (array, struct, cell, struct array and cell array) into +JSON/UBJSON formatted strings, or to decode a JSON/UBJSON file into MATLAB +data structure. JSONLab supports both MATLAB and `GNU Octave `_ (a free MATLAB clone). + +JSON (`JavaScript Object Notation `_) is a highly portable, +human-readable and " `fat-free `_" text format +to represent complex and hierarchical data. It is as powerful as `XML `_, +but less verbose. JSON format is widely used for data-exchange in applications. + +UBJSON (`Universal Binary JSON `_) is a binary JSON format, specifically +optimized for compact file size and better performance while keeping +the semantics as simple as the text-based JSON format. Using the UBJSON +format allows to wrap complex binary data in a flexible and extensible +structure, making it possible to process complex and large dataset +without accuracy loss due to text conversions. + +We envision that both JSON and its binary version will play important roles +as mainstream data-exchange formats for scientific research. +It has both the flexibility and generality as offered by other popular +general-purpose file specifications, such as `HDF5 `_, +but with significantly reduced complexity and excellent readability. + +================ +Installation +================ + +The installation of JSONLab is no different than any other simple +MATLAB toolbox. You only need to download/unzip the JSONLab package +to a folder, and add the folder's path to MATLAB/Octave's path list +by using the following command: + +.. code:: shell + + addpath('/path/to/jsonlab'); + +If you want to add this path permanently, you need to type "pathtool", +browse to the zmat root folder and add to the list, then click "Save". +Then, run "rehash" in MATLAB, and type "which savejson", if you see an +output, that means JSONLab is installed for MATLAB/Octave. + +If you use MATLAB in a shared environment such as a Linux server, the +best way to add path is to type + +.. code:: shell + + mkdir ~/matlab/ + nano ~/matlab/startup.m + +and type addpath('/path/to/jsonlab') in this file, save and quit the editor. +MATLAB will execute this file every time it starts. For Octave, the file +you need to edit is ~/.octaverc , where "~" is your home directory. + + +================ +Using JSONLab +================ + +JSONLab provides two functions, loadjson.m -- a MATLAB->JSON decoder, +and savejson.m -- a MATLAB->JSON encoder, for the text-based JSON, and +two equivallent functions -- loadubjson and saveubjson for the binary +JSON. The detailed help info for the four functions can be found below: + +---------- +loadjson.m +---------- + +.. code-block:: matlab + + data=loadjson(fname,opt) + or + data=loadjson(fname,'param1',value1,'param2',value2,...) + + parse a JSON (JavaScript Object Notation) file or string + + authors:Qianqian Fang (q.fang neu.edu) + created on 2011/09/09, including previous works from + + Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713 + created on 2009/11/02 + François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393 + created on 2009/03/22 + Joel Feenstra: + http://www.mathworks.com/matlabcentral/fileexchange/20565 + created on 2008/07/03 + + $Id$ + + input: + fname: input file name, if fname contains "{}" or "[]", fname + will be interpreted as a JSON string + opt: a struct to store parsing options, opt can be replaced by + a list of ('param',value) pairs - the param string is equivallent + to a field in opt. opt can have the following + fields (first in [.|.] is the default) + + opt.SimplifyCell [0|1]: if set to 1, loadjson will call cell2mat + for each element of the JSON data, and group + arrays based on the cell2mat rules. + opt.FastArrayParser [1|0 or integer]: if set to 1, use a + speed-optimized array parser when loading an + array object. The fast array parser may + collapse block arrays into a single large + array similar to rules defined in cell2mat; 0 to + use a legacy parser; if set to a larger-than-1 + value, this option will specify the minimum + dimension to enable the fast array parser. For + example, if the input is a 3D array, setting + FastArrayParser to 1 will return a 3D array; + setting to 2 will return a cell array of 2D + arrays; setting to 3 will return to a 2D cell + array of 1D vectors; setting to 4 will return a + 3D cell array. + opt.ShowProgress [0|1]: if set to 1, loadjson displays a progress bar. + opt.ParseStringArray [0|1]: if set to 1, loadjson displays a progress bar. + + output: + dat: a cell array, where {...} blocks are converted into cell arrays, + and [...] are converted to arrays + + examples: + dat=loadjson('{"obj":{"string":"value","array":[1,2,3]}}') + dat=loadjson(['examples' filesep 'example1.json']) + dat=loadjson(['examples' filesep 'example1.json'],'SimplifyCell',1) + + license: + BSD, see LICENSE_BSD.txt file for details + +---------- +savejson.m +---------- + +.. code-block:: matlab + + json=savejson(rootname,obj,filename) + or + json=savejson(rootname,obj,opt) + json=savejson(rootname,obj,'param1',value1,'param2',value2,...) + + convert a MATLAB object (cell, struct or array) into a JSON (JavaScript + Object Notation) string + + author: Qianqian Fang (q.fang neu.edu) + created on 2011/09/09 + + $Id$ + + input: + rootname: the name of the root-object, when set to '', the root name + is ignored, however, when opt.ForceRootName is set to 1 (see below), + the MATLAB variable name will be used as the root name. + obj: a MATLAB object (array, cell, cell array, struct, struct array, + class instance). + filename: a string for the file name to save the output JSON data. + opt: a struct for additional options, ignore to use default values. + opt can have the following fields (first in [.|.] is the default) + + opt.FileName [''|string]: a file name to save the output JSON data + opt.FloatFormat ['%.10g'|string]: format to show each numeric element + of a 1D/2D array; + opt.ArrayIndent [1|0]: if 1, output explicit data array with + precedent indentation; if 0, no indentation + opt.ArrayToStruct[0|1]: when set to 0, savejson outputs 1D/2D + array in JSON array format; if sets to 1, an + array will be shown as a struct with fields + "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for + sparse arrays, the non-zero elements will be + saved to _ArrayData_ field in triplet-format i.e. + (ix,iy,val) and "_ArrayIsSparse_" will be added + with a value of 1; for a complex array, the + _ArrayData_ array will include two columns + (4 for sparse) to record the real and imaginary + parts, and also "_ArrayIsComplex_":1 is added. + opt.ParseLogical [0|1]: if this is set to 1, logical array elem + will use true/false rather than 1/0. + opt.SingletArray [0|1]: if this is set to 1, arrays with a single + numerical element will be shown without a square + bracket, unless it is the root object; if 0, square + brackets are forced for any numerical arrays. + opt.SingletCell [1|0]: if 1, always enclose a cell with "[]" + even it has only one element; if 0, brackets + are ignored when a cell has only 1 element. + opt.ForceRootName [0|1]: when set to 1 and rootname is empty, savejson + will use the name of the passed obj variable as the + root object name; if obj is an expression and + does not have a name, 'root' will be used; if this + is set to 0 and rootname is empty, the root level + will be merged down to the lower level. + opt.Inf ['"$1_Inf_"'|string]: a customized regular expression pattern + to represent +/-Inf. The matched pattern is '([-+]*)Inf' + and $1 represents the sign. For those who want to use + 1e999 to represent Inf, they can set opt.Inf to '$11e999' + opt.NaN ['"_NaN_"'|string]: a customized regular expression pattern + to represent NaN + opt.JSONP [''|string]: to generate a JSONP output (JSON with padding), + for example, if opt.JSONP='foo', the JSON data is + wrapped inside a function call as 'foo(...);' + opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson + back to the string form + opt.SaveBinary [0|1]: 1 - save the JSON file in binary mode; 0 - text mode. + opt.Compact [0|1]: 1- out compact JSON format (remove all newlines and tabs) + opt.Compression 'zlib' or 'gzip': specify array compression + method; currently only supports 'gzip' or 'zlib'. The + data compression only applicable to numerical arrays + in 3D or higher dimensions, or when ArrayToStruct + is 1 for 1D or 2D arrays. If one wants to + compress a long string, one must convert + it to uint8 or int8 array first. The compressed + array uses three extra fields + "_ArrayCompressionMethod_": the opt.Compression value. + "_ArrayCompressionSize_": a 1D interger array to + store the pre-compressed (but post-processed) + array dimensions, and + "_ArrayCompressedData_": the "base64" encoded + compressed binary array data. + opt.CompressArraySize [100|int]: only to compress an array if the total + element count is larger than this number. + opt can be replaced by a list of ('param',value) pairs. The param + string is equivallent to a field in opt and is case sensitive. + output: + json: a string in the JSON format (see http://json.org) + + examples: + jsonmesh=struct('MeshNode',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],... + 'MeshTetra',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],... + 'MeshTri',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;... + 2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],... + 'MeshCreator','FangQ','MeshTitle','T6 Cube',... + 'SpecialData',[nan, inf, -inf]); + savejson('jmesh',jsonmesh) + savejson('',jsonmesh,'ArrayIndent',0,'FloatFormat','\t%.5g') + + license: + BSD, see LICENSE_BSD.txt file for details + +------------- +loadubjson.m +------------- + +.. code-block:: matlab + + data=loadubjson(fname,opt) + or + data=loadubjson(fname,'param1',value1,'param2',value2,...) + + parse a JSON (JavaScript Object Notation) file or string + + authors:Qianqian Fang (q.fang neu.edu) + created on 2013/08/01 + + $Id$ + + input: + fname: input file name, if fname contains "{}" or "[]", fname + will be interpreted as a UBJSON string + opt: a struct to store parsing options, opt can be replaced by + a list of ('param',value) pairs - the param string is equivallent + to a field in opt. opt can have the following + fields (first in [.|.] is the default) + + opt.SimplifyCell [0|1]: if set to 1, loadubjson will call cell2mat + for each element of the JSON data, and group + arrays based on the cell2mat rules. + opt.IntEndian [B|L]: specify the endianness of the integer fields + in the UBJSON input data. B - Big-Endian format for + integers (as required in the UBJSON specification); + L - input integer fields are in Little-Endian order. + opt.NameIsString [0|1]: for UBJSON Specification Draft 8 or + earlier versions (JSONLab 1.0 final or earlier), + the "name" tag is treated as a string. To load + these UBJSON data, you need to manually set this + flag to 1. + + output: + dat: a cell array, where {...} blocks are converted into cell arrays, + and [...] are converted to arrays + + examples: + obj=struct('string','value','array',[1 2 3]); + ubjdata=saveubjson('obj',obj); + dat=loadubjson(ubjdata) + dat=loadubjson(['examples' filesep 'example1.ubj']) + dat=loadubjson(['examples' filesep 'example1.ubj'],'SimplifyCell',1) + + license: + BSD, see LICENSE_BSD.txt file for details + +------------- +saveubjson.m +------------- + + +.. code-block:: matlab + + json=saveubjson(rootname,obj,filename) + or + json=saveubjson(rootname,obj,opt) + json=saveubjson(rootname,obj,'param1',value1,'param2',value2,...) + + convert a MATLAB object (cell, struct or array) into a Universal + Binary JSON (UBJSON) binary string + + author: Qianqian Fang (q.fang neu.edu) + created on 2013/08/17 + + $Id$ + + input: + rootname: the name of the root-object, when set to '', the root name + is ignored, however, when opt.ForceRootName is set to 1 (see below), + the MATLAB variable name will be used as the root name. + obj: a MATLAB object (array, cell, cell array, struct, struct array, + class instance) + filename: a string for the file name to save the output UBJSON data + opt: a struct for additional options, ignore to use default values. + opt can have the following fields (first in [.|.] is the default) + + opt.FileName [''|string]: a file name to save the output JSON data + opt.ArrayToStruct[0|1]: when set to 0, saveubjson outputs 1D/2D + array in JSON array format; if sets to 1, an + array will be shown as a struct with fields + "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for + sparse arrays, the non-zero elements will be + saved to _ArrayData_ field in triplet-format i.e. + (ix,iy,val) and "_ArrayIsSparse_" will be added + with a value of 1; for a complex array, the + _ArrayData_ array will include two columns + (4 for sparse) to record the real and imaginary + parts, and also "_ArrayIsComplex_":1 is added. + opt.ParseLogical [1|0]: if this is set to 1, logical array elem + will use true/false rather than 1/0. + opt.SingletArray [0|1]: if this is set to 1, arrays with a single + numerical element will be shown without a square + bracket, unless it is the root object; if 0, square + brackets are forced for any numerical arrays. + opt.SingletCell [1|0]: if 1, always enclose a cell with "[]" + even it has only one element; if 0, brackets + are ignored when a cell has only 1 element. + opt.ForceRootName [0|1]: when set to 1 and rootname is empty, saveubjson + will use the name of the passed obj variable as the + root object name; if obj is an expression and + does not have a name, 'root' will be used; if this + is set to 0 and rootname is empty, the root level + will be merged down to the lower level. + opt.JSONP [''|string]: to generate a JSONP output (JSON with padding), + for example, if opt.JSON='foo', the JSON data is + wrapped inside a function call as 'foo(...);' + opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson + back to the string form + opt.Compression 'zlib' or 'gzip': specify array compression + method; currently only supports 'gzip' or 'zlib'. The + data compression only applicable to numerical arrays + in 3D or higher dimensions, or when ArrayToStruct + is 1 for 1D or 2D arrays. If one wants to + compress a long string, one must convert + it to uint8 or int8 array first. The compressed + array uses three extra fields + "_ArrayCompressionMethod_": the opt.Compression value. + "_ArrayCompressionSize_": a 1D interger array to + store the pre-compressed (but post-processed) + array dimensions, and + "_ArrayCompressedData_": the binary stream of + the compressed binary array data WITHOUT + 'base64' encoding + opt.CompressArraySize [100|int]: only to compress an array if the total + element count is larger than this number. + + opt can be replaced by a list of ('param',value) pairs. The param + string is equivallent to a field in opt and is case sensitive. + output: + json: a binary string in the UBJSON format (see http://ubjson.org) + + examples: + jsonmesh=struct('MeshNode',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],... + 'MeshTetra',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],... + 'MeshTri',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;... + 2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],... + 'MeshCreator','FangQ','MeshTitle','T6 Cube',... + 'SpecialData',[nan, inf, -inf]); + saveubjson('jsonmesh',jsonmesh) + saveubjson('jsonmesh',jsonmesh,'meshdata.ubj') + + license: + BSD, see LICENSE_BSD.txt file for details + +--------- +examples +--------- + +Under the ``"examples"`` folder, you can find several scripts to demonstrate the +basic utilities of JSONLab. Running the ``"demo_jsonlab_basic.m"`` script, you +will see the conversions from MATLAB data structure to JSON text and backward. +In ``"jsonlab_selftest.m"``, we load complex JSON files downloaded from the Internet +and validate the ``loadjson/savejson`` functions for regression testing purposes. +Similarly, a ``"demo_ubjson_basic.m"`` script is provided to test the saveubjson +and loadubjson functions for various matlab data structures. + +Please run these examples and understand how JSONLab works before you use +it to process your data. + +======================= +Known Issues and TODOs +======================= + +JSONLab has several known limitations. We are striving to make it more general +and robust. Hopefully in a few future releases, the limitations become less. + +Here are the known issues: + + * 3D or higher dimensional cell/struct-arrays will be converted to 2D arrays + + * When processing names containing multi-byte characters, Octave and MATLAB can give different field-names; you can use feature('DefaultCharacterSet','latin1') in MATLAB to get consistant results + + * savejson can not handle class and dataset. + + * saveubjson converts a logical array into a uint8 ([U]) array + + * an unofficial N-D array count syntax is implemented in saveubjson. We are actively communicating with the UBJSON spec maintainer to investigate the possibility of making it upstream + + * loadubjson can not parse all UBJSON Specification (Draft 9) compliant files, however, it can parse all UBJSON files produced by saveubjson. + +========================== +Contribution and feedback +========================== + +JSONLab is an open-source project. This means you can not only use it and modify +it as you wish, but also you can contribute your changes back to JSONLab so +that everyone else can enjoy the improvement. For anyone who want to contribute, +please download JSONLab source code from its source code repositories by using the +following command: + + +.. code:: shell + + git clone https://github.com/fangq/jsonlab.git jsonlab + +or browsing the github site at + + https://github.com/fangq/jsonlab + +Please report any bugs or issues to the below URL: + + https://github.com/fangq/jsonlab/issues + +Sometimes, you may find it is necessary to modify JSONLab to achieve your +goals, or attempt to modify JSONLab functions to fix a bug that you have +encountered. If you are happy with your changes and willing to share those +changes to the upstream author, you are recommended to create a pull-request +on github. + +To create a pull-request, you first need to "fork" jsonlab on Github by +clicking on the "fork" button on top-right of jsonlab's github page. Once you forked +jsonlab to your own directory, you should then implement the changes in your +own fork. After thoroughly testing it and you are confident the modification +is complete and effective, you can then click on the "New pull request" +button, and on the left, select fangq/jsonlab as the "base". Then type +in the description of the changes. You are responsible to format the code +updates using the same convention (tab-width: 8, indentation: 4 spaces) as +the upstream code. + +We appreciate any suggestions and feedbacks from you. Please use the following +mailing list to report any questions you may have regarding JSONLab: + + https://github.com/fangq/jsonlab/issues + +(Subscription to the mailing list is needed in order to post messages). + + +========================== +Acknowledgement +========================== + +--------- +zlibdecode.m, zlibencode.m, gzipencode.m, gzipdecode.m, base64encode.m, base64decode.m +--------- + +* Author: Kota Yamaguchi +* URL: https://www.mathworks.com/matlabcentral/fileexchange/39526-byte-encoding-utilities +* License: BSD License, see below + +``` +Copyright (c) 2012, Kota Yamaguchi +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` \ No newline at end of file diff --git a/matlab/+jsonlab/README.txt b/matlab/+jsonlab/README.txt new file mode 100644 index 0000000..704519e --- /dev/null +++ b/matlab/+jsonlab/README.txt @@ -0,0 +1,565 @@ +=============================================================================== += JSONLab = += An open-source MATLAB/Octave JSON encoder and decoder = +=============================================================================== + +*Copyright (C) 2011-2019 Qianqian Fang +*License: BSD, License_BSD.txt for details +*Version: 1.9 (Magnus - alpha) + +------------------------------------------------------------------------------- + +Table of Content: + +0. What's New +I. Introduction +II. Installation +III.Using JSONLab +IV. Known Issues and TODOs +V. Contribution and feedback +V. Acknowledgement + +------------------------------------------------------------------------------- + +0. What's New + +JSONLab v1.9 is the alpha release of the next milestone - code named "Magnus". +Notable changes are summarized below, key features marked by *: + + 2019-05-06 [25ad795] unescape string in loadjson.m + 2019-05-04 [2e317c9] explain extra compression fields + 2019-05-02 [1b1be65] avoid side effect of removing singletarray + 2019-05-02*[8360fd1] support zmat based base64 encoding and decoding + 2019-05-01*[c797bb2] integrating zmat, for zlib/gzip data compression + 2019-04-29 [70551fe] remove warnings from matlab + 2019-04-28 [0d61c4b] complete data compression support, close #52 + 2019-04-27 [804115b] avoid typecast error + 2019-04-27 [c166aa7] change default compressarraysize to 100 + 2019-04-27*[3322f6f] major new feature: support array compression and decompression + 2019-03-13*[9c01046] support saving function handles, close #51 + 2019-03-13 [a8fde38] add option to parse string array or convert to char, close #50 + 2019-03-12 [ed2645e] treat string array as cell array in newer matlab + 2018-11-18 [c3eb021] allow saving uint64 integers in saveubjson, fix #49 + +The biggest change in this release, compared to v1.8 released in July 2018, +is the support of data compression via the 'Compression' option for both +savejson and saveubjson. Two compression methods are currently supported - +"zlib" and "gzip". The compression interfaces, zlibencode/zlibdecode/gzipencode/ +gzipdecode are modified from the "Byte Encoding Utilities" by Kota Yamaguchi [1], +which has built-in support for java-based compression in MATLAB (when jvm is +enabled). To support Octave, as well as MATLAB in "nojvm" mode, a mex-based +data compression/encoding toolbox, ZMat [2], written by Qianqian Fang, takes priority +over the java-based utilities, if installed. For savejson, a 'base64' encoding is +applied to convert the compressed binary stream into a string; 'base64' encoding +is not used in saveubjson. The encoding and restoration of the binary matlab arrays +are automatically handled in save*json/load*json round-trip conversions. + +To save matlab data with compression, one simply append 'Compression', 'method' pair +in the savejson/saveubjson call. For example + + jsonstr=savejson('',mydata,'compression','zlib'); + data=loadjson(jsonstr); + +In addition, the below features are added to JSONLab + +* save function handles +* support saving "string" class in MATLAB +* fix two bugs in saveubjson +* unescape strings in loadjson + + +* [1] https://www.mathworks.com/matlabcentral/fileexchange/39526-byte-encoding-utilities +* [2] http://github.com/fangq/zmat + +------------------------------------------------------------------------------- + +I. Introduction + +JSONLab is a free and open-source implementation of a JSON/UBJSON encoder +and a decoder in the native MATLAB language. It can be used to convert a MATLAB +data structure (array, struct, cell, struct array and cell array) into +JSON/UBJSON formatted strings, or to decode a JSON/UBJSON file into MATLAB +data structure. JSONLab supports both MATLAB and +[http://www.gnu.org/software/octave/ GNU Octave] (a free MATLAB clone). + +JSON ([http://www.json.org/ JavaScript Object Notation]) is a highly portable, +human-readable and "[http://en.wikipedia.org/wiki/JSON fat-free]" text format +to represent complex and hierarchical data. It is as powerful as +[http://en.wikipedia.org/wiki/XML XML], but less verbose. JSON format is widely +used for data-exchange in applications, and is essential for the wild success +of [http://en.wikipedia.org/wiki/Ajax_(programming) Ajax] and +[http://en.wikipedia.org/wiki/Web_2.0 Web2.0]. + +UBJSON ([JSON decoder, +and savejson.m -- a MATLAB->JSON encoder, for the text-based JSON, and +two equivallent functions -- loadubjson and saveubjson for the binary +JSON. The detailed help info for the four functions can be found below: + +=== loadjson.m === +
+  data=loadjson(fname,opt)
+     or
+  data=loadjson(fname,'param1',value1,'param2',value2,...)
+ 
+  parse a JSON (JavaScript Object Notation) file or string
+ 
+  authors:Qianqian Fang (q.fang  neu.edu)
+  created on 2011/09/09, including previous works from 
+ 
+          Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713
+             created on 2009/11/02
+          François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393
+             created on  2009/03/22
+          Joel Feenstra:
+          http://www.mathworks.com/matlabcentral/fileexchange/20565
+             created on 2008/07/03
+ 
+  $Id$
+ 
+  input:
+       fname: input file name, if fname contains "{}" or "[]", fname
+              will be interpreted as a JSON string
+       opt: a struct to store parsing options, opt can be replaced by 
+            a list of ('param',value) pairs - the param string is equivallent
+            to a field in opt. opt can have the following 
+            fields (first in [.|.] is the default)
+ 
+            opt.SimplifyCell [0|1]: if set to 1, loadjson will call cell2mat
+                          for each element of the JSON data, and group 
+                          arrays based on the cell2mat rules.
+            opt.FastArrayParser [1|0 or integer]: if set to 1, use a
+                          speed-optimized array parser when loading an 
+                          array object. The fast array parser may 
+                          collapse block arrays into a single large
+                          array similar to rules defined in cell2mat; 0 to 
+                          use a legacy parser; if set to a larger-than-1
+                          value, this option will specify the minimum
+                          dimension to enable the fast array parser. For
+                          example, if the input is a 3D array, setting
+                          FastArrayParser to 1 will return a 3D array;
+                          setting to 2 will return a cell array of 2D
+                          arrays; setting to 3 will return to a 2D cell
+                          array of 1D vectors; setting to 4 will return a
+                          3D cell array.
+            opt.ShowProgress [0|1]: if set to 1, loadjson displays a progress bar.
+            opt.ParseStringArray [0|1]: if set to 1, loadjson displays a progress bar.
+ 
+  output:
+       dat: a cell array, where {...} blocks are converted into cell arrays,
+            and [...] are converted to arrays
+ 
+  examples:
+       dat=loadjson('{"obj":{"string":"value","array":[1,2,3]}}')
+       dat=loadjson(['examples' filesep 'example1.json'])
+       dat=loadjson(['examples' filesep 'example1.json'],'SimplifyCell',1)
+ 
+  license:
+      BSD, see LICENSE_BSD.txt file for details 
+  
+ +=== savejson.m === + +
+  json=savejson(rootname,obj,filename)
+     or
+  json=savejson(rootname,obj,opt)
+  json=savejson(rootname,obj,'param1',value1,'param2',value2,...)
+ 
+  convert a MATLAB object (cell, struct or array) into a JSON (JavaScript
+  Object Notation) string
+ 
+  author: Qianqian Fang (q.fang  neu.edu)
+  created on 2011/09/09
+ 
+  $Id$
+ 
+  input:
+       rootname: the name of the root-object, when set to '', the root name
+         is ignored, however, when opt.ForceRootName is set to 1 (see below),
+         the MATLAB variable name will be used as the root name.
+       obj: a MATLAB object (array, cell, cell array, struct, struct array,
+       class instance).
+       filename: a string for the file name to save the output JSON data.
+       opt: a struct for additional options, ignore to use default values.
+         opt can have the following fields (first in [.|.] is the default)
+ 
+         opt.FileName [''|string]: a file name to save the output JSON data
+         opt.FloatFormat ['%.10g'|string]: format to show each numeric element
+                          of a 1D/2D array;
+         opt.ArrayIndent [1|0]: if 1, output explicit data array with
+                          precedent indentation; if 0, no indentation
+         opt.ArrayToStruct[0|1]: when set to 0, savejson outputs 1D/2D
+                          array in JSON array format; if sets to 1, an
+                          array will be shown as a struct with fields
+                          "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for
+                          sparse arrays, the non-zero elements will be
+                          saved to _ArrayData_ field in triplet-format i.e.
+                          (ix,iy,val) and "_ArrayIsSparse_" will be added
+                          with a value of 1; for a complex array, the 
+                          _ArrayData_ array will include two columns 
+                          (4 for sparse) to record the real and imaginary 
+                          parts, and also "_ArrayIsComplex_":1 is added. 
+         opt.ParseLogical [0|1]: if this is set to 1, logical array elem
+                          will use true/false rather than 1/0.
+         opt.SingletArray [0|1]: if this is set to 1, arrays with a single
+                          numerical element will be shown without a square
+                          bracket, unless it is the root object; if 0, square
+                          brackets are forced for any numerical arrays.
+         opt.SingletCell  [1|0]: if 1, always enclose a cell with "[]" 
+                          even it has only one element; if 0, brackets
+                          are ignored when a cell has only 1 element.
+         opt.ForceRootName [0|1]: when set to 1 and rootname is empty, savejson
+                          will use the name of the passed obj variable as the 
+                          root object name; if obj is an expression and 
+                          does not have a name, 'root' will be used; if this 
+                          is set to 0 and rootname is empty, the root level 
+                          will be merged down to the lower level.
+         opt.Inf ['"$1_Inf_"'|string]: a customized regular expression pattern
+                          to represent +/-Inf. The matched pattern is '([-+]*)Inf'
+                          and $1 represents the sign. For those who want to use
+                          1e999 to represent Inf, they can set opt.Inf to '$11e999'
+         opt.NaN ['"_NaN_"'|string]: a customized regular expression pattern
+                          to represent NaN
+         opt.JSONP [''|string]: to generate a JSONP output (JSON with padding),
+                          for example, if opt.JSONP='foo', the JSON data is
+                          wrapped inside a function call as 'foo(...);'
+         opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson 
+                          back to the string form
+         opt.SaveBinary [0|1]: 1 - save the JSON file in binary mode; 0 - text mode.
+         opt.Compact [0|1]: 1- out compact JSON format (remove all newlines and tabs)
+         opt.Compression  'zlib' or 'gzip': specify array compression
+                          method; currently only supports 'gzip' or 'zlib'. The
+                          data compression only applicable to numerical arrays 
+                          in 3D or higher dimensions, or when ArrayToStruct
+                          is 1 for 1D or 2D arrays. If one wants to
+                          compress a long string, one must convert
+                          it to uint8 or int8 array first. The compressed
+                          array uses three extra fields
+                          "_ArrayCompressionMethod_": the opt.Compression value. 
+                          "_ArrayCompressionSize_": a 1D interger array to
+                             store the pre-compressed (but post-processed)
+                             array dimensions, and 
+                          "_ArrayCompressedData_": the "base64" encoded
+                              compressed binary array data. 
+         opt.CompressArraySize [100|int]: only to compress an array if the total 
+                          element count is larger than this number.
+         opt can be replaced by a list of ('param',value) pairs. The param 
+         string is equivallent to a field in opt and is case sensitive.
+  output:
+       json: a string in the JSON format (see http://json.org)
+ 
+  examples:
+       jsonmesh=struct('MeshNode',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],... 
+                'MeshTetra',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],...
+                'MeshTri',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;...
+                           2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],...
+                'MeshCreator','FangQ','MeshTitle','T6 Cube',...
+                'SpecialData',[nan, inf, -inf]);
+       savejson('jmesh',jsonmesh)
+       savejson('',jsonmesh,'ArrayIndent',0,'FloatFormat','\t%.5g')
+ 
+  license:
+      BSD, see LICENSE_BSD.txt file for details
+ 
+ +=== loadubjson.m === + +
+  data=loadubjson(fname,opt)
+     or
+  data=loadubjson(fname,'param1',value1,'param2',value2,...)
+ 
+  parse a JSON (JavaScript Object Notation) file or string
+ 
+  authors:Qianqian Fang (q.fang  neu.edu)
+  created on 2013/08/01
+ 
+  $Id$
+ 
+  input:
+       fname: input file name, if fname contains "{}" or "[]", fname
+              will be interpreted as a UBJSON string
+       opt: a struct to store parsing options, opt can be replaced by 
+            a list of ('param',value) pairs - the param string is equivallent
+            to a field in opt. opt can have the following 
+            fields (first in [.|.] is the default)
+ 
+            opt.SimplifyCell [0|1]: if set to 1, loadubjson will call cell2mat
+                          for each element of the JSON data, and group 
+                          arrays based on the cell2mat rules.
+            opt.IntEndian [B|L]: specify the endianness of the integer fields
+                          in the UBJSON input data. B - Big-Endian format for 
+                          integers (as required in the UBJSON specification); 
+                          L - input integer fields are in Little-Endian order.
+            opt.NameIsString [0|1]: for UBJSON Specification Draft 8 or 
+                          earlier versions (JSONLab 1.0 final or earlier), 
+                          the "name" tag is treated as a string. To load 
+                          these UBJSON data, you need to manually set this 
+                          flag to 1.
+ 
+  output:
+       dat: a cell array, where {...} blocks are converted into cell arrays,
+            and [...] are converted to arrays
+ 
+  examples:
+       obj=struct('string','value','array',[1 2 3]);
+       ubjdata=saveubjson('obj',obj);
+       dat=loadubjson(ubjdata)
+       dat=loadubjson(['examples' filesep 'example1.ubj'])
+       dat=loadubjson(['examples' filesep 'example1.ubj'],'SimplifyCell',1)
+ 
+  license:
+      BSD, see LICENSE_BSD.txt file for details 
+
+ +=== saveubjson.m === + +
+  json=saveubjson(rootname,obj,filename)
+     or
+  json=saveubjson(rootname,obj,opt)
+  json=saveubjson(rootname,obj,'param1',value1,'param2',value2,...)
+ 
+  convert a MATLAB object (cell, struct or array) into a Universal 
+  Binary JSON (UBJSON) binary string
+ 
+  author: Qianqian Fang (q.fang  neu.edu)
+  created on 2013/08/17
+ 
+  $Id$
+ 
+  input:
+       rootname: the name of the root-object, when set to '', the root name
+         is ignored, however, when opt.ForceRootName is set to 1 (see below),
+         the MATLAB variable name will be used as the root name.
+       obj: a MATLAB object (array, cell, cell array, struct, struct array,
+       class instance)
+       filename: a string for the file name to save the output UBJSON data
+       opt: a struct for additional options, ignore to use default values.
+         opt can have the following fields (first in [.|.] is the default)
+ 
+         opt.FileName [''|string]: a file name to save the output JSON data
+         opt.ArrayToStruct[0|1]: when set to 0, saveubjson outputs 1D/2D
+                          array in JSON array format; if sets to 1, an
+                          array will be shown as a struct with fields
+                          "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for
+                          sparse arrays, the non-zero elements will be
+                          saved to _ArrayData_ field in triplet-format i.e.
+                          (ix,iy,val) and "_ArrayIsSparse_" will be added
+                          with a value of 1; for a complex array, the 
+                          _ArrayData_ array will include two columns 
+                          (4 for sparse) to record the real and imaginary 
+                          parts, and also "_ArrayIsComplex_":1 is added. 
+         opt.ParseLogical [1|0]: if this is set to 1, logical array elem
+                          will use true/false rather than 1/0.
+         opt.SingletArray [0|1]: if this is set to 1, arrays with a single
+                          numerical element will be shown without a square
+                          bracket, unless it is the root object; if 0, square
+                          brackets are forced for any numerical arrays.
+         opt.SingletCell  [1|0]: if 1, always enclose a cell with "[]" 
+                          even it has only one element; if 0, brackets
+                          are ignored when a cell has only 1 element.
+         opt.ForceRootName [0|1]: when set to 1 and rootname is empty, saveubjson
+                          will use the name of the passed obj variable as the 
+                          root object name; if obj is an expression and 
+                          does not have a name, 'root' will be used; if this 
+                          is set to 0 and rootname is empty, the root level 
+                          will be merged down to the lower level.
+         opt.JSONP [''|string]: to generate a JSONP output (JSON with padding),
+                          for example, if opt.JSON='foo', the JSON data is
+                          wrapped inside a function call as 'foo(...);'
+         opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson 
+                          back to the string form
+         opt.Compression  'zlib' or 'gzip': specify array compression
+                          method; currently only supports 'gzip' or 'zlib'. The
+                          data compression only applicable to numerical arrays 
+                          in 3D or higher dimensions, or when ArrayToStruct
+                          is 1 for 1D or 2D arrays. If one wants to
+                          compress a long string, one must convert
+                          it to uint8 or int8 array first. The compressed
+                          array uses three extra fields
+                          "_ArrayCompressionMethod_": the opt.Compression value. 
+                          "_ArrayCompressionSize_": a 1D interger array to
+                             store the pre-compressed (but post-processed)
+                             array dimensions, and 
+                          "_ArrayCompressedData_": the binary stream of
+                             the compressed binary array data WITHOUT
+                             'base64' encoding
+         opt.CompressArraySize [100|int]: only to compress an array if the total 
+                          element count is larger than this number.
+ 
+         opt can be replaced by a list of ('param',value) pairs. The param 
+         string is equivallent to a field in opt and is case sensitive.
+  output:
+       json: a binary string in the UBJSON format (see http://ubjson.org)
+ 
+  examples:
+       jsonmesh=struct('MeshNode',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],... 
+                'MeshTetra',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],...
+                'MeshTri',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;...
+                           2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],...
+                'MeshCreator','FangQ','MeshTitle','T6 Cube',...
+                'SpecialData',[nan, inf, -inf]);
+       saveubjson('jsonmesh',jsonmesh)
+       saveubjson('jsonmesh',jsonmesh,'meshdata.ubj')
+ 
+  license:
+      BSD, see LICENSE_BSD.txt file for details
+
+ + +=== examples === + +Under the "examples" folder, you can find several scripts to demonstrate the +basic utilities of JSONLab. Running the "demo_jsonlab_basic.m" script, you +will see the conversions from MATLAB data structure to JSON text and backward. +In "jsonlab_selftest.m", we load complex JSON files downloaded from the Internet +and validate the loadjson/savejson functions for regression testing purposes. +Similarly, a "demo_ubjson_basic.m" script is provided to test the saveubjson +and loadubjson functions for various matlab data structures. + +Please run these examples and understand how JSONLab works before you use +it to process your data. + +------------------------------------------------------------------------------- + +IV. Known Issues and TODOs + +JSONLab has several known limitations. We are striving to make it more general +and robust. Hopefully in a few future releases, the limitations become less. + +Here are the known issues: + +# 3D or higher dimensional cell/struct-arrays will be converted to 2D arrays; +# When processing names containing multi-byte characters, Octave and MATLAB \ +can give different field-names; you can use feature('DefaultCharacterSet','latin1') \ +in MATLAB to get consistant results +# savejson can not handle class and dataset. +# saveubjson converts a logical array into a uint8 ([U]) array +# an unofficial N-D array count syntax is implemented in saveubjson. We are \ +actively communicating with the UBJSON spec maintainer to investigate the \ +possibility of making it upstream +# loadubjson can not parse all UBJSON Specification (Draft 9) compliant \ +files, however, it can parse all UBJSON files produced by saveubjson. + +------------------------------------------------------------------------------- + +V. Contribution and feedback + +JSONLab is an open-source project. This means you can not only use it and modify +it as you wish, but also you can contribute your changes back to JSONLab so +that everyone else can enjoy the improvement. For anyone who want to contribute, +please download JSONLab source code from its source code repositories by using the +following command: + + git clone https://github.com/fangq/jsonlab.git jsonlab + +or browsing the github site at + + https://github.com/fangq/jsonlab + +Please report any bugs or issues to the below URL: + + https://github.com/fangq/jsonlab/issues + +Sometimes, you may find it is necessary to modify JSONLab to achieve your +goals, or attempt to modify JSONLab functions to fix a bug that you have +encountered. If you are happy with your changes and willing to share those +changes to the upstream author, you are recommended to create a pull-request +on github. + +To create a pull-request, you first need to "fork" jsonlab on Github by +clicking on the "fork" button on top-right of jsonlab's github page. Once you forked +jsonlab to your own directory, you should then implement the changes in your +own fork. After thoroughly testing it and you are confident the modification +is complete and effective, you can then click on the "New pull request" +button, and on the left, select fangq/jsonlab as the "base". Then type +in the description of the changes. You are responsible to format the code +updates using the same convention (tab-width: 8, indentation: 4 spaces) as +the upstream code. + +We appreciate any suggestions and feedbacks from you. Please use the following +mailing list to report any questions you may have regarding JSONLab: + + https://github.com/fangq/jsonlab/issues + +(Subscription to the mailing list is needed in order to post messages). + +------------------------------------------------------------------------------- + +V. Acknowledgement + +This toolbox contains modified functions from the below toolboxes: + +== zlibdecode.m, zlibencode.m, gzipencode.m, gzipdecode.m, base64encode.m, base64decode.m == + +* Author: Kota Yamaguchi +* URL:https://www.mathworks.com/matlabcentral/fileexchange/39526-byte-encoding-utilities +* License: BSD License, see below + +Copyright (c) 2012, Kota Yamaguchi +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/matlab/+jsonlab/base64decode.m b/matlab/+jsonlab/base64decode.m new file mode 100644 index 0000000..512949d --- /dev/null +++ b/matlab/+jsonlab/base64decode.m @@ -0,0 +1,39 @@ +function output = base64decode(input) +import jsonlab.* +%BASE64DECODE Decode Base64 string to a byte array. +% +% output = base64decode(input) +% +% The function takes a Base64 string INPUT and returns a uint8 array +% OUTPUT. JAVA must be running to use this function. The result is always +% given as a 1-by-N array, and doesn't retrieve the original dimensions. +% +% See also base64encode +% +% Copyright (c) 2012, Kota Yamaguchi +% URL: https://www.mathworks.com/matlabcentral/fileexchange/39526-byte-encoding-utilities +% License : BSD, see LICENSE_*.txt +% + +if(nargin==0) + error('you must provide at least 1 input'); +end +if(exist('zmat')==3) + output=zmat(uint8(input),0,'base64'); + return; +end +if(exist('OCTAVE_VERSION','builtin')) + len=rem(numel(input),8) + if(len) + input=[input(:)', repmat(sprintf('\0'),1,(8-len))]; + end + output = base64_decode(input); + return; +end +error(javachk('jvm')); +if ischar(input), input = uint8(input); end + +output = typecast(org.apache.commons.codec.binary.Base64.decodeBase64(input), 'uint8')'; + +end + diff --git a/matlab/+jsonlab/base64encode.m b/matlab/+jsonlab/base64encode.m new file mode 100644 index 0000000..2f21878 --- /dev/null +++ b/matlab/+jsonlab/base64encode.m @@ -0,0 +1,34 @@ +function output = base64encode(input) +import jsonlab.* +%BASE64ENCODE Encode a byte array using Base64 codec. +% +% output = base64encode(input) +% +% The function takes a char, int8, or uint8 array INPUT and returns Base64 +% encoded string OUTPUT. JAVA must be running to use this function. Note +% that encoding doesn't preserve input dimensions. +% +% See also base64decode +% +% Copyright (c) 2012, Kota Yamaguchi +% URL: https://www.mathworks.com/matlabcentral/fileexchange/39526-byte-encoding-utilities +% License : BSD, see LICENSE_*.txt +% + +if(nargin==0) + error('you must provide at least 1 input'); +end +if(exist('zmat')==3) + output=zmat(uint8(input),1,'base64'); + return; +end +if(exist('OCTAVE_VERSION','builtin')) + output = base64_encode(uint8(input)); + return; +end +error(javachk('jvm')); +if ischar(input), input = uint8(input); end + +output = char(org.apache.commons.codec.binary.Base64.encodeBase64Chunked(input))'; +output = regexprep(output,'\r',''); +end diff --git a/matlab/+jsonlab/examples/demo_jsonlab_basic.m b/matlab/+jsonlab/examples/demo_jsonlab_basic.m new file mode 100644 index 0000000..d44d4ff --- /dev/null +++ b/matlab/+jsonlab/examples/demo_jsonlab_basic.m @@ -0,0 +1,181 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Demonstration of Basic Utilities of JSONlab +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +rngstate = rand ('state'); +randseed=hex2dec('623F9A9E'); +clear data2json json2data + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a simple scalar value \n') +fprintf(1,'%%=================================================\n\n') + +data2json=pi +savejson('',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a complex number\n') +fprintf(1,'%%=================================================\n\n') + +clear i; +data2json=1+2*i +savejson('',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a complex matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=magic(6); +data2json=data2json(:,1:3)+data2json(:,4:6)*i +savejson('',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% MATLAB special constants\n') +fprintf(1,'%%=================================================\n\n') + +data2json=[NaN Inf -Inf] +savejson('specials',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a real sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sprand(10,10,0.1) +savejson('sparse',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a complex sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=data2json-data2json*i +savejson('complex_sparse',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an all-zero sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse(2,3); +savejson('all_zero_sparse',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an empty sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse([]); +savejson('empty_sparse',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an empty 0-by-0 real matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=[]; +savejson('empty_0by0_real',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an empty 0-by-3 real matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=zeros(0,3); +savejson('empty_0by3_real',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse real column vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse([0,3,0,1,4]'); +savejson('sparse_column_vector',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse complex column vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=data2json-1i*data2json; +savejson('complex_sparse_column_vector',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse real row vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse([0,3,0,1,4]); +savejson('sparse_row_vector',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse complex row vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=data2json-1i*data2json; +savejson('complex_sparse_row_vector',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a structure\n') +fprintf(1,'%%=================================================\n\n') + +data2json=struct('name','Think Different','year',1997,'magic',magic(3),... + 'misfits',[Inf,NaN],'embedded',struct('left',true,'right',false)) +savejson('astruct',data2json,struct('ParseLogical',1)) +json2data=loadjson(ans) +class(json2data.astruct.embedded.left) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a structure array\n') +fprintf(1,'%%=================================================\n\n') + +data2json=struct('name','Nexus Prime','rank',9); +data2json(2)=struct('name','Sentinel Prime','rank',9); +data2json(3)=struct('name','Optimus Prime','rank',9); +savejson('Supreme Commander',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a cell array\n') +fprintf(1,'%%=================================================\n\n') + +data2json=cell(3,1); +data2json{1}=struct('buzz',1.1,'rex',1.2,'bo',1.3,'hamm',2.0,'slink',2.1,'potato',2.2,... + 'woody',3.0,'sarge',3.1,'etch',4.0,'lenny',5.0,'squeeze',6.0,'wheezy',7.0); +data2json{2}=struct('Ubuntu',['Kubuntu';'Xubuntu';'Lubuntu']); +data2json{3}=[10.04,10.10,11.04,11.10] +savejson('debian',data2json,struct('FloatFormat','%.2f')) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% invalid field-name handling\n') +fprintf(1,'%%=================================================\n\n') + +json2data=loadjson('{"ValidName":1, "_InvalidName":2, ":Field:":3, "项目":"绝密"}') + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a 2D cell array\n') +fprintf(1,'%%=================================================\n\n') + +data2json={{1,{2,3}},{4,5},{6};{7},{8,9},{10}}; +savejson('data2json',data2json) +json2data=loadjson(ans) % only savejson works for cell arrays, loadjson has issues + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a 2D struct array\n') +fprintf(1,'%%=================================================\n\n') + +data2json=repmat(struct('idx',0,'data','structs'),[2,3]) +for i=1:6 + data2json(i).idx=i; +end +savejson('data2json',data2json) +json2data=loadjson(ans) + +rand ('state',rngstate); + diff --git a/matlab/+jsonlab/examples/demo_ubjson_basic.m b/matlab/+jsonlab/examples/demo_ubjson_basic.m new file mode 100644 index 0000000..0c35433 --- /dev/null +++ b/matlab/+jsonlab/examples/demo_ubjson_basic.m @@ -0,0 +1,180 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Demonstration of Basic Utilities of JSONlab +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +rngstate = rand ('state'); +randseed=hex2dec('623F9A9E'); +clear data2json json2data + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a simple scalar value \n') +fprintf(1,'%%=================================================\n\n') + +data2json=pi +saveubjson('',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a complex number\n') +fprintf(1,'%%=================================================\n\n') + +clear i; +data2json=1+2*i +saveubjson('',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a complex matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=magic(6); +data2json=data2json(:,1:3)+data2json(:,4:6)*i +saveubjson('',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% MATLAB special constants\n') +fprintf(1,'%%=================================================\n\n') + +data2json=[NaN Inf -Inf] +saveubjson('specials',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a real sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sprand(10,10,0.1) +saveubjson('sparse',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a complex sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=data2json-data2json*i +saveubjson('complex_sparse',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an all-zero sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse(2,3); +saveubjson('all_zero_sparse',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an empty sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse([]); +saveubjson('empty_sparse',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an empty 0-by-0 real matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=[]; +saveubjson('empty_0by0_real',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an empty 0-by-3 real matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=zeros(0,3); +saveubjson('empty_0by3_real',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse real column vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse([0,3,0,1,4]'); +saveubjson('sparse_column_vector',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse complex column vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=data2json-1i*data2json; +saveubjson('complex_sparse_column_vector',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse real row vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse([0,3,0,1,4]); +saveubjson('sparse_row_vector',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse complex row vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=data2json-1i*data2json; +saveubjson('complex_sparse_row_vector',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a structure\n') +fprintf(1,'%%=================================================\n\n') + +data2json=struct('name','Think Different','year',1997,'magic',magic(3),... + 'misfits',[Inf,NaN],'embedded',struct('left',true,'right',false)) +saveubjson('astruct',data2json,struct('ParseLogical',1)) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a structure array\n') +fprintf(1,'%%=================================================\n\n') + +data2json=struct('name','Nexus Prime','rank',9); +data2json(2)=struct('name','Sentinel Prime','rank',9); +data2json(3)=struct('name','Optimus Prime','rank',9); +saveubjson('Supreme Commander',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a cell array\n') +fprintf(1,'%%=================================================\n\n') + +data2json=cell(3,1); +data2json{1}=struct('buzz',1.1,'rex',1.2,'bo',1.3,'hamm',2.0,'slink',2.1,'potato',2.2,... + 'woody',3.0,'sarge',3.1,'etch',4.0,'lenny',5.0,'squeeze',6.0,'wheezy',7.0); +data2json{2}=struct('Ubuntu',['Kubuntu';'Xubuntu';'Lubuntu']); +data2json{3}=[10.04,10.10,11.04,11.10] +saveubjson('debian',data2json,struct('FloatFormat','%.2f')) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% invalid field-name handling\n') +fprintf(1,'%%=================================================\n\n') + +json2data=loadubjson(saveubjson('',loadjson('{"ValidName":1, "_InvalidName":2, ":Field:":3, "项目":"绝密"}'))) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a 2D cell array\n') +fprintf(1,'%%=================================================\n\n') + +data2json={{1,{2,3}},{4,5},{6};{7},{8,9},{10}}; +saveubjson('data2json',data2json) +json2data=loadubjson(ans) % only savejson works for cell arrays, loadjson has issues + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a 2D struct array\n') +fprintf(1,'%%=================================================\n\n') + +data2json=repmat(struct('idx',0,'data','structs'),[2,3]) +for i=1:6 + data2json(i).idx=i; +end +saveubjson('data2json',data2json) +json2data=loadubjson(ans) + +rand ('state',rngstate); + diff --git a/matlab/+jsonlab/examples/example1.json b/matlab/+jsonlab/examples/example1.json new file mode 100644 index 0000000..be0993e --- /dev/null +++ b/matlab/+jsonlab/examples/example1.json @@ -0,0 +1,23 @@ + { + "firstName": "John", + "lastName": "Smith", + "age": 25, + "address": + { + "streetAddress": "21 2nd Street", + "city": "New York", + "state": "NY", + "postalCode": "10021" + }, + "phoneNumber": + [ + { + "type": "home", + "number": "212 555-1234" + }, + { + "type": "fax", + "number": "646 555-4567" + } + ] + } diff --git a/matlab/+jsonlab/examples/example2.json b/matlab/+jsonlab/examples/example2.json new file mode 100644 index 0000000..eacfbf5 --- /dev/null +++ b/matlab/+jsonlab/examples/example2.json @@ -0,0 +1,22 @@ +{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} diff --git a/matlab/+jsonlab/examples/example3.json b/matlab/+jsonlab/examples/example3.json new file mode 100644 index 0000000..f52661b --- /dev/null +++ b/matlab/+jsonlab/examples/example3.json @@ -0,0 +1,11 @@ +{"menu": { + "id": "file", + "value": "_&File", + "popup": { + "menuitem": [ + {"value": "_&New", "onclick": "CreateNewDoc(\"'\\\"Untitled\\\"'\")"}, + {"value": "_&Open", "onclick": "OpenDoc()"}, + {"value": "_&Close", "onclick": "CloseDoc()"} + ] + } +}} diff --git a/matlab/+jsonlab/examples/example4.json b/matlab/+jsonlab/examples/example4.json new file mode 100644 index 0000000..1872c12 --- /dev/null +++ b/matlab/+jsonlab/examples/example4.json @@ -0,0 +1,35 @@ +[ + { + "sample" : { + "rho" : 1 + } + }, + { + "sample" : { + "rho" : 2 + } + }, + [ + { + "_ArrayType_" : "double", + "_ArraySize_" : [1,2], + "_ArrayData_" : [1,0] + }, + { + "_ArrayType_" : "double", + "_ArraySize_" : [1,2], + "_ArrayData_" : [1,1] + }, + { + "_ArrayType_" : "double", + "_ArraySize_" : [1,2], + "_ArrayData_" : [1,2] + } + ], + [ + "Paper", + "Scissors", + "Stone" + ], + ["a", "b\\", "c\"","d\\\"","e\"[","f\\\"[","g[\\","h[\\\""] +] diff --git a/matlab/+jsonlab/examples/jsonlab_basictest.matlab b/matlab/+jsonlab/examples/jsonlab_basictest.matlab new file mode 100644 index 0000000..4bd16fa --- /dev/null +++ b/matlab/+jsonlab/examples/jsonlab_basictest.matlab @@ -0,0 +1,671 @@ + + < M A T L A B (R) > + Copyright 1984-2010 The MathWorks, Inc. + Version 7.11.0.584 (R2010b) 64-bit (glnxa64) + August 16, 2010 + + + To get started, type one of these: helpwin, helpdesk, or demo. + For product information, visit www.mathworks.com. + +>> >> >> >> >> >> >> >> >> +%================================================= +>> % a simple scalar value +>> %================================================= + +>> >> +data2json = + + 3.1416 + +>> +ans = + +[3.141592654] + + +>> +json2data = + + 3.1416 + +>> >> +%================================================= +>> % a complex number +>> %================================================= + +>> >> >> +data2json = + + 1.0000 + 2.0000i + +>> +ans = + +{ + "_ArrayType_": "double", + "_ArraySize_": [1,1], + "_ArrayIsComplex_": 1, + "_ArrayData_": [1,2] +} + + +>> +json2data = + + 1.0000 + 2.0000i + +>> >> +%================================================= +>> % a complex matrix +>> %================================================= + +>> >> >> +data2json = + + 35.0000 +26.0000i 1.0000 +19.0000i 6.0000 +24.0000i + 3.0000 +21.0000i 32.0000 +23.0000i 7.0000 +25.0000i + 31.0000 +22.0000i 9.0000 +27.0000i 2.0000 +20.0000i + 8.0000 +17.0000i 28.0000 +10.0000i 33.0000 +15.0000i + 30.0000 +12.0000i 5.0000 +14.0000i 34.0000 +16.0000i + 4.0000 +13.0000i 36.0000 +18.0000i 29.0000 +11.0000i + +>> +ans = + +{ + "_ArrayType_": "double", + "_ArraySize_": [6,3], + "_ArrayIsComplex_": 1, + "_ArrayData_": [ + [35,26], + [3,21], + [31,22], + [8,17], + [30,12], + [4,13], + [1,19], + [32,23], + [9,27], + [28,10], + [5,14], + [36,18], + [6,24], + [7,25], + [2,20], + [33,15], + [34,16], + [29,11] + ] +} + + +>> +json2data = + + 35.0000 +26.0000i 1.0000 +19.0000i 6.0000 +24.0000i + 3.0000 +21.0000i 32.0000 +23.0000i 7.0000 +25.0000i + 31.0000 +22.0000i 9.0000 +27.0000i 2.0000 +20.0000i + 8.0000 +17.0000i 28.0000 +10.0000i 33.0000 +15.0000i + 30.0000 +12.0000i 5.0000 +14.0000i 34.0000 +16.0000i + 4.0000 +13.0000i 36.0000 +18.0000i 29.0000 +11.0000i + +>> >> +%================================================= +>> % MATLAB special constants +>> %================================================= + +>> >> +data2json = + + NaN Inf -Inf + +>> +ans = + +{ + "specials": ["_NaN_","_Inf_","-_Inf_"] +} + + +>> +json2data = + + specials: [NaN Inf -Inf] + +>> >> +%================================================= +>> % a real sparse matrix +>> %================================================= + +>> >> +data2json = + + (1,2) 0.6557 + (9,2) 0.7577 + (3,5) 0.8491 + (10,5) 0.7431 + (10,8) 0.3922 + (7,9) 0.6787 + (2,10) 0.0357 + (6,10) 0.9340 + (10,10) 0.6555 + +>> +ans = + +{ + "sparse": { + "_ArrayType_": "double", + "_ArraySize_": [10,10], + "_ArrayIsSparse_": 1, + "_ArrayData_": [ + [1,2,0.6557406992], + [9,2,0.7577401306], + [3,5,0.8491293059], + [10,5,0.7431324681], + [10,8,0.3922270195], + [7,9,0.6787351549], + [2,10,0.03571167857], + [6,10,0.9339932478], + [10,10,0.6554778902] + ] + } +} + + +>> +json2data = + + sparse: [10x10 double] + +>> >> +%================================================= +>> % a complex sparse matrix +>> %================================================= + +>> >> +data2json = + + (1,2) 0.6557 - 0.6557i + (9,2) 0.7577 - 0.7577i + (3,5) 0.8491 - 0.8491i + (10,5) 0.7431 - 0.7431i + (10,8) 0.3922 - 0.3922i + (7,9) 0.6787 - 0.6787i + (2,10) 0.0357 - 0.0357i + (6,10) 0.9340 - 0.9340i + (10,10) 0.6555 - 0.6555i + +>> +ans = + +{ + "complex_sparse": { + "_ArrayType_": "double", + "_ArraySize_": [10,10], + "_ArrayIsComplex_": 1, + "_ArrayIsSparse_": 1, + "_ArrayData_": [ + [1,2,0.6557406992,-0.6557406992], + [9,2,0.7577401306,-0.7577401306], + [3,5,0.8491293059,-0.8491293059], + [10,5,0.7431324681,-0.7431324681], + [10,8,0.3922270195,-0.3922270195], + [7,9,0.6787351549,-0.6787351549], + [2,10,0.03571167857,-0.03571167857], + [6,10,0.9339932478,-0.9339932478], + [10,10,0.6554778902,-0.6554778902] + ] + } +} + + +>> +json2data = + + complex_sparse: [10x10 double] + +>> >> +%================================================= +>> % an all-zero sparse matrix +>> %================================================= + +>> >> >> +ans = + +{ + "all_zero_sparse": { + "_ArrayType_": "double", + "_ArraySize_": [2,3], + "_ArrayIsSparse_": 1, + "_ArrayData_": null + } +} + + +>> +json2data = + + all_zero_sparse: [2x3 double] + +>> >> +%================================================= +>> % an empty sparse matrix +>> %================================================= + +>> >> >> +ans = + +{ + "empty_sparse": { + "_ArrayType_": "double", + "_ArraySize_": [0,0], + "_ArrayIsSparse_": 1, + "_ArrayData_": null + } +} + + +>> +json2data = + + empty_sparse: [] + +>> >> +%================================================= +>> % an empty 0-by-0 real matrix +>> %================================================= + +>> >> >> +ans = + +{ + "empty_0by0_real": null +} + + +>> +json2data = + + empty_0by0_real: [] + +>> >> +%================================================= +>> % an empty 0-by-3 real matrix +>> %================================================= + +>> >> >> +ans = + +{ + "empty_0by3_real": { + "_ArrayType_": "double", + "_ArraySize_": [0,3], + "_ArrayData_": null + } +} + + +>> +json2data = + + empty_0by3_real: [0x3 double] + +>> >> +%================================================= +>> % a sparse real column vector +>> %================================================= + +>> >> >> +ans = + +{ + "sparse_column_vector": { + "_ArrayType_": "double", + "_ArraySize_": [5,1], + "_ArrayIsSparse_": 1, + "_ArrayData_": [ + [2,3], + [4,1], + [5,4] + ] + } +} + + +>> +json2data = + + sparse_column_vector: [5x1 double] + +>> >> +%================================================= +>> % a sparse complex column vector +>> %================================================= + +>> >> >> +ans = + +{ + "complex_sparse_column_vector": { + "_ArrayType_": "double", + "_ArraySize_": [5,1], + "_ArrayIsComplex_": 1, + "_ArrayIsSparse_": 1, + "_ArrayData_": [ + [2,3,-3], + [4,1,-1], + [5,4,-4] + ] + } +} + + +>> +json2data = + + complex_sparse_column_vector: [5x1 double] + +>> >> +%================================================= +>> % a sparse real row vector +>> %================================================= + +>> >> >> +ans = + +{ + "sparse_row_vector": { + "_ArrayType_": "double", + "_ArraySize_": [1,5], + "_ArrayIsSparse_": 1, + "_ArrayData_": [ + [2,3], + [4,1], + [5,4] + ] + } +} + + +>> +json2data = + + sparse_row_vector: [0 3 0 1 4] + +>> >> +%================================================= +>> % a sparse complex row vector +>> %================================================= + +>> >> >> +ans = + +{ + "complex_sparse_row_vector": { + "_ArrayType_": "double", + "_ArraySize_": [1,5], + "_ArrayIsComplex_": 1, + "_ArrayIsSparse_": 1, + "_ArrayData_": [ + [2,3,-3], + [4,1,-1], + [5,4,-4] + ] + } +} + + +>> +json2data = + + complex_sparse_row_vector: [1x5 double] + +>> >> +%================================================= +>> % a structure +>> %================================================= + +>> >> +data2json = + + name: 'Think Different' + year: 1997 + magic: [3x3 double] + misfits: [Inf NaN] + embedded: [1x1 struct] + +>> +ans = + +{ + "astruct": { + "name": "Think Different", + "year": 1997, + "magic": [ + [8,1,6], + [3,5,7], + [4,9,2] + ], + "misfits": ["_Inf_","_NaN_"], + "embedded": { + "left": true, + "right": false + } + } +} + + +>> +json2data = + + astruct: [1x1 struct] + +>> +ans = + +logical + +>> >> +%================================================= +>> % a structure array +>> %================================================= + +>> >> >> >> >> +ans = + +{ + "Supreme Commander": [ + { + "name": "Nexus Prime", + "rank": 9 + }, + { + "name": "Sentinel Prime", + "rank": 9 + }, + { + "name": "Optimus Prime", + "rank": 9 + } + ] +} + + +>> +json2data = + + Supreme_0x20_Commander: {[1x1 struct] [1x1 struct] [1x1 struct]} + +>> >> +%================================================= +>> % a cell array +>> %================================================= + +>> >> >> >> >> +data2json = + + [1x1 struct] + [1x1 struct] + [1x4 double] + +>> +ans = + +{ + "debian": [ + [ + { + "buzz": 1.10, + "rex": 1.20, + "bo": 1.30, + "hamm": 2.00, + "slink": 2.10, + "potato": 2.20, + "woody": 3.00, + "sarge": 3.10, + "etch": 4.00, + "lenny": 5.00, + "squeeze": 6.00, + "wheezy": 7.00 + } + ], + [ + { + "Ubuntu": [ + "Kubuntu", + "Xubuntu", + "Lubuntu" + ] + } + ], + [ + [10.04,10.10,11.04,11.10] + ] + ] +} + + +>> +json2data = + + debian: {{1x1 cell} {1x1 cell} [10.0400 10.1000 11.0400 11.1000]} + +>> >> +%================================================= +>> % invalid field-name handling +>> %================================================= + +>> >> +json2data = + + ValidName: 1 + x0x5F_InvalidName: 2 + x0x3A_Field_0x3A_: 3 + x0xE9A1B9__0xE79BAE_: '绝密' + +>> >> +%================================================= +>> % a 2D cell array +>> %================================================= + +>> >> >> +ans = + +{ + "data2json": [ + [ + [ + 1, + [ + 2, + 3 + ] + ], + [ + 4, + 5 + ], + [ + 6 + ] + ], + [ + [ + 7 + ], + [ + 8, + 9 + ], + [ + 10 + ] + ] + ] +} + + +>> +json2data = + + data2json: {{3x1 cell} {3x1 cell}} + +>> >> +%================================================= +>> % a 2D struct array +>> %================================================= + +>> >> +data2json = + +2x3 struct array with fields: + idx + data + +>> >> +ans = + +{ + "data2json": [ + [ + { + "idx": 1, + "data": "structs" + }, + { + "idx": 2, + "data": "structs" + } + ], + [ + { + "idx": 3, + "data": "structs" + }, + { + "idx": 4, + "data": "structs" + } + ], + [ + { + "idx": 5, + "data": "structs" + }, + { + "idx": 6, + "data": "structs" + } + ] + ] +} + + +>> +json2data = + + data2json: {{1x2 cell} {1x2 cell} {1x2 cell}} + +>> >> >> >> \ No newline at end of file diff --git a/matlab/+jsonlab/examples/jsonlab_selftest.m b/matlab/+jsonlab/examples/jsonlab_selftest.m new file mode 100644 index 0000000..27aee24 --- /dev/null +++ b/matlab/+jsonlab/examples/jsonlab_selftest.m @@ -0,0 +1,27 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Regression Test Unit of loadjson and savejson +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +for i=1:4 + fname=sprintf('example%d.json',i); + if(exist(fname,'file')==0) break; end + fprintf(1,'===============================================\n>> %s\n',fname); + json=savejson('data',loadjson(fname)); + fprintf(1,'%s\n',json); + fprintf(1,'%s\n',savejson('data',loadjson(fname),'Compact',1)); + data=loadjson(json); + savejson('data',data,'selftest.json'); + data=loadjson('selftest.json'); +end + +for i=1:4 + fname=sprintf('example%d.json',i); + if(exist(fname,'file')==0) break; end + fprintf(1,'===============================================\n>> %s\n',fname); + json=saveubjson('data',loadjson(fname)); + fprintf(1,'%s\n',json); + data=loadubjson(json); + savejson('',data); + saveubjson('data',data,'selftest.ubj'); + data=loadubjson('selftest.ubj'); +end diff --git a/matlab/+jsonlab/examples/jsonlab_selftest.matlab b/matlab/+jsonlab/examples/jsonlab_selftest.matlab new file mode 100644 index 0000000..ac652c5 --- /dev/null +++ b/matlab/+jsonlab/examples/jsonlab_selftest.matlab @@ -0,0 +1,154 @@ + + < M A T L A B (R) > + Copyright 1984-2010 The MathWorks, Inc. + Version 7.11.0.584 (R2010b) 64-bit (glnxa64) + August 16, 2010 + + + To get started, type one of these: helpwin, helpdesk, or demo. + For product information, visit www.mathworks.com. + +>> >> >> >> >> =============================================== +>> example1.json +{ + "data": { + "firstName": "John", + "lastName": "Smith", + "age": 25, + "address": { + "streetAddress": "21 2nd Street", + "city": "New York", + "state": "NY", + "postalCode": "10021" + }, + "phoneNumber": [ + { + "type": "home", + "number": "212 555-1234" + }, + { + "type": "fax", + "number": "646 555-4567" + } + ] + } +} + +{"data": {"firstName": "John","lastName": "Smith","age": 25,"address": {"streetAddress": "21 2nd Street","city": "New York","state": "NY","postalCode": "10021"},"phoneNumber": [{"type": "home","number": "212 555-1234"},{"type": "fax","number": "646 555-4567"}]}} + +=============================================== +>> example2.json +{ + "data": { + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": [ + "GML", + "XML" + ] + }, + "GlossSee": "markup" + } + } + } + } + } +} + +{"data": {"glossary": {"title": "example glossary","GlossDiv": {"title": "S","GlossList": {"GlossEntry": {"ID": "SGML","SortAs": "SGML","GlossTerm": "Standard Generalized Markup Language","Acronym": "SGML","Abbrev": "ISO 8879:1986","GlossDef": {"para": "A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso": ["GML","XML"]},"GlossSee": "markup"}}}}}} + +=============================================== +>> example3.json +{ + "data": { + "menu": { + "id": "file", + "value": "_&File", + "popup": { + "menuitem": [ + { + "value": "_&New", + "onclick": "CreateNewDoc(\"'\\\"Untitled\\\"'\")" + }, + { + "value": "_&Open", + "onclick": "OpenDoc()" + }, + { + "value": "_&Close", + "onclick": "CloseDoc()" + } + ] + } + } + } +} + +{"data": {"menu": {"id": "file","value": "_&File","popup": {"menuitem": [{"value": "_&New","onclick": "CreateNewDoc(\"'\\\"Untitled\\\"'\")"},{"value": "_&Open","onclick": "OpenDoc()"},{"value": "_&Close","onclick": "CloseDoc()"}]}}}} + +=============================================== +>> example4.json +{ + "data": [ + { + "sample": { + "rho": 1 + } + }, + { + "sample": { + "rho": 2 + } + }, + [ + [1,0], + [1,1], + [1,2] + ], + [ + "Paper", + "Scissors", + "Stone" + ], + [ + "a", + "b\\", + "c\"", + "d\\\"", + "e\"[", + "f\\\"[", + "g[\\", + "h[\\\"" + ] + ] +} + +{"data": [{"sample": {"rho": 1}},{"sample": {"rho": 2}},[[1,0],[1,1],[1,2]],["Paper","Scissors","Stone"],["a","b\\","c\"","d\\\"","e\"[","f\\\"[","g[\\","h[\\\""]]} + +>> >> =============================================== +>> example1.json +{Udata{U firstNameSUJohnUlastNameSUSmithUageiUaddress{U streetAddressSU 21 2nd StreetUcitySUNew YorkUstateSUNYU +postalCodeSU10021}U phoneNumber[{UtypeSUhomeUnumberSU 212 555-1234}{UtypeSUfaxUnumberSU 646 555-4567}]}} +=============================================== +>> example2.json +{Udata{Uglossary{UtitleSUexample glossaryUGlossDiv{UtitleCSU GlossList{U +GlossEntry{UIDSUSGMLUSortAsSUSGMLU GlossTermSU$Standard Generalized Markup LanguageUAcronymSUSGMLUAbbrevSU ISO 8879:1986UGlossDef{UparaSUHA meta-markup language, used to create markup languages such as DocBook.U GlossSeeAlso[SUGMLSUXML]}UGlossSeeSUmarkup}}}}}} +=============================================== +>> example3.json +{Udata{Umenu{UidSUfileUvalueSU_&FileUpopup{Umenuitem[{UvalueSU_&NewUonclickSUCreateNewDoc("'\"Untitled\"'")}{UvalueSU_&OpenUonclickSU OpenDoc()}{UvalueSU_&CloseUonclickSU +CloseDoc()}]}}}} +=============================================== +>> example4.json +{Udata[{Usample{Urhoi}}{Usample{Urhoi}}[[$i#U[$i#U[$i#U][SUPaperSUScissorsSUStone][CaSUb\SUc"SUd\"SUe"[SUf\"[SUg[\SUh[\"]]} +>> \ No newline at end of file diff --git a/matlab/+jsonlab/examples/jsonlab_speedtest.m b/matlab/+jsonlab/examples/jsonlab_speedtest.m new file mode 100644 index 0000000..4990fba --- /dev/null +++ b/matlab/+jsonlab/examples/jsonlab_speedtest.m @@ -0,0 +1,21 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Benchmarking processing speed of savejson and loadjson +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +datalen=[1e3 1e4 1e5 1e6]; +len=length(datalen); +tsave=zeros(len,1); +tload=zeros(len,1); +for i=1:len + tic; + json=savejson('data',struct('d1',rand(datalen(i),3),'d2',rand(datalen(i),3)>0.5)); + tsave(i)=toc; + data=loadjson(json); + tload(i)=toc-tsave(i); + fprintf(1,'matrix size: %d\n',datalen(i)); +end + +loglog(datalen,tsave,'o-',datalen,tload,'r*-'); +legend('savejson runtime (s)','loadjson runtime (s)'); +xlabel('array size'); +ylabel('running time (s)'); diff --git a/matlab/+jsonlab/examples/jsonlab_ubjson_basictest.matlab b/matlab/+jsonlab/examples/jsonlab_ubjson_basictest.matlab new file mode 100644 index 0000000..e16806c --- /dev/null +++ b/matlab/+jsonlab/examples/jsonlab_ubjson_basictest.matlab @@ -0,0 +1,394 @@ + + < M A T L A B (R) > + Copyright 1984-2010 The MathWorks, Inc. + Version 7.11.0.584 (R2010b) 64-bit (glnxa64) + August 16, 2010 + + + To get started, type one of these: helpwin, helpdesk, or demo. + For product information, visit www.mathworks.com. + +>> >> >> >> >> >> >> >> >> +%================================================= +>> % a simple scalar value +>> %================================================= + +>> >> +data2json = + + 3.1416 + +>> +ans = + +[D@ !ûTD-] + +>> +json2data = + + [3.1416] + +>> >> +%================================================= +>> % a complex number +>> %================================================= + +>> >> >> +data2json = + + 1.0000 + 2.0000i + +>> +ans = + +{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsComplex_TU _ArrayData_[$i#U} + +>> +json2data = + + 1.0000 + 2.0000i + +>> >> +%================================================= +>> % a complex matrix +>> %================================================= + +>> >> >> +data2json = + + 35.0000 +26.0000i 1.0000 +19.0000i 6.0000 +24.0000i + 3.0000 +21.0000i 32.0000 +23.0000i 7.0000 +25.0000i + 31.0000 +22.0000i 9.0000 +27.0000i 2.0000 +20.0000i + 8.0000 +17.0000i 28.0000 +10.0000i 33.0000 +15.0000i + 30.0000 +12.0000i 5.0000 +14.0000i 34.0000 +16.0000i + 4.0000 +13.0000i 36.0000 +18.0000i 29.0000 +11.0000i + +>> +ans = + +{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsComplex_TU _ArrayData_[$i#[$U#U# $!"  + } + +>> +json2data = + + 35.0000 +26.0000i 1.0000 +19.0000i 6.0000 +24.0000i + 3.0000 +21.0000i 32.0000 +23.0000i 7.0000 +25.0000i + 31.0000 +22.0000i 9.0000 +27.0000i 2.0000 +20.0000i + 8.0000 +17.0000i 28.0000 +10.0000i 33.0000 +15.0000i + 30.0000 +12.0000i 5.0000 +14.0000i 34.0000 +16.0000i + 4.0000 +13.0000i 36.0000 +18.0000i 29.0000 +11.0000i + +>> >> +%================================================= +>> % MATLAB special constants +>> %================================================= + +>> >> +data2json = + + NaN Inf -Inf + +>> +ans = + +{Uspecials[$D#Uÿø ð ÿð } + +>> +json2data = + + specials: [NaN Inf -Inf] + +>> >> +%================================================= +>> % a real sparse matrix +>> %================================================= + +>> >> +data2json = + + (1,2) 0.6557 + (9,2) 0.7577 + (3,5) 0.8491 + (10,5) 0.7431 + (10,8) 0.3922 + (7,9) 0.6787 + (2,10) 0.0357 + (6,10) 0.9340 + (10,10) 0.6555 + +>> +ans = + +{Usparse{U _ArrayType_SUdoubleU _ArraySize_[$U#U + +U_ArrayIsSparse_TU _ArrayData_[$D#[$U#U ?ð @" @ @$ @$ @ @ @ @$ @ @ @ @ @ @" @$ @$ @$ ?äûÓë12?è?h:öl;?ë,8Ù±?çǽ½æ'#?Ù?[`o€?å¸2ɘNé?¢HÍpà?íãEι¶P?äù¬Ä² ¶}} + +>> +json2data = + + sparse: [10x10 double] + +>> >> +%================================================= +>> % a complex sparse matrix +>> %================================================= + +>> >> +data2json = + + (1,2) 0.6557 - 0.6557i + (9,2) 0.7577 - 0.7577i + (3,5) 0.8491 - 0.8491i + (10,5) 0.7431 - 0.7431i + (10,8) 0.3922 - 0.3922i + (7,9) 0.6787 - 0.6787i + (2,10) 0.0357 - 0.0357i + (6,10) 0.9340 - 0.9340i + (10,10) 0.6555 - 0.6555i + +>> +ans = + +{Ucomplex_sparse{U _ArrayType_SUdoubleU _ArraySize_[$U#U + +U_ArrayIsComplex_TU_ArrayIsSparse_TU _ArrayData_[$D#[$U#U ?ð @" @ @$ @$ @ @ @ @$ @ @ @ @ @ @" @$ @$ @$ ?äûÓë12?è?h:öl;?ë,8Ù±?çǽ½æ'#?Ù?[`o€?å¸2ɘNé?¢HÍpà?íãEι¶P?äù¬Ä² ¶¿äûÓë12¿è?h:öl;¿ë,8Ù±¿çǽ½æ'#¿Ù?[`o€¿å¸2ɘNé¿¢HÍpà¿íãEι¶P¿äù¬Ä² ¶}} + +>> +json2data = + + complex_sparse: [10x10 double] + +>> >> +%================================================= +>> % an all-zero sparse matrix +>> %================================================= + +>> >> >> +ans = + +{Uall_zero_sparse{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsSparse_TU _ArrayData_Z}} + +>> +json2data = + + all_zero_sparse: [2x3 double] + +>> >> +%================================================= +>> % an empty sparse matrix +>> %================================================= + +>> >> >> +ans = + +{U empty_sparseZ} + +>> +json2data = + + empty_sparse: [] + +>> >> +%================================================= +>> % an empty 0-by-0 real matrix +>> %================================================= + +>> >> >> +ans = + +{Uempty_0by0_realZ} + +>> +json2data = + + empty_0by0_real: [] + +>> >> +%================================================= +>> % an empty 0-by-3 real matrix +>> %================================================= + +>> >> >> +ans = + +{Uempty_0by3_realZ} + +>> +json2data = + + empty_0by3_real: [] + +>> >> +%================================================= +>> % a sparse real column vector +>> %================================================= + +>> >> >> +ans = + +{Usparse_column_vector{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsSparse_TU _ArrayData_[$i#[$U#U}} + +>> +json2data = + + sparse_column_vector: [5x1 double] + +>> >> +%================================================= +>> % a sparse complex column vector +>> %================================================= + +>> >> >> +ans = + +{Ucomplex_sparse_column_vector{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsComplex_TU_ArrayIsSparse_TU _ArrayData_[$i#[$U#Uýÿü}} + +>> +json2data = + + complex_sparse_column_vector: [5x1 double] + +>> >> +%================================================= +>> % a sparse real row vector +>> %================================================= + +>> >> >> +ans = + +{Usparse_row_vector{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsSparse_TU _ArrayData_[$i#[$U#U}} + +>> +json2data = + + sparse_row_vector: [0 3 0 1 4] + +>> >> +%================================================= +>> % a sparse complex row vector +>> %================================================= + +>> >> >> +ans = + +{Ucomplex_sparse_row_vector{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsComplex_TU_ArrayIsSparse_TU _ArrayData_[$i#[$U#Uýÿü}} + +>> +json2data = + + complex_sparse_row_vector: [1x5 double] + +>> >> +%================================================= +>> % a structure +>> %================================================= + +>> >> +data2json = + + name: 'Think Different' + year: 1997 + magic: [3x3 double] + misfits: [Inf NaN] + embedded: [1x1 struct] + +>> +ans = + +{Uastruct{UnameSUThink DifferentUyearIÍUmagic[$i#[$U#U Umisfits[$D#Uð ÿø Uembedded{UleftTUrightF}}} + +>> +json2data = + + astruct: [1x1 struct] + +>> >> +%================================================= +>> % a structure array +>> %================================================= + +>> >> >> >> >> +ans = + +{USupreme Commander[{UnameSU Nexus PrimeUranki }{UnameSUSentinel PrimeUranki }{UnameSU Optimus PrimeUranki }]} + +>> +json2data = + + Supreme_0x20_Commander: {[1x1 struct] [1x1 struct] [1x1 struct]} + +>> >> +%================================================= +>> % a cell array +>> %================================================= + +>> >> >> >> >> +data2json = + + [1x1 struct] + [1x1 struct] + [1x4 double] + +>> +ans = + +{Udebian[[{UbuzzD?ñ™™™™™šUrexD?ó333333UboD?ôÌÌÌÌÌÍUhammiUslinkD@ ÌÌÌÌÌÍUpotatoD@™™™™™šUwoodyiUsargeD@ÌÌÌÌÌÍUetchiUlennyiUsqueezeiUwheezyi}{UUbuntu[SUKubuntuSUXubuntuSULubuntu]}[$D#U@$záG®@$333333@&záG®@&333333]]} + +>> +json2data = + + debian: {{1x3 cell}} + +>> >> +%================================================= +>> % invalid field-name handling +>> %================================================= + +>> >> +json2data = + + ValidName: 1 + x0x5F_InvalidName: 2 + x0x3A_Field_0x3A_: 3 + x0xEFBFBD__0xEFBFBD_: '绝密' + +>> >> +%================================================= +>> % a 2D cell array +>> %================================================= + +>> >> >> +ans = + +{U data2json[[[[i][[i][i]]][[i]]][[[i][i]][[i][i ]]][[[i]][[i +]]]]} + +>> +json2data = + + data2json: {{1x2 cell} {1x2 cell} {1x2 cell}} + +>> >> +%================================================= +>> % a 2D struct array +>> %================================================= + +>> >> +data2json = + +2x3 struct array with fields: + idx + data + +>> >> +ans = + +{U data2json[[{UidxiUdataSUstructs}{UidxiUdataSUstructs}][{UidxiUdataSUstructs}{UidxiUdataSUstructs}][{UidxiUdataSUstructs}{UidxiUdataSUstructs}]]} + +>> +json2data = + + data2json: {{1x2 cell} {1x2 cell} {1x2 cell}} + +>> >> >> >> \ No newline at end of file diff --git a/matlab/+jsonlab/gzipdecode.m b/matlab/+jsonlab/gzipdecode.m new file mode 100644 index 0000000..7067909 --- /dev/null +++ b/matlab/+jsonlab/gzipdecode.m @@ -0,0 +1,41 @@ +function output = gzipdecode(input) +import jsonlab.* +%GZIPDECODE Decompress input bytes using GZIP. +% +% output = gzipdecode(input) +% +% The function takes a compressed byte array INPUT and returns inflated +% bytes OUTPUT. The INPUT is a result of GZIPENCODE function. The OUTPUT +% is always an 1-by-N uint8 array. JAVA must be enabled to use the function. +% +% See also gzipencode typecast +% +% Copyright (c) 2012, Kota Yamaguchi +% URL: https://www.mathworks.com/matlabcentral/fileexchange/39526-byte-encoding-utilities +% License : BSD, see LICENSE_*.txt +% + +if(nargin==0) + error('you must provide at least 1 input'); +end +if(exist('zmat')==3) + output=zmat(uint8(input),0,'gzip'); + return; +end +error(javachk('jvm')); +if ischar(input) + warning('gzipdecode:inputTypeMismatch', ... + 'Input is char, but treated as uint8.'); + input = uint8(input); +end +if ~isa(input, 'int8') && ~isa(input, 'uint8') + error('Input must be either int8 or uint8.'); +end + +gzip = java.util.zip.GZIPInputStream(java.io.ByteArrayInputStream(input)); +buffer = java.io.ByteArrayOutputStream(); +org.apache.commons.io.IOUtils.copy(gzip, buffer); +gzip.close(); +output = typecast(buffer.toByteArray(), 'uint8')'; + +end diff --git a/matlab/+jsonlab/gzipencode.m b/matlab/+jsonlab/gzipencode.m new file mode 100644 index 0000000..fe10c13 --- /dev/null +++ b/matlab/+jsonlab/gzipencode.m @@ -0,0 +1,39 @@ +function output = gzipencode(input) +import jsonlab.* +%GZIPENCODE Compress input bytes with GZIP. +% +% output = gzipencode(input) +% +% The function takes a char, int8, or uint8 array INPUT and returns +% compressed bytes OUTPUT as a uint8 array. Note that the compression +% doesn't preserve input dimensions. JAVA must be enabled to use the +% function. +% +% See also gzipdecode typecast +% +% Copyright (c) 2012, Kota Yamaguchi +% URL: https://www.mathworks.com/matlabcentral/fileexchange/39526-byte-encoding-utilities +% License : BSD, see LICENSE_*.txt +% + +if(nargin==0) + error('you must provide at least 1 input'); +end +if(exist('zmat')==3) + output=zmat(uint8(input),1,'gzip'); + return; +end +error(javachk('jvm')); +if ischar(input), input = uint8(input); end +if ~isa(input, 'int8') && ~isa(input, 'uint8') + error('Input must be either char, int8 or uint8.'); +end + +buffer = java.io.ByteArrayOutputStream(); +gzip = java.util.zip.GZIPOutputStream(buffer); +gzip.write(input, 0, numel(input)); +gzip.close(); +output = typecast(buffer.toByteArray(), 'uint8')'; + +end + diff --git a/matlab/+jsonlab/jsonopt.m b/matlab/+jsonlab/jsonopt.m new file mode 100644 index 0000000..7f3fcdb --- /dev/null +++ b/matlab/+jsonlab/jsonopt.m @@ -0,0 +1,36 @@ +function val=jsonopt(key,default,varargin) +import jsonlab.* +% +% val=jsonopt(key,default,optstruct) +% +% setting options based on a struct. The struct can be produced +% by varargin2struct from a list of 'param','value' pairs +% +% authors:Qianqian Fang (q.fang neu.edu) +% +% $Id: loadjson.m 371 2012-06-20 12:43:06Z fangq $ +% +% input: +% key: a string with which one look up a value from a struct +% default: if the key does not exist, return default +% optstruct: a struct where each sub-field is a key +% +% output: +% val: if key exists, val=optstruct.key; otherwise val=default +% +% license: +% BSD, see LICENSE_BSD.txt file for details +% +% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +val=default; +if(nargin<=2) return; end +opt=varargin{1}; +if(isstruct(opt)) + if(isfield(opt,key)) + val=getfield(opt,key); + elseif(isfield(opt,lower(key))) + val=getfield(opt,lower(key)); + end +end diff --git a/matlab/+jsonlab/license.txt b/matlab/+jsonlab/license.txt new file mode 100644 index 0000000..8be2cdc --- /dev/null +++ b/matlab/+jsonlab/license.txt @@ -0,0 +1,35 @@ +Copyright (c) 2019, Qianqian Fang +Copyright (c) 2009, Nedialko +Copyright (c) 2016, The MathWorks, Inc. +Copyright (c) 2011, François Glineur +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of the nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. +* Neither the name of the The MathWorks, Inc. nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. +* In all cases, the software is, and all modifications and derivatives of the + software shall be, licensed to you solely for use in conjunction with + MathWorks products and service offerings. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/matlab/+jsonlab/loadjson.m b/matlab/+jsonlab/loadjson.m new file mode 100644 index 0000000..0d167dc --- /dev/null +++ b/matlab/+jsonlab/loadjson.m @@ -0,0 +1,537 @@ +function data = loadjson(fname,varargin) +% +% data=loadjson(fname,opt) +% or +% data=loadjson(fname,'param1',value1,'param2',value2,...) +% +% parse a JSON (JavaScript Object Notation) file or string +% +% authors:Qianqian Fang (q.fang neu.edu) +% created on 2011/09/09, including previous works from +% +% Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713 +% created on 2009/11/02 +% François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393 +% created on 2009/03/22 +% Joel Feenstra: +% http://www.mathworks.com/matlabcentral/fileexchange/20565 +% created on 2008/07/03 +% +% $Id$ +% +% input: +% fname: input file name, if fname contains "{}" or "[]", fname +% will be interpreted as a JSON string +% opt: a struct to store parsing options, opt can be replaced by +% a list of ('param',value) pairs - the param string is equivallent +% to a field in opt. opt can have the following +% fields (first in [.|.] is the default) +% +% opt.SimplifyCell [0|1]: if set to 1, loadjson will call cell2mat +% for each element of the JSON data, and group +% arrays based on the cell2mat rules. +% opt.FastArrayParser [1|0 or integer]: if set to 1, use a +% speed-optimized array parser when loading an +% array object. The fast array parser may +% collapse block arrays into a single large +% array similar to rules defined in cell2mat; 0 to +% use a legacy parser; if set to a larger-than-1 +% value, this option will specify the minimum +% dimension to enable the fast array parser. For +% example, if the input is a 3D array, setting +% FastArrayParser to 1 will return a 3D array; +% setting to 2 will return a cell array of 2D +% arrays; setting to 3 will return to a 2D cell +% array of 1D vectors; setting to 4 will return a +% 3D cell array. +% opt.ShowProgress [0|1]: if set to 1, loadjson displays a progress bar. +% opt.ParseStringArray [0|1]: if set to 1, loadjson displays a progress bar. +% +% output: +% dat: a cell array, where {...} blocks are converted into cell arrays, +% and [...] are converted to arrays +% +% examples: +% dat=loadjson('{"obj":{"string":"value","array":[1,2,3]}}') +% dat=loadjson(['examples' filesep 'example1.json']) +% dat=loadjson(['examples' filesep 'example1.json'],'SimplifyCell',1) +% +% license: +% BSD, see LICENSE_BSD.txt file for details +% +% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% +import jsonlab.* + +global pos index_esc isoct arraytoken + +if(regexp(fname,'^\s*(?:\[.*\])|(?:\{.*\})\s*$','once')) + string=fname; +elseif(exist(fname,'file')) + try + string = fileread(fname); + catch + try + string = urlread(['file://',fname]); + catch + string = urlread(['file://',fullfile(pwd,fname)]); + end + end +else + error('input file does not exist'); +end + +pos = 1; len = length(string); inStr = string; +isoct=exist('OCTAVE_VERSION','builtin'); +arraytoken=find(inStr=='[' | inStr==']' | inStr=='"'); +jstr=regexprep(inStr,'\\\\',' '); +escquote=regexp(jstr,'\\"'); +arraytoken=sort([arraytoken escquote]); + +% String delimiters and escape chars identified to improve speed: +esc = find(inStr=='"' | inStr=='\' ); % comparable to: regexp(inStr, '["\\]'); +index_esc = 1; + +opt=varargin2struct(varargin{:}); + +if(jsonopt('ShowProgress',0,opt)==1) + opt.progressbar_=waitbar(0,'loading ...'); +end +jsoncount=1; +while pos <= len + switch(next_char(inStr)) + case '{' + data{jsoncount} = parse_object(inStr, esc, opt); + case '[' + data{jsoncount} = parse_array(inStr, esc, opt); + otherwise + error_pos('Outer level structure must be an object or an array',inStr); + end + jsoncount=jsoncount+1; +end % while + +jsoncount=length(data); +if(jsoncount==1 && iscell(data)) + data=data{1}; +end + +if(isfield(opt,'progressbar_')) + close(opt.progressbar_); +end + +%%------------------------------------------------------------------------- +function object = parse_object(inStr, esc, varargin) +import jsonlab.* + parse_char(inStr, '{'); + object = []; + if next_char(inStr) ~= '}' + while 1 + str = parseStr(inStr, esc, varargin{:}); + if isempty(str) + error_pos('Name of value at position %d cannot be empty',inStr); + end + parse_char(inStr, ':'); + val = parse_value(inStr, esc, varargin{:}); + object.(valid_field(str))=val; + if next_char(inStr) == '}' + break; + end + parse_char(inStr, ','); + end + end + parse_char(inStr, '}'); + if(isstruct(object)) + object=struct2jdata(object); + end + +%%------------------------------------------------------------------------- + +function object = parse_array(inStr, esc, varargin) % JSON array is written in row-major order +import jsonlab.* + global pos isoct + parse_char(inStr, '['); + object = cell(0, 1); + dim2=[]; + arraydepth=jsonopt('JSONLAB_ArrayDepth_',1,varargin{:}); + pbar=-1; + if(isfield(varargin{1},'progressbar_')) + pbar=varargin{1}.progressbar_; + end + + if next_char(inStr) ~= ']' + if(jsonopt('FastArrayParser',1,varargin{:})>=1 && arraydepth>=jsonopt('FastArrayParser',1,varargin{:})) + [endpos, e1l, e1r]=matching_bracket(inStr,pos); + arraystr=['[' inStr(pos:endpos)]; + arraystr=regexprep(arraystr,'"_NaN_"','NaN'); + arraystr=regexprep(arraystr,'"([-+]*)_Inf_"','$1Inf'); + arraystr(arraystr==sprintf('\n'))=[]; + arraystr(arraystr==sprintf('\r'))=[]; + %arraystr=regexprep(arraystr,'\s*,',','); % this is slow,sometimes needed + if(~isempty(e1l) && ~isempty(e1r)) % the array is in 2D or higher D + astr=inStr((e1l+1):(e1r-1)); + astr=regexprep(astr,'"_NaN_"','NaN'); + astr=regexprep(astr,'"([-+]*)_Inf_"','$1Inf'); + astr(astr==sprintf('\n'))=[]; + astr(astr==sprintf('\r'))=[]; + astr(astr==' ')=''; + if(isempty(find(astr=='[', 1))) % array is 2D + dim2=length(sscanf(astr,'%f,',[1 inf])); + end + else % array is 1D + astr=arraystr(2:end-1); + astr(astr==' ')=''; + [obj, count, errmsg, nextidx]=sscanf(astr,'%f,',[1,inf]); + if(nextidx>=length(astr)-1) + object=obj; + pos=endpos; + parse_char(inStr, ']'); + return; + end + end + + try + if(~isempty(dim2)) + astr=arraystr; + astr(astr=='[')=''; + astr(astr==']')=''; + astr=regexprep(astr,'\s*$',''); + astr(astr==' ')=''; + [obj, count, errmsg, nextidx]=sscanf(astr,'%f,',inf); + if(nextidx>=length(astr)-1) + object=reshape(obj,dim2,numel(obj)/dim2)'; + pos=endpos; + parse_char(inStr, ']'); + if(pbar>0) + waitbar(pos/length(inStr),pbar,'loading ...'); + end + return; + end + end + arraystr=regexprep(arraystr,'\]\s*,','];'); + catch + end + else + arraystr='['; + end + try + arraystr=regexprep(arraystr,'^\s*\[','{','once'); + arraystr=regexprep(arraystr,'\]\s*$','}','once'); + if(isoct && regexp(arraystr,'"','once')) + error('Octave eval can produce empty cells for JSON-like input'); + end + if(regexp(arraystr,':','once')) + error('One can not use MATLAB-like ":" construct inside a JSON array'); + end + if(jsonopt('ParseStringArray',0,varargin{:})==0) + arraystr=regexprep(arraystr,'\"',''''); + end + object=eval(arraystr); + if(iscell(object)) + object=cellfun(@unescapejsonstring,object,'UniformOutput',false); + end + pos=endpos; + catch + while 1 + newopt=varargin2struct(varargin{:},'JSONLAB_ArrayDepth_',arraydepth+1); + val = parse_value(inStr, esc, newopt); + object{end+1} = val; + if next_char(inStr) == ']' + break; + end + parse_char(inStr, ','); + end + end + end + if(jsonopt('SimplifyCell',0,varargin{:})==1) + try + oldobj=object; + object=cell2mat(object')'; + if(iscell(oldobj) && isstruct(object) && numel(object)>1 && jsonopt('SimplifyCellArray',1,varargin{:})==0) + object=oldobj; + elseif(size(object,1)>1 && ismatrix(object)) + object=object'; + end + catch + end + end + parse_char(inStr, ']'); + + if(pbar>0) + waitbar(pos/length(inStr),pbar,'loading ...'); + end +%%------------------------------------------------------------------------- + +function parse_char(inStr, c) +import jsonlab.* + global pos + pos=skip_whitespace(pos, inStr); + if pos > length(inStr) || inStr(pos) ~= c + error_pos(sprintf('Expected %c at position %%d', c),inStr); + else + pos = pos + 1; + pos=skip_whitespace(pos, inStr); + end + +%%------------------------------------------------------------------------- + +function c = next_char(inStr) +import jsonlab.* + global pos + pos=skip_whitespace(pos, inStr); + if pos > length(inStr) + c = []; + else + c = inStr(pos); + end + +%%------------------------------------------------------------------------- + +function newpos=skip_whitespace(pos, inStr) +import jsonlab.* + newpos=pos; + while newpos <= length(inStr) && isspace(inStr(newpos)) + newpos = newpos + 1; + end + +%%------------------------------------------------------------------------- +function str = parseStr(inStr, esc, varargin) +import jsonlab.* + global pos index_esc + % len, ns = length(inStr), keyboard + if inStr(pos) ~= '"' + error_pos('String starting with " expected at position %d',inStr); + else + pos = pos + 1; + end + str = ''; + while pos <= length(inStr) + while index_esc <= length(esc) && esc(index_esc) < pos + index_esc = index_esc + 1; + end + if index_esc > length(esc) + str = [str inStr(pos:end)]; + pos = length(inStr) + 1; + break; + else + str = [str inStr(pos:esc(index_esc)-1)]; + pos = esc(index_esc); + end + nstr = length(str); + switch inStr(pos) + case '"' + pos = pos + 1; + if(~isempty(str)) + if(strcmp(str,'_Inf_')) + str=Inf; + elseif(strcmp(str,'-_Inf_')) + str=-Inf; + elseif(strcmp(str,'_NaN_')) + str=NaN; + end + end + return; + case '\' + if pos+1 > length(inStr) + error_pos('End of file reached right after escape character',inStr); + end + pos = pos + 1; + switch inStr(pos) + case {'"' '\' '/'} + str(nstr+1) = inStr(pos); + pos = pos + 1; + case {'b' 'f' 'n' 'r' 't'} + str(nstr+1) = sprintf(['\' inStr(pos)]); + pos = pos + 1; + case 'u' + if pos+4 > length(inStr) + error_pos('End of file reached in escaped unicode character',inStr); + end + str(nstr+(1:6)) = inStr(pos-1:pos+4); + pos = pos + 5; + end + otherwise % should never happen + str(nstr+1) = inStr(pos); + keyboard; + pos = pos + 1; + end + end + str=unescapejsonstring(str); + error_pos('End of file while expecting end of inStr',inStr); + +%%------------------------------------------------------------------------- + +function num = parse_number(inStr, varargin) +import jsonlab.* + global pos isoct + currstr=inStr(pos:min(pos+30,end)); + if(isoct~=0) + numstr=regexp(currstr,'^\s*-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?','end'); + [num] = sscanf(currstr, '%f', 1); + delta=numstr+1; + else + [num, one, err, delta] = sscanf(currstr, '%f', 1); + if ~isempty(err) + error_pos('Error reading number at position %d',inStr); + end + end + pos = pos + delta-1; + +%%------------------------------------------------------------------------- + +function val = parse_value(inStr, esc, varargin) +import jsonlab.* + global pos + len=length(inStr); + if(isfield(varargin{1},'progressbar_')) + waitbar(pos/len,varargin{1}.progressbar_,'loading ...'); + end + + switch(inStr(pos)) + case '"' + val = parseStr(inStr, esc, varargin{:}); + return; + case '[' + val = parse_array(inStr, esc, varargin{:}); + return; + case '{' + val = parse_object(inStr, esc, varargin{:}); + return; + case {'-','0','1','2','3','4','5','6','7','8','9'} + val = parse_number(inStr, varargin{:}); + return; + case 't' + if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'true') + val = true; + pos = pos + 4; + return; + end + case 'f' + if pos+4 <= len && strcmpi(inStr(pos:pos+4), 'false') + val = false; + pos = pos + 5; + return; + end + case 'n' + if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'null') + val = []; + pos = pos + 4; + return; + end + end + error_pos('Value expected at position %d',inStr); +%%------------------------------------------------------------------------- + +function error_pos(msg, inStr) +import jsonlab.* + global pos len + poShow = max(min([pos-15 pos-1 pos pos+20],len),1); + if poShow(3) == poShow(2) + poShow(3:4) = poShow(2)+[0 -1]; % display nothing after + end + msg = [sprintf(msg, pos) ': ' ... + inStr(poShow(1):poShow(2)) '' inStr(poShow(3):poShow(4)) ]; + error( ['JSONparser:invalidFormat: ' msg] ); + +%%------------------------------------------------------------------------- + +function str = valid_field(str) +import jsonlab.* +global isoct +% From MATLAB doc: field names must begin with a letter, which may be +% followed by any combination of letters, digits, and underscores. +% Invalid characters will be converted to underscores, and the prefix +% "x0x[Hex code]_" will be added if the first character is not a letter. + pos=regexp(str,'^[^A-Za-z]','once'); + if(~isempty(pos)) + if(~isoct && str(1)+0 > 255) + str=regexprep(str,'^([^A-Za-z])','x0x${sprintf(''%X'',unicode2native($1))}_','once'); + else + str=sprintf('x0x%X_%s',char(str(1)),str(2:end)); + end + end + if(isempty(regexp(str,'[^0-9A-Za-z_]', 'once' ))) + return; + end + if(~isoct) + str=regexprep(str,'([^0-9A-Za-z_])','_0x${sprintf(''%X'',unicode2native($1))}_'); + else + pos=regexp(str,'[^0-9A-Za-z_]'); + if(isempty(pos)) + return; + end + str0=str; + pos0=[0 pos(:)' length(str)]; + str=''; + for i=1:length(pos) + str=[str str0(pos0(i)+1:pos(i)-1) sprintf('_0x%X_',str0(pos(i)))]; + end + if(pos(end)~=length(str)) + str=[str str0(pos0(end-1)+1:pos0(end))]; + end + end + %str(~isletter(str) & ~('0' <= str & str <= '9')) = '_'; + +%%------------------------------------------------------------------------- +function endpos = matching_quote(str,pos) +import jsonlab.* +len=length(str); +while(pos1 && str(pos-1)=='\')) + endpos=pos; + return; + end + end + pos=pos+1; +end +error('unmatched quotation mark'); +%%------------------------------------------------------------------------- +function [endpos, e1l, e1r, maxlevel] = matching_bracket(str,pos) +import jsonlab.* +global arraytoken +level=1; +maxlevel=level; +endpos=0; +bpos=arraytoken(arraytoken>=pos); +tokens=str(bpos); +len=length(tokens); +pos=1; +e1l=[]; +e1r=[]; +while(pos<=len) + c=tokens(pos); + if(c==']') + level=level-1; + if(isempty(e1r)) + e1r=bpos(pos); + end + if(level==0) + endpos=bpos(pos); + return + end + elseif(c=='[') + if(isempty(e1l)) + e1l=bpos(pos); + end + level=level+1; + maxlevel=max(maxlevel,level); + elseif(c=='"') + pos=matching_quote(tokens,pos+1); + end + pos=pos+1; +end +if(endpos==0) + error('unmatched "]"'); +end + +function newstr=unescapejsonstring(str) +import jsonlab.* +newstr=str; +if(~ischar(str)) + return; +end +escapechars={'\\','\"','\/','\a','\b','\f','\n','\r','\t','\v'}; +for i=1:length(escapechars); + newstr=regexprep(newstr,regexprep(escapechars{i},'\\','\\\\'), escapechars{i}); +end +newstr=regexprep(newstr,'\\u([0-9A-Fa-f]{4})', '${char(base2dec($1,16))}'); diff --git a/matlab/+jsonlab/loadubjson.m b/matlab/+jsonlab/loadubjson.m new file mode 100644 index 0000000..067524e --- /dev/null +++ b/matlab/+jsonlab/loadubjson.m @@ -0,0 +1,465 @@ +function data = loadubjson(fname,varargin) +import jsonlab.* +% +% data=loadubjson(fname,opt) +% or +% data=loadubjson(fname,'param1',value1,'param2',value2,...) +% +% parse a JSON (JavaScript Object Notation) file or string +% +% authors:Qianqian Fang (q.fang neu.edu) +% created on 2013/08/01 +% +% $Id$ +% +% input: +% fname: input file name, if fname contains "{}" or "[]", fname +% will be interpreted as a UBJSON string +% opt: a struct to store parsing options, opt can be replaced by +% a list of ('param',value) pairs - the param string is equivallent +% to a field in opt. opt can have the following +% fields (first in [.|.] is the default) +% +% opt.SimplifyCell [0|1]: if set to 1, loadubjson will call cell2mat +% for each element of the JSON data, and group +% arrays based on the cell2mat rules. +% opt.IntEndian [B|L]: specify the endianness of the integer fields +% in the UBJSON input data. B - Big-Endian format for +% integers (as required in the UBJSON specification); +% L - input integer fields are in Little-Endian order. +% opt.NameIsString [0|1]: for UBJSON Specification Draft 8 or +% earlier versions (JSONLab 1.0 final or earlier), +% the "name" tag is treated as a string. To load +% these UBJSON data, you need to manually set this +% flag to 1. +% +% output: +% dat: a cell array, where {...} blocks are converted into cell arrays, +% and [...] are converted to arrays +% +% examples: +% obj=struct('string','value','array',[1 2 3]); +% ubjdata=saveubjson('obj',obj); +% dat=loadubjson(ubjdata) +% dat=loadubjson(['examples' filesep 'example1.ubj']) +% dat=loadubjson(['examples' filesep 'example1.ubj'],'SimplifyCell',1) +% +% license: +% BSD, see LICENSE_BSD.txt file for details +% +% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +global pos inStr len esc index_esc len_esc isoct arraytoken fileendian systemendian + +if(regexp(fname,'[\{\}\]\[]','once')) + string=fname; +elseif(exist(fname,'file')) + fid = fopen(fname,'rb'); + string = fread(fid,inf,'uint8=>char')'; + fclose(fid); +else + error('input file does not exist'); +end + +pos = 1; len = length(string); inStr = string; +isoct=exist('OCTAVE_VERSION','builtin'); +arraytoken=find(inStr=='[' | inStr==']' | inStr=='"'); +jstr=regexprep(inStr,'\\\\',' '); +escquote=regexp(jstr,'\\"'); +arraytoken=sort([arraytoken escquote]); + +% String delimiters and escape chars identified to improve speed: +esc = find(inStr=='"' | inStr=='\' ); % comparable to: regexp(inStr, '["\\]'); +index_esc = 1; len_esc = length(esc); + +opt=varargin2struct(varargin{:}); +fileendian=upper(jsonopt('IntEndian','B',opt)); +[os,maxelem,systemendian]=computer; + +jsoncount=1; +while pos <= len + switch(next_char) + case '{' + data{jsoncount} = parse_object(opt); + case '[' + data{jsoncount} = parse_array(opt); + otherwise + error_pos('Outer level structure must be an object or an array'); + end + jsoncount=jsoncount+1; +end % while + +jsoncount=length(data); +if(jsoncount==1 && iscell(data)) + data=data{1}; +end + +%%------------------------------------------------------------------------- +function object = parse_object(varargin) +import jsonlab.* + parse_char('{'); + object = []; + type=''; + count=-1; + if(next_char == '$') + type=inStr(pos+1); % TODO + pos=pos+2; + end + if(next_char == '#') + pos=pos+1; + count=double(parse_number()); + end + if next_char ~= '}' + num=0; + while 1 + if(jsonopt('NameIsString',0,varargin{:})) + str = parseStr(varargin{:}); + else + str = parse_name(varargin{:}); + end + if isempty(str) + error_pos('Name of value at position %d cannot be empty'); + end + %parse_char(':'); + val = parse_value(varargin{:}); + num=num+1; + object.(valid_field(str))=val; + if next_char == '}' || (count>=0 && num>=count) + break; + end + %parse_char(','); + end + end + if(count==-1) + parse_char('}'); + end + if(isstruct(object)) + object=struct2jdata(object,struct('Base64',0)); + end + +%%------------------------------------------------------------------------- +function [cid,len]=elem_info(type) +import jsonlab.* +id=strfind('iUIlLdD',type); +dataclass={'int8','uint8','int16','int32','int64','single','double'}; +bytelen=[1,1,2,4,8,4,8]; +if(id>0) + cid=dataclass{id}; + len=bytelen(id); +else + error_pos('unsupported type at position %d'); +end +%%------------------------------------------------------------------------- + + +function [data, adv]=parse_block(type,count,varargin) +import jsonlab.* +global pos inStr isoct fileendian systemendian +[cid,len]=elem_info(type); +datastr=inStr(pos:pos+len*count-1); +newdata=uint8(datastr); +id=strfind('iUIlLdD',type); +if(fileendian~=systemendian) + newdata=swapbytes(typecast(newdata,cid)); +end +data=typecast(newdata,cid); +adv=double(len*count); + +%%------------------------------------------------------------------------- + + +function object = parse_array(varargin) % JSON array is written in row-major order +import jsonlab.* +global pos inStr + parse_char('['); + object = cell(0, 1); + dim=[]; + type=''; + count=-1; + if(next_char == '$') + type=inStr(pos+1); + pos=pos+2; + end + if(next_char == '#') + pos=pos+1; + if(next_char=='[') + dim=parse_array(varargin{:}); + count=prod(double(dim)); + else + count=double(parse_number()); + end + end + if(~isempty(type)) + if(count>=0) + [object, adv]=parse_block(type,count,varargin{:}); + if(~isempty(dim)) + object=reshape(object,dim); + end + pos=pos+adv; + return; + else + endpos=matching_bracket(inStr,pos); + [cid,len]=elem_info(type); + count=(endpos-pos)/len; + [object, adv]=parse_block(type,count,varargin{:}); + pos=pos+adv; + parse_char(']'); + return; + end + end + if next_char ~= ']' + while 1 + val = parse_value(varargin{:}); + object{end+1} = val; + if next_char == ']' + break; + end + %parse_char(','); + end + end + if(jsonopt('SimplifyCell',0,varargin{:})==1) + try + oldobj=object; + object=cell2mat(object')'; + if(iscell(oldobj) && isstruct(object) && numel(object)>1 && jsonopt('SimplifyCellArray',1,varargin{:})==0) + object=oldobj; + elseif(size(object,1)>1 && ismatrix(object)) + object=object'; + end + catch + end + end + if(count==-1) + parse_char(']'); + end + +%%------------------------------------------------------------------------- + +function parse_char(c) +import jsonlab.* + global pos inStr len + skip_whitespace; + if pos > len || inStr(pos) ~= c + error_pos(sprintf('Expected %c at position %%d', c)); + else + pos = pos + 1; + skip_whitespace; + end + +%%------------------------------------------------------------------------- + +function c = next_char +import jsonlab.* + global pos inStr len + skip_whitespace; + if pos > len + c = []; + else + c = inStr(pos); + end + +%%------------------------------------------------------------------------- + +function skip_whitespace +import jsonlab.* + global pos inStr len + while pos <= len && isspace(inStr(pos)) + pos = pos + 1; + end + +%%------------------------------------------------------------------------- +function str = parse_name(varargin) +import jsonlab.* + global pos inStr + bytelen=double(parse_number()); + if(length(inStr)>=pos+bytelen-1) + str=inStr(pos:pos+bytelen-1); + pos=pos+bytelen; + else + error_pos('End of file while expecting end of name'); + end +%%------------------------------------------------------------------------- + +function str = parseStr(varargin) +import jsonlab.* + global pos inStr + % len, ns = length(inStr), keyboard + type=inStr(pos); + if type ~= 'S' && type ~= 'C' && type ~= 'H' + error_pos('String starting with S expected at position %d'); + else + pos = pos + 1; + end + if(type == 'C') + str=inStr(pos); + pos=pos+1; + return; + end + bytelen=double(parse_number()); + if(length(inStr)>=pos+bytelen-1) + str=inStr(pos:pos+bytelen-1); + pos=pos+bytelen; + else + error_pos('End of file while expecting end of inStr'); + end + +%%------------------------------------------------------------------------- + +function num = parse_number(varargin) +import jsonlab.* + global pos inStr isoct fileendian systemendian + id=strfind('iUIlLdD',inStr(pos)); + if(isempty(id)) + error_pos('expecting a number at position %d'); + end + type={'int8','uint8','int16','int32','int64','single','double'}; + bytelen=[1,1,2,4,8,4,8]; + datastr=inStr(pos+1:pos+bytelen(id)); + newdata=uint8(datastr); + if(fileendian~=systemendian) + newdata=swapbytes(typecast(newdata,type{id})); + end + num=typecast(newdata,type{id}); + pos = pos + bytelen(id)+1; + +%%------------------------------------------------------------------------- + +function val = parse_value(varargin) +import jsonlab.* + global pos inStr + + switch(inStr(pos)) + case {'S','C','H'} + val = parseStr(varargin{:}); + return; + case '[' + val = parse_array(varargin{:}); + return; + case '{' + val = parse_object(varargin{:}); + return; + case {'i','U','I','l','L','d','D'} + val = parse_number(varargin{:}); + return; + case 'T' + val = true; + pos = pos + 1; + return; + case 'F' + val = false; + pos = pos + 1; + return; + case {'Z','N'} + val = []; + pos = pos + 1; + return; + end + error_pos('Value expected at position %d'); +%%------------------------------------------------------------------------- + +function error_pos(msg) +import jsonlab.* + global pos inStr len + poShow = max(min([pos-15 pos-1 pos pos+20],len),1); + if poShow(3) == poShow(2) + poShow(3:4) = poShow(2)+[0 -1]; % display nothing after + end + msg = [sprintf(msg, pos) ': ' ... + inStr(poShow(1):poShow(2)) '' inStr(poShow(3):poShow(4)) ]; + error( ['JSONparser:invalidFormat: ' msg] ); + +%%------------------------------------------------------------------------- + +function str = valid_field(str) +import jsonlab.* +global isoct +% From MATLAB doc: field names must begin with a letter, which may be +% followed by any combination of letters, digits, and underscores. +% Invalid characters will be converted to underscores, and the prefix +% "x0x[Hex code]_" will be added if the first character is not a letter. + pos=regexp(str,'^[^A-Za-z]','once'); + if(~isempty(pos)) + if(~isoct) + str=regexprep(str,'^([^A-Za-z])','x0x${sprintf(''%X'',unicode2native($1))}_','once'); + else + str=sprintf('x0x%X_%s',char(str(1)),str(2:end)); + end + end + if(isempty(regexp(str,'[^0-9A-Za-z_]', 'once' ))) + return; + end + if(~isoct) + str=regexprep(str,'([^0-9A-Za-z_])','_0x${sprintf(''%X'',unicode2native($1))}_'); + else + pos=regexp(str,'[^0-9A-Za-z_]'); + if(isempty(pos)) + return; + end + str0=str; + pos0=[0 pos(:)' length(str)]; + str=''; + for i=1:length(pos) + str=[str str0(pos0(i)+1:pos(i)-1) sprintf('_0x%X_',str0(pos(i)))]; + end + if(pos(end)~=length(str)) + str=[str str0(pos0(end-1)+1:pos0(end))]; + end + end + %str(~isletter(str) & ~('0' <= str & str <= '9')) = '_'; + +%%------------------------------------------------------------------------- +function endpos = matching_quote(str,pos) +import jsonlab.* +len=length(str); +while(pos1 && str(pos-1)=='\')) + endpos=pos; + return; + end + end + pos=pos+1; +end +error('unmatched quotation mark'); +%%------------------------------------------------------------------------- +function [endpos, e1l, e1r, maxlevel] = matching_bracket(str,pos) +import jsonlab.* +global arraytoken +level=1; +maxlevel=level; +endpos=0; +bpos=arraytoken(arraytoken>=pos); +tokens=str(bpos); +len=length(tokens); +pos=1; +e1l=[]; +e1r=[]; +while(pos<=len) + c=tokens(pos); + if(c==']') + level=level-1; + if(isempty(e1r)) + e1r=bpos(pos); + end + if(level==0) + endpos=bpos(pos); + return + end + end + if(c=='[') + if(isempty(e1l)) + e1l=bpos(pos); + end + level=level+1; + maxlevel=max(maxlevel,level); + end + if(c=='"') + pos=matching_quote(tokens,pos+1); + end + pos=pos+1; +end +if(endpos==0) + error('unmatched "]"'); +end + diff --git a/matlab/+jsonlab/mergestruct.m b/matlab/+jsonlab/mergestruct.m new file mode 100644 index 0000000..d962ece --- /dev/null +++ b/matlab/+jsonlab/mergestruct.m @@ -0,0 +1,34 @@ +function s=mergestruct(s1,s2) +import jsonlab.* +% +% s=mergestruct(s1,s2) +% +% merge two struct objects into one +% +% authors:Qianqian Fang (q.fang neu.edu) +% date: 2012/12/22 +% +% input: +% s1,s2: a struct object, s1 and s2 can not be arrays +% +% output: +% s: the merged struct object. fields in s1 and s2 will be combined in s. +% +% license: +% BSD, see LICENSE_BSD.txt file for details +% +% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +if(~isstruct(s1) || ~isstruct(s2)) + error('input parameters contain non-struct'); +end +if(length(s1)>1 || length(s2)>1) + error('can not merge struct arrays'); +end +fn=fieldnames(s2); +s=s1; +for i=1:length(fn) + s=setfield(s,fn{i},getfield(s2,fn{i})); +end + diff --git a/matlab/+jsonlab/package.json b/matlab/+jsonlab/package.json new file mode 100644 index 0000000..ecf30f5 --- /dev/null +++ b/matlab/+jsonlab/package.json @@ -0,0 +1,21 @@ +{ + "name": "jsonlab", + "version": "1.9", + "description": "An open-source MATLAB/Octave JSON encoder and decoder", + "directories": { + "example": "examples" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/fangq/jsonlab.git" + }, + "author": "Qianqian Fang ", + "license": "BSD", + "bugs": { + "url": "https://github.com/fangq/jsonlab/issues" + }, + "homepage": "https://iso2mesh.sf.net/jsonlab/" +} diff --git a/matlab/+jsonlab/savejson.m b/matlab/+jsonlab/savejson.m new file mode 100644 index 0000000..ad2c5ac --- /dev/null +++ b/matlab/+jsonlab/savejson.m @@ -0,0 +1,686 @@ +function json=savejson(rootname,obj,varargin) +import jsonlab.* +% +% json=savejson(rootname,obj,filename) +% or +% json=savejson(rootname,obj,opt) +% json=savejson(rootname,obj,'param1',value1,'param2',value2,...) +% +% convert a MATLAB object (cell, struct or array) into a JSON (JavaScript +% Object Notation) string +% +% author: Qianqian Fang (q.fang neu.edu) +% created on 2011/09/09 +% +% $Id$ +% +% input: +% rootname: the name of the root-object, when set to '', the root name +% is ignored, however, when opt.ForceRootName is set to 1 (see below), +% the MATLAB variable name will be used as the root name. +% obj: a MATLAB object (array, cell, cell array, struct, struct array, +% class instance). +% filename: a string for the file name to save the output JSON data. +% opt: a struct for additional options, ignore to use default values. +% opt can have the following fields (first in [.|.] is the default) +% +% opt.FileName [''|string]: a file name to save the output JSON data +% opt.FloatFormat ['%.10g'|string]: format to show each numeric element +% of a 1D/2D array; +% opt.ArrayIndent [1|0]: if 1, output explicit data array with +% precedent indentation; if 0, no indentation +% opt.ArrayToStruct[0|1]: when set to 0, savejson outputs 1D/2D +% array in JSON array format; if sets to 1, an +% array will be shown as a struct with fields +% "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for +% sparse arrays, the non-zero elements will be +% saved to _ArrayData_ field in triplet-format i.e. +% (ix,iy,val) and "_ArrayIsSparse_" will be added +% with a value of 1; for a complex array, the +% _ArrayData_ array will include two columns +% (4 for sparse) to record the real and imaginary +% parts, and also "_ArrayIsComplex_":1 is added. +% opt.ParseLogical [0|1]: if this is set to 1, logical array elem +% will use true/false rather than 1/0. +% opt.SingletArray [0|1]: if this is set to 1, arrays with a single +% numerical element will be shown without a square +% bracket, unless it is the root object; if 0, square +% brackets are forced for any numerical arrays. +% opt.SingletCell [1|0]: if 1, always enclose a cell with "[]" +% even it has only one element; if 0, brackets +% are ignored when a cell has only 1 element. +% opt.ForceRootName [0|1]: when set to 1 and rootname is empty, savejson +% will use the name of the passed obj variable as the +% root object name; if obj is an expression and +% does not have a name, 'root' will be used; if this +% is set to 0 and rootname is empty, the root level +% will be merged down to the lower level. +% opt.Inf ['"$1_Inf_"'|string]: a customized regular expression pattern +% to represent +/-Inf. The matched pattern is '([-+]*)Inf' +% and $1 represents the sign. For those who want to use +% 1e999 to represent Inf, they can set opt.Inf to '$11e999' +% opt.NaN ['"_NaN_"'|string]: a customized regular expression pattern +% to represent NaN +% opt.JSONP [''|string]: to generate a JSONP output (JSON with padding), +% for example, if opt.JSONP='foo', the JSON data is +% wrapped inside a function call as 'foo(...);' +% opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson +% back to the string form +% opt.SaveBinary [0|1]: 1 - save the JSON file in binary mode; 0 - text mode. +% opt.Compact [0|1]: 1- out compact JSON format (remove all newlines and tabs) +% opt.Compression 'zlib' or 'gzip': specify array compression +% method; currently only supports 'gzip' or 'zlib'. The +% data compression only applicable to numerical arrays +% in 3D or higher dimensions, or when ArrayToStruct +% is 1 for 1D or 2D arrays. If one wants to +% compress a long string, one must convert +% it to uint8 or int8 array first. The compressed +% array uses three extra fields +% "_ArrayCompressionMethod_": the opt.Compression value. +% "_ArrayCompressionSize_": a 1D interger array to +% store the pre-compressed (but post-processed) +% array dimensions, and +% "_ArrayCompressedData_": the "base64" encoded +% compressed binary array data. +% opt.CompressArraySize [100|int]: only to compress an array if the total +% element count is larger than this number. +% opt can be replaced by a list of ('param',value) pairs. The param +% string is equivallent to a field in opt and is case sensitive. +% output: +% json: a string in the JSON format (see http://json.org) +% +% examples: +% jsonmesh=struct('MeshNode',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],... +% 'MeshTetra',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],... +% 'MeshTri',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;... +% 2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],... +% 'MeshCreator','FangQ','MeshTitle','T6 Cube',... +% 'SpecialData',[nan, inf, -inf]); +% savejson('jmesh',jsonmesh) +% savejson('',jsonmesh,'ArrayIndent',0,'FloatFormat','\t%.5g') +% +% license: +% BSD, see LICENSE_BSD.txt file for details +% +% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +if(nargin==1) + varname=inputname(1); + obj=rootname; + if(isempty(varname)) + varname='root'; + end + rootname=varname; +else + varname=inputname(2); +end +if(length(varargin)==1 && ischar(varargin{1})) + opt=struct('filename',varargin{1}); +else + opt=varargin2struct(varargin{:}); +end +opt.IsOctave=exist('OCTAVE_VERSION','builtin'); + +dozip=jsonopt('Compression','',opt); +if(~isempty(dozip)) + if(~(strcmpi(dozip,'gzip') || strcmpi(dozip,'zlib'))) + error('compression method "%s" is not supported',dozip); + end + if(exist('zmat')~=3) + try + error(javachk('jvm')); + try + base64decode('test'); + catch + matlab.net.base64decode('test'); + end + catch + error('java-based compression is not supported'); + end + end + opt.Compression=dozip; +end + +if(isfield(opt,'norowbracket')) + warning('Option ''NoRowBracket'' is depreciated, please use ''SingletArray'' and set its value to not(NoRowBracket)'); + if(~isfield(opt,'singletarray')) + opt.singletarray=not(opt.norowbracket); + end +end +rootisarray=0; +rootlevel=1; +forceroot=jsonopt('ForceRootName',0,opt); +if((isnumeric(obj) || islogical(obj) || ischar(obj) || isstruct(obj) || ... + iscell(obj) || isobject(obj)) && isempty(rootname) && forceroot==0) + rootisarray=1; + rootlevel=0; +else + if(isempty(rootname)) + rootname=varname; + end +end +if((isstruct(obj) || iscell(obj))&& isempty(rootname) && forceroot) + rootname='root'; +end + +whitespaces=struct('tab',sprintf('\t'),'newline',sprintf('\n'),'sep',sprintf(',\n')); +if(jsonopt('Compact',0,opt)==1) + whitespaces=struct('tab','','newline','','sep',','); +end +if(~isfield(opt,'whitespaces_')) + opt.whitespaces_=whitespaces; +end + +nl=whitespaces.newline; + +json=obj2json(rootname,obj,rootlevel,opt); +if(rootisarray) + json=sprintf('%s%s',json,nl); +else + json=sprintf('{%s%s%s}\n',nl,json,nl); +end + +jsonp=jsonopt('JSONP','',opt); +if(~isempty(jsonp)) + json=sprintf('%s(%s);%s',jsonp,json,nl); +end + +% save to a file if FileName is set, suggested by Patrick Rapin +filename=jsonopt('FileName','',opt); +if(~isempty(filename)) + if(jsonopt('SaveBinary',0,opt)==1) + fid = fopen(filename, 'wb'); + fwrite(fid,json); + else + fid = fopen(filename, 'wt'); + fwrite(fid,json,'char'); + end + fclose(fid); +end + +%%------------------------------------------------------------------------- +function txt=obj2json(name,item,level,varargin) +import jsonlab.* + +if(iscell(item) || isa(item,'string')) + txt=cell2json(name,item,level,varargin{:}); +elseif(isstruct(item)) + txt=struct2json(name,item,level,varargin{:}); +elseif(ischar(item)) + txt=str2json(name,item,level,varargin{:}); +elseif(isobject(item)) + if(~exist('OCTAVE_VERSION','builtin') && exist('istable','builtin') && istable(item)) + txt=matlabtable2json(name,item,level,varargin{:}); + else + txt=matlabobject2json(name,item,level,varargin{:}); + end +elseif(isa(item,'function_handle')) + txt=struct2json(name,functions(item),level,varargin{:}); +else + txt=mat2json(name,item,level,varargin{:}); +end + +%%------------------------------------------------------------------------- +function txt=cell2json(name,item,level,varargin) +import jsonlab.* +txt={}; +if(~iscell(item) && ~isa(item,'string')) + error('input is not a cell or string array'); +end + +dim=size(item); +if(ndims(squeeze(item))>2) % for 3D or higher dimensions, flatten to 2D for now + item=reshape(item,dim(1),numel(item)/dim(1)); + dim=size(item); +end +len=numel(item); +ws=jsonopt('whitespaces_',struct('tab',sprintf('\t'),'newline',sprintf('\n'),'sep',sprintf(',\n')),varargin{:}); +padding0=repmat(ws.tab,1,level); +padding2=repmat(ws.tab,1,level+1); +nl=ws.newline; +bracketlevel=~jsonopt('singletcell',1,varargin{:}); +if(len>bracketlevel) + if(~isempty(name)) + txt={padding0, '"', checkname(name,varargin{:}),'": [', nl}; name=''; + else + txt={padding0, '[', nl}; + end +elseif(len==0) + if(~isempty(name)) + txt={padding0, '"' checkname(name,varargin{:}) '": []'}; name=''; + else + txt={padding0, '[]'}; + end +end +for i=1:dim(1) + if(dim(1)>1) + txt(end+1:end+3)={padding2,'[',nl}; + end + for j=1:dim(2) + txt{end+1}=obj2json(name,item{i,j},level+(dim(1)>1)+(len>bracketlevel),varargin{:}); + if(j1) + txt(end+1:end+3)={nl,padding2,']'}; + end + if(ibracketlevel) + txt(end+1:end+3)={nl,padding0,']'}; +end +txt = sprintf('%s',txt{:}); + +%%------------------------------------------------------------------------- +function txt=struct2json(name,item,level,varargin) +import jsonlab.* +txt={}; +if(~isstruct(item)) + error('input is not a struct'); +end +dim=size(item); +if(ndims(squeeze(item))>2) % for 3D or higher dimensions, flatten to 2D for now + item=reshape(item,dim(1),numel(item)/dim(1)); + dim=size(item); +end +len=numel(item); +forcearray= (len>1 || (jsonopt('SingletArray',0,varargin{:})==1 && level>0)); +ws=struct('tab',sprintf('\t'),'newline',sprintf('\n')); +ws=jsonopt('whitespaces_',ws,varargin{:}); +padding0=repmat(ws.tab,1,level); +padding2=repmat(ws.tab,1,level+1); +padding1=repmat(ws.tab,1,level+(dim(1)>1)+forcearray); +nl=ws.newline; + +if(isempty(item)) + if(~isempty(name)) + txt={padding0, '"', checkname(name,varargin{:}),'": []'}; + else + txt={padding0, '[]'}; + end + txt = sprintf('%s',txt{:}); + return; +end +if(~isempty(name)) + if(forcearray) + txt={padding0, '"', checkname(name,varargin{:}),'": [', nl}; + end +else + if(forcearray) + txt={padding0, '[', nl}; + end +end +for j=1:dim(2) + if(dim(1)>1) + txt(end+1:end+3)={padding2,'[',nl}; + end + for i=1:dim(1) + names = fieldnames(item(i,j)); + if(~isempty(name) && len==1 && ~forcearray) + txt(end+1:end+5)={padding1, '"', checkname(name,varargin{:}),'": {', nl}; + else + txt(end+1:end+3)={padding1, '{', nl}; + end + if(~isempty(names)) + for e=1:length(names) + txt{end+1}=obj2json(names{e},item(i,j).(names{e}),... + level+(dim(1)>1)+1+forcearray,varargin{:}); + if(e1) + txt(end+1:end+3)={nl,padding2,']'}; + end + if(j1) + txt={padding1, '"', checkname(name,varargin{:}),'": [', nl}; + end +else + if(len>1) + txt={padding1, '[', nl}; + end +end +for e=1:len + val=escapejsonstring(item(e,:)); + if(len==1) + obj=['"' checkname(name,varargin{:}) '": ' '"',val,'"']; + if(isempty(name)) + obj=['"',val,'"']; + end + txt(end+1:end+2)={padding1, obj}; + else + txt(end+1:end+4)={padding0,'"',val,'"'}; + end + if(e==len) + sep=''; + end + txt{end+1}=sep; +end +if(len>1) + txt(end+1:end+3)={nl,padding1,']'}; +end +txt = sprintf('%s',txt{:}); + +%%------------------------------------------------------------------------- +function txt=mat2json(name,item,level,varargin) +import jsonlab.* +if(~isnumeric(item) && ~islogical(item)) + error('input is not an array'); +end +ws=struct('tab',sprintf('\t'),'newline',sprintf('\n'),'sep',sprintf(',\n')); +ws=jsonopt('whitespaces_',ws,varargin{:}); +padding1=repmat(ws.tab,1,level); +padding0=repmat(ws.tab,1,level+1); +nl=ws.newline; +sep=ws.sep; + +dozip=jsonopt('Compression','',varargin{:}); +zipsize=jsonopt('CompressArraySize',100,varargin{:}); + +if(length(size(item))>2 || issparse(item) || ~isreal(item) || ... + (isempty(item) && any(size(item))) ||jsonopt('ArrayToStruct',0,varargin{:}) || (~isempty(dozip) && numel(item)>zipsize)) + if(isempty(name)) + txt=sprintf('%s{%s%s"_ArrayType_": "%s",%s%s"_ArraySize_": %s,%s',... + padding1,nl,padding0,class(item),nl,padding0,regexprep(mat2str(size(item)),'\s+',','),nl); + else + txt=sprintf('%s"%s": {%s%s"_ArrayType_": "%s",%s%s"_ArraySize_": %s,%s',... + padding1,checkname(name,varargin{:}),nl,padding0,class(item),nl,padding0,regexprep(mat2str(size(item)),'\s+',','),nl); + end +else + if(numel(item)==1 && jsonopt('SingletArray',0,varargin{:})==0 && level>0) + numtxt=regexprep(regexprep(matdata2json(item,level+1,varargin{:}),'^\[',''),']$',''); + else + numtxt=matdata2json(item,level+1,varargin{:}); + end + if(isempty(name)) + txt=sprintf('%s%s',padding1,numtxt); + else + if(numel(item)==1 && jsonopt('SingletArray',0,varargin{:})==0) + txt=sprintf('%s"%s": %s',padding1,checkname(name,varargin{:}),numtxt); + else + txt=sprintf('%s"%s": %s',padding1,checkname(name,varargin{:}),numtxt); + end + end + return; +end +dataformat='%s%s%s%s%s'; + +if(issparse(item)) + [ix,iy]=find(item); + data=full(item(find(item))); + if(~isreal(item)) + data=[real(data(:)),imag(data(:))]; + if(size(item,1)==1) + % Kludge to have data's 'transposedness' match item's. + % (Necessary for complex row vector handling below.) + data=data'; + end + txt=sprintf(dataformat,txt,padding0,'"_ArrayIsComplex_": ','1', sep); + end + txt=sprintf(dataformat,txt,padding0,'"_ArrayIsSparse_": ','1', sep); + if(~isempty(dozip) && numel(data*2)>zipsize) + if(size(item,1)==1) + % Row vector, store only column indices. + fulldata=[iy(:),data']; + elseif(size(item,2)==1) + % Column vector, store only row indices. + fulldata=[ix,data]; + else + % General case, store row and column indices. + fulldata=[ix,iy,data]; + end + txt=sprintf(dataformat,txt,padding0,'"_ArrayCompressionSize_": ',regexprep(mat2str(size(fulldata)),'\s+',','), sep); + txt=sprintf(dataformat,txt,padding0,'"_ArrayCompressionMethod_": "',dozip, ['"' sep]); + if(strcmpi(dozip,'gzip')) + txt=sprintf(dataformat,txt,padding0,'"_ArrayCompressedData_": "',base64encode(gzipencode(typecast(fulldata(:),'uint8'))),['"' nl]); + elseif(strcmpi(dozip,'zlib')) + txt=sprintf(dataformat,txt,padding0,'"_ArrayCompressedData_": "',base64encode(zlibencode(typecast(fulldata(:),'uint8'))),['"' nl]); + else + error('compression method not supported'); + end + else + if(size(item,1)==1) + % Row vector, store only column indices. + txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',... + matdata2json([iy(:),data'],level+2,varargin{:}), nl); + elseif(size(item,2)==1) + % Column vector, store only row indices. + txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',... + matdata2json([ix,data],level+2,varargin{:}), nl); + else + % General case, store row and column indices. + txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',... + matdata2json([ix,iy,data],level+2,varargin{:}), nl); + end + end +else + if(~isempty(dozip) && numel(item)>zipsize) + if(isreal(item)) + fulldata=item(:)'; + if(islogical(fulldata)) + fulldata=uint8(fulldata); + end + else + txt=sprintf(dataformat,txt,padding0,'"_ArrayIsComplex_": ','1', sep); + fulldata=[real(item(:)) imag(item(:))]; + end + txt=sprintf(dataformat,txt,padding0,'"_ArrayCompressionSize_": ',regexprep(mat2str(size(fulldata)),'\s+',','), sep); + txt=sprintf(dataformat,txt,padding0,'"_ArrayCompressionMethod_": "',dozip, ['"' sep]); + if(strcmpi(dozip,'gzip')) + txt=sprintf(dataformat,txt,padding0,'"_ArrayCompressedData_": "',base64encode(gzipencode(typecast(fulldata(:),'uint8'))),['"' nl]); + elseif(strcmpi(dozip,'zlib')) + txt=sprintf(dataformat,txt,padding0,'"_ArrayCompressedData_": "',base64encode(zlibencode(typecast(fulldata(:),'uint8'))),['"' nl]); + else + error('compression method not supported'); + end + else + if(isreal(item)) + txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',... + matdata2json(item(:)',level+2,varargin{:}), nl); + else + txt=sprintf(dataformat,txt,padding0,'"_ArrayIsComplex_": ','1', sep); + txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',... + matdata2json([real(item(:)) imag(item(:))],level+2,varargin{:}), nl); + end + end +end +txt=sprintf('%s%s%s',txt,padding1,'}'); + +%%------------------------------------------------------------------------- +function txt=matlabobject2json(name,item,level,varargin) +import jsonlab.* +if numel(item) == 0 %empty object + st = struct(); +elseif numel(item) == 1 % + st = struct(); + txt = str2json(name, char(item), level, varargin(:)); + return +else + % "st = struct(item);" would produce an inmutable warning, because it + % make the protected and private properties visible. Instead we get the + % visible properties + propertynames = properties(item); + for p = 1:numel(propertynames) + for o = numel(item):-1:1 % aray of objects + st(o).(propertynames{p}) = item(o).(propertynames{p}); + end + end +end +txt=struct2json(name,st,level,varargin{:}); + +%%------------------------------------------------------------------------- +function txt=matlabtable2json(name,item,level,varargin) +import jsonlab.* +if numel(item) == 0 %empty object + st = struct(); +else + % "st = struct(item);" would produce an inmutable warning, because it + % make the protected and private properties visible. Instead we get the + % visible properties + st = struct(); + propertynames = properties(item); + if(isfield(item.Properties,'RowNames') && ~isempty(item.Properties.RowNames)) + rownames=item.Properties.RowNames; + for p = 1:(numel(propertynames)-1) + for j = 1:size(item(:,p),1) + st.(rownames{j}).(propertynames{p}) = item{j,p}; + end + end + else + for p = 1:(numel(propertynames)-1) + for j = 1:size(item(:,p),1) + st(j).(propertynames{p}) = item{j,p}; + end + end + end +end +txt=struct2json(name,st,level,varargin{:}); + +%%------------------------------------------------------------------------- +function txt=matdata2json(mat,level,varargin) +import jsonlab.* + +ws=struct('tab',sprintf('\t'),'newline',sprintf('\n'),'sep',sprintf(',\n')); +ws=jsonopt('whitespaces_',ws,varargin{:}); +tab=ws.tab; +nl=ws.newline; + +if(size(mat,1)==1) + pre=''; + post=''; + level=level-1; +else + pre=sprintf('[%s',nl); + post=sprintf('%s%s]',nl,repmat(tab,1,level-1)); +end + +if(isempty(mat)) + txt='null'; + return; +end +if(isinteger(mat)) + floatformat=jsonopt('FloatFormat','%d',varargin{:}); +else + floatformat=jsonopt('FloatFormat','%.10g',varargin{:}); +end +%if(numel(mat)>1) + formatstr=['[' repmat([floatformat ','],1,size(mat,2)-1) [floatformat sprintf('],%s',nl)]]; +%else +% formatstr=[repmat([floatformat ','],1,size(mat,2)-1) [floatformat sprintf(',\n')]]; +%end + +if(nargin>=2 && size(mat,1)>1 && jsonopt('ArrayIndent',1,varargin{:})==1) + formatstr=[repmat(tab,1,level) formatstr]; +end + +txt=sprintf(formatstr,mat'); +txt(end-length(nl):end)=[]; +if(islogical(mat) && jsonopt('ParseLogical',0,varargin{:})==1) + txt=regexprep(txt,'1','true'); + txt=regexprep(txt,'0','false'); +end +%txt=regexprep(mat2str(mat),'\s+',','); +%txt=regexprep(txt,';',sprintf('],\n[')); +% if(nargin>=2 && size(mat,1)>1) +% txt=regexprep(txt,'\[',[repmat(sprintf('\t'),1,level) '[']); +% end +txt=[pre txt post]; +if(any(isinf(mat(:)))) + txt=regexprep(txt,'([-+]*)Inf',jsonopt('Inf','"$1_Inf_"',varargin{:})); +end +if(any(isnan(mat(:)))) + txt=regexprep(txt,'NaN',jsonopt('NaN','"_NaN_"',varargin{:})); +end + +%%------------------------------------------------------------------------- +function newname=checkname(name,varargin) +import jsonlab.* +isunpack=jsonopt('UnpackHex',1,varargin{:}); +newname=name; +if(isempty(regexp(name,'0x([0-9a-fA-F]+)_','once'))) + return +end +if(isunpack) + isoct=jsonopt('IsOctave',0,varargin{:}); + if(~isoct) + newname=regexprep(name,'(^x|_){1}0x([0-9a-fA-F]+)_','${native2unicode(hex2dec($2))}'); + else + pos=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','start'); + pend=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','end'); + if(isempty(pos)) + return; + end + str0=name; + pos0=[0 pend(:)' length(name)]; + newname=''; + for i=1:length(pos) + newname=[newname str0(pos0(i)+1:pos(i)-1) char(hex2dec(str0(pos(i)+3:pend(i)-1)))]; + end + if(pos(end)~=length(name)) + newname=[newname str0(pos0(end-1)+1:pos0(end))]; + end + end +end + +%%------------------------------------------------------------------------- +function newstr=escapejsonstring(str) +import jsonlab.* +newstr=str; +isoct=exist('OCTAVE_VERSION','builtin'); +if(isoct) + vv=sscanf(OCTAVE_VERSION,'%f'); + if(vv(1)>=3.8) + isoct=0; + end +end +if(isoct) + escapechars={'\\','\"','\/','\a','\f','\n','\r','\t','\v'}; + for i=1:length(escapechars); + newstr=regexprep(newstr,escapechars{i},escapechars{i}); + end + newstr=regexprep(newstr,'\\\\(u[0-9a-fA-F]{4}[^0-9a-fA-F]*)','\$1'); +else + escapechars={'\\','\"','\/','\a','\b','\f','\n','\r','\t','\v'}; + for i=1:length(escapechars); + newstr=regexprep(newstr,escapechars{i},regexprep(escapechars{i},'\\','\\\\')); + end + newstr=regexprep(newstr,'\\\\(u[0-9a-fA-F]{4}[^0-9a-fA-F]*)','\\$1'); +end diff --git a/matlab/+jsonlab/saveubjson.m b/matlab/+jsonlab/saveubjson.m new file mode 100644 index 0000000..f7dd285 --- /dev/null +++ b/matlab/+jsonlab/saveubjson.m @@ -0,0 +1,663 @@ +function json=saveubjson(rootname,obj,varargin) +import jsonlab.* +% +% json=saveubjson(rootname,obj,filename) +% or +% json=saveubjson(rootname,obj,opt) +% json=saveubjson(rootname,obj,'param1',value1,'param2',value2,...) +% +% convert a MATLAB object (cell, struct or array) into a Universal +% Binary JSON (UBJSON) binary string +% +% author: Qianqian Fang (q.fang neu.edu) +% created on 2013/08/17 +% +% $Id$ +% +% input: +% rootname: the name of the root-object, when set to '', the root name +% is ignored, however, when opt.ForceRootName is set to 1 (see below), +% the MATLAB variable name will be used as the root name. +% obj: a MATLAB object (array, cell, cell array, struct, struct array, +% class instance) +% filename: a string for the file name to save the output UBJSON data +% opt: a struct for additional options, ignore to use default values. +% opt can have the following fields (first in [.|.] is the default) +% +% opt.FileName [''|string]: a file name to save the output JSON data +% opt.ArrayToStruct[0|1]: when set to 0, saveubjson outputs 1D/2D +% array in JSON array format; if sets to 1, an +% array will be shown as a struct with fields +% "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for +% sparse arrays, the non-zero elements will be +% saved to _ArrayData_ field in triplet-format i.e. +% (ix,iy,val) and "_ArrayIsSparse_" will be added +% with a value of 1; for a complex array, the +% _ArrayData_ array will include two columns +% (4 for sparse) to record the real and imaginary +% parts, and also "_ArrayIsComplex_":1 is added. +% opt.ParseLogical [1|0]: if this is set to 1, logical array elem +% will use true/false rather than 1/0. +% opt.SingletArray [0|1]: if this is set to 1, arrays with a single +% numerical element will be shown without a square +% bracket, unless it is the root object; if 0, square +% brackets are forced for any numerical arrays. +% opt.SingletCell [1|0]: if 1, always enclose a cell with "[]" +% even it has only one element; if 0, brackets +% are ignored when a cell has only 1 element. +% opt.ForceRootName [0|1]: when set to 1 and rootname is empty, saveubjson +% will use the name of the passed obj variable as the +% root object name; if obj is an expression and +% does not have a name, 'root' will be used; if this +% is set to 0 and rootname is empty, the root level +% will be merged down to the lower level. +% opt.JSONP [''|string]: to generate a JSONP output (JSON with padding), +% for example, if opt.JSON='foo', the JSON data is +% wrapped inside a function call as 'foo(...);' +% opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson +% back to the string form +% opt.Compression 'zlib' or 'gzip': specify array compression +% method; currently only supports 'gzip' or 'zlib'. The +% data compression only applicable to numerical arrays +% in 3D or higher dimensions, or when ArrayToStruct +% is 1 for 1D or 2D arrays. If one wants to +% compress a long string, one must convert +% it to uint8 or int8 array first. The compressed +% array uses three extra fields +% "_ArrayCompressionMethod_": the opt.Compression value. +% "_ArrayCompressionSize_": a 1D interger array to +% store the pre-compressed (but post-processed) +% array dimensions, and +% "_ArrayCompressedData_": the binary stream of +% the compressed binary array data WITHOUT +% 'base64' encoding +% opt.CompressArraySize [100|int]: only to compress an array if the total +% element count is larger than this number. +% +% opt can be replaced by a list of ('param',value) pairs. The param +% string is equivallent to a field in opt and is case sensitive. +% output: +% json: a binary string in the UBJSON format (see http://ubjson.org) +% +% examples: +% jsonmesh=struct('MeshNode',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],... +% 'MeshTetra',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],... +% 'MeshTri',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;... +% 2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],... +% 'MeshCreator','FangQ','MeshTitle','T6 Cube',... +% 'SpecialData',[nan, inf, -inf]); +% saveubjson('jsonmesh',jsonmesh) +% saveubjson('jsonmesh',jsonmesh,'meshdata.ubj') +% +% license: +% BSD, see LICENSE_BSD.txt file for details +% +% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +if(nargin==1) + varname=inputname(1); + obj=rootname; + if(isempty(varname)) + varname='root'; + end + rootname=varname; +else + varname=inputname(2); +end +if(length(varargin)==1 && ischar(varargin{1})) + opt=struct('filename',varargin{1}); +else + opt=varargin2struct(varargin{:}); +end +opt.IsOctave=exist('OCTAVE_VERSION','builtin'); + +dozip=jsonopt('Compression','',opt); +if(~isempty(dozip)) + if(~(strcmpi(dozip,'gzip') || strcmpi(dozip,'zlib'))) + error('compression method "%s" is not supported',dozip); + end + if(exist('zmat')~=3) + try + error(javachk('jvm')); + try + base64decode('test'); + catch + matlab.net.base64decode('test'); + end + catch + error('java-based compression is not supported'); + end + end + opt.Compression=dozip; +end + +if(isfield(opt,'norowbracket')) + warning('Option ''NoRowBracket'' is depreciated, please use ''SingletArray'' and set its value to not(NoRowBracket)'); + if(~isfield(opt,'singletarray')) + opt.singletarray=not(opt.norowbracket); + end +end +rootisarray=0; +rootlevel=1; +forceroot=jsonopt('ForceRootName',0,opt); +if((isnumeric(obj) || islogical(obj) || ischar(obj) || isstruct(obj) || ... + iscell(obj) || isobject(obj)) && isempty(rootname) && forceroot==0) + rootisarray=1; + rootlevel=0; +else + if(isempty(rootname)) + rootname=varname; + end +end +if((isstruct(obj) || iscell(obj))&& isempty(rootname) && forceroot) + rootname='root'; +end +json=obj2ubjson(rootname,obj,rootlevel,opt); +if(~rootisarray) + json=['{' json '}']; +end + +jsonp=jsonopt('JSONP','',opt); +if(~isempty(jsonp)) + json=[jsonp '(' json ')']; +end + +% save to a file if FileName is set, suggested by Patrick Rapin +filename=jsonopt('FileName','',opt); +if(~isempty(filename)) + fid = fopen(filename, 'wb'); + fwrite(fid,json); + fclose(fid); +end + +%%------------------------------------------------------------------------- +function txt=obj2ubjson(name,item,level,varargin) +import jsonlab.* + +if(iscell(item)) + txt=cell2ubjson(name,item,level,varargin{:}); +elseif(isstruct(item)) + txt=struct2ubjson(name,item,level,varargin{:}); +elseif(ischar(item)) + txt=str2ubjson(name,item,level,varargin{:}); +elseif(isobject(item)) + txt=matlabobject2ubjson(name,item,level,varargin{:}); +else + txt=mat2ubjson(name,item,level,varargin{:}); +end + +%%------------------------------------------------------------------------- +function txt=cell2ubjson(name,item,level,varargin) +import jsonlab.* +txt=''; +if(~iscell(item)) + error('input is not a cell'); +end + +dim=size(item); +if(ndims(squeeze(item))>2) % for 3D or higher dimensions, flatten to 2D for now + item=reshape(item,dim(1),numel(item)/dim(1)); + dim=size(item); +end +bracketlevel=~jsonopt('singletcell',1,varargin{:}); +len=numel(item); % let's handle 1D cell first +if(len>bracketlevel) + if(~isempty(name)) + txt=[N_(checkname(name,varargin{:})) '[']; name=''; + else + txt='['; + end +elseif(len==0) + if(~isempty(name)) + txt=[N_(checkname(name,varargin{:})) 'Z']; name=''; + else + txt='Z'; + end +end +for j=1:dim(2) + if(dim(1)>1) + txt=[txt '[']; + end + for i=1:dim(1) + txt=[txt obj2ubjson(name,item{i,j},level+(len>bracketlevel),varargin{:})]; + end + if(dim(1)>1) + txt=[txt ']']; + end +end +if(len>bracketlevel) + txt=[txt ']']; +end + +%%------------------------------------------------------------------------- +function txt=struct2ubjson(name,item,level,varargin) +import jsonlab.* +txt=''; +if(~isstruct(item)) + error('input is not a struct'); +end +dim=size(item); +if(ndims(squeeze(item))>2) % for 3D or higher dimensions, flatten to 2D for now + item=reshape(item,dim(1),numel(item)/dim(1)); + dim=size(item); +end +len=numel(item); +forcearray= (len>1 || (jsonopt('SingletArray',0,varargin{:})==1 && level>0)); + +if(~isempty(name)) + if(forcearray) + txt=[N_(checkname(name,varargin{:})) '[']; + end +else + if(forcearray) + txt='['; + end +end +for j=1:dim(2) + if(dim(1)>1) + txt=[txt '[']; + end + for i=1:dim(1) + names = fieldnames(item(i,j)); + if(~isempty(name) && len==1 && ~forcearray) + txt=[txt N_(checkname(name,varargin{:})) '{']; + else + txt=[txt '{']; + end + if(~isempty(names)) + for e=1:length(names) + txt=[txt obj2ubjson(names{e},item(i,j).(names{e}),... + level+(dim(1)>1)+1+forcearray,varargin{:})]; + end + end + txt=[txt '}']; + end + if(dim(1)>1) + txt=[txt ']']; + end +end +if(forcearray) + txt=[txt ']']; +end + +%%------------------------------------------------------------------------- +function txt=str2ubjson(name,item,level,varargin) +import jsonlab.* +txt=''; +if(~ischar(item)) + error('input is not a string'); +end +item=reshape(item, max(size(item),[1 0])); +len=size(item,1); + +if(~isempty(name)) + if(len>1) + txt=[N_(checkname(name,varargin{:})) '[']; + end +else + if(len>1) + txt='['; + end +end +for e=1:len + val=item(e,:); + if(len==1) + obj=[N_(checkname(name,varargin{:})) '' '',S_(val),'']; + if(isempty(name)) + obj=['',S_(val),'']; + end + txt=[txt,'',obj]; + else + txt=[txt,'',['',S_(val),'']]; + end +end +if(len>1) + txt=[txt ']']; +end + +%%------------------------------------------------------------------------- +function txt=mat2ubjson(name,item,level,varargin) +import jsonlab.* +if(~isnumeric(item) && ~islogical(item)) + error('input is not an array'); +end + +dozip=jsonopt('Compression','',varargin{:}); +zipsize=jsonopt('CompressArraySize',100,varargin{:}); + +if(length(size(item))>2 || issparse(item) || ~isreal(item) || ... + (isempty(item) && any(size(item))) ||jsonopt('ArrayToStruct',0,varargin{:}) || (~isempty(dozip) && numel(item)>zipsize)) + cid=I_(uint32(max(size(item)))); + if(isempty(name)) + txt=['{' N_('_ArrayType_'),S_(class(item)),N_('_ArraySize_'),I_a(size(item),cid(1)) ]; + else + if(isempty(item)) + txt=[N_(checkname(name,varargin{:})),'Z']; + return; + else + txt=[N_(checkname(name,varargin{:})),'{',N_('_ArrayType_'),S_(class(item)),N_('_ArraySize_'),I_a(size(item),cid(1))]; + end + end +else + if(isempty(name)) + txt=matdata2ubjson(item,level+1,varargin{:}); + else + if(numel(item)==1 && jsonopt('SingletArray',0,varargin{:})==0) + numtxt=regexprep(regexprep(matdata2ubjson(item,level+1,varargin{:}),'^\[',''),']$',''); + txt=[N_(checkname(name,varargin{:})) numtxt]; + else + txt=[N_(checkname(name,varargin{:})),matdata2ubjson(item,level+1,varargin{:})]; + end + end + return; +end +if(issparse(item)) + [ix,iy]=find(item); + data=full(item(find(item))); + if(~isreal(item)) + data=[real(data(:)),imag(data(:))]; + if(size(item,1)==1) + % Kludge to have data's 'transposedness' match item's. + % (Necessary for complex row vector handling below.) + data=data'; + end + txt=[txt,N_('_ArrayIsComplex_'),'T']; + end + txt=[txt,N_('_ArrayIsSparse_'),'T']; + if(~isempty(dozip) && numel(data*2)>zipsize) + if(size(item,1)==1) + % Row vector, store only column indices. + fulldata=[iy(:),data']; + elseif(size(item,2)==1) + % Column vector, store only row indices. + fulldata=[ix,data]; + else + % General case, store row and column indices. + fulldata=[ix,iy,data]; + end + cid=I_(uint32(max(size(fulldata)))); + txt=[txt, N_('_ArrayCompressionSize_'),I_a(size(fulldata),cid(1))]; + txt=[txt, N_('_ArrayCompressionMethod_'),S_(dozip)]; + if(strcmpi(dozip,'gzip')) + txt=[txt,N_('_ArrayCompressedData_'), I_a(gzipencode(typecast(fulldata(:),'uint8')),'U')]; + elseif(strcmpi(dozip,'zlib')) + txt=[txt,N_('_ArrayCompressedData_'), I_a(zlibencode(typecast(fulldata(:),'uint8')),'U')]; + else + error('compression method not supported'); + end + else + if(size(item,1)==1) + % Row vector, store only column indices. + txt=[txt,N_('_ArrayData_'),... + matdata2ubjson([iy(:),data'],level+2,varargin{:})]; + elseif(size(item,2)==1) + % Column vector, store only row indices. + txt=[txt,N_('_ArrayData_'),... + matdata2ubjson([ix,data],level+2,varargin{:})]; + else + % General case, store row and column indices. + txt=[txt,N_('_ArrayData_'),... + matdata2ubjson([ix,iy,data],level+2,varargin{:})]; + end + end +else + if(~isempty(dozip) && numel(item)>zipsize) + if(isreal(item)) + fulldata=item(:)'; + if(islogical(fulldata)) + fulldata=uint8(fulldata); + end + else + txt=[txt,N_('_ArrayIsComplex_'),'T']; + fulldata=[real(item(:)) imag(item(:))]; + end + cid=I_(uint32(max(size(fulldata)))); + txt=[txt, N_('_ArrayCompressionSize_'),I_a(size(fulldata),cid(1))]; + txt=[txt, N_('_ArrayCompressionMethod_'),S_(dozip)]; + if(strcmpi(dozip,'gzip')) + txt=[txt,N_('_ArrayCompressedData_'), I_a(gzipencode(typecast(fulldata(:),'uint8')),'U')]; + elseif(strcmpi(dozip,'zlib')) + txt=[txt,N_('_ArrayCompressedData_'), I_a(zlibencode(typecast(fulldata(:),'uint8')),'U')]; + else + error('compression method not supported'); + end + else + if(isreal(item)) + txt=[txt,N_('_ArrayData_'),... + matdata2ubjson(item(:)',level+2,varargin{:})]; + else + txt=[txt,N_('_ArrayIsComplex_'),'T']; + txt=[txt,N_('_ArrayData_'),... + matdata2ubjson([real(item(:)) imag(item(:))],level+2,varargin{:})]; + end + end +end +txt=[txt,'}']; + +%%------------------------------------------------------------------------- +function txt=matlabobject2ubjson(name,item,level,varargin) +import jsonlab.* +st = struct(); +if numel(item) > 0 %non-empty object + % "st = struct(item);" would produce an inmutable warning, because it + % make the protected and private properties visible. Instead we get the + % visible properties + propertynames = properties(item); + for p = 1:numel(propertynames) + for o = numel(item):-1:1 % aray of objects + st(o).(propertynames{p}) = item(o).(propertynames{p}); + end + end +end +txt=struct2ubjson(name,st,level,varargin{:}); + +%%------------------------------------------------------------------------- +function txt=matdata2ubjson(mat,level,varargin) +import jsonlab.* +if(isempty(mat)) + txt='Z'; + return; +end +type=''; +hasnegtive=(mat<0); +if(isa(mat,'integer') || isinteger(mat) || (isfloat(mat) && all(mod(mat(:),1) == 0))) + if(isempty(hasnegtive)) + if(max(mat(:))<=2^8) + type='U'; + end + end + if(isempty(type)) + % todo - need to consider negative ones separately + id= histc(abs(max(double(mat(:)))),[0 2^7 2^15 2^31 2^63]); + if(isempty(id~=0)) + error('high-precision data is not yet supported'); + end + key='iIlL'; + type=key(id~=0); + end + txt=[I_a(mat(:),type,size(mat))]; +elseif(islogical(mat)) + logicalval='FT'; + if(numel(mat)==1) + txt=logicalval(mat+1); + else + txt=['[$U#' I_a(size(mat),'l') typecast(swapbytes(uint8(mat(:)')),'uint8')]; + end +else + if(numel(mat)==1) + txt=['[' D_(mat) ']']; + else + txt=D_a(mat(:),'D',size(mat)); + end +end + +%txt=regexprep(mat2str(mat),'\s+',','); +%txt=regexprep(txt,';',sprintf('],[')); +% if(nargin>=2 && size(mat,1)>1) +% txt=regexprep(txt,'\[',[repmat(sprintf('\t'),1,level) '[']); +% end +if(any(isinf(mat(:)))) + txt=regexprep(txt,'([-+]*)Inf',jsonopt('Inf','"$1_Inf_"',varargin{:})); +end +if(any(isnan(mat(:)))) + txt=regexprep(txt,'NaN',jsonopt('NaN','"_NaN_"',varargin{:})); +end + +%%------------------------------------------------------------------------- +function newname=checkname(name,varargin) +import jsonlab.* +isunpack=jsonopt('UnpackHex',1,varargin{:}); +newname=name; +if(isempty(regexp(name,'0x([0-9a-fA-F]+)_','once'))) + return +end +if(isunpack) + isoct=jsonopt('IsOctave',0,varargin{:}); + if(~isoct) + newname=regexprep(name,'(^x|_){1}0x([0-9a-fA-F]+)_','${native2unicode(hex2dec($2))}'); + else + pos=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','start'); + pend=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','end'); + if(isempty(pos)) + return; + end + str0=name; + pos0=[0 pend(:)' length(name)]; + newname=''; + for i=1:length(pos) + newname=[newname str0(pos0(i)+1:pos(i)-1) char(hex2dec(str0(pos(i)+3:pend(i)-1)))]; + end + if(pos(end)~=length(name)) + newname=[newname str0(pos0(end-1)+1:pos0(end))]; + end + end +end +%%------------------------------------------------------------------------- +function val=N_(str) +import jsonlab.* +val=[I_(int32(length(str))) str]; +%%------------------------------------------------------------------------- +function val=S_(str) +import jsonlab.* +if(length(str)==1) + val=['C' str]; +else + val=['S' I_(int32(length(str))) str]; +end +%%------------------------------------------------------------------------- +function val=I_(num) +import jsonlab.* +if(~isinteger(num)) + error('input is not an integer'); +end +if(num>=0 && num<255) + val=['U' data2byte(swapbytes(cast(num,'uint8')),'uint8')]; + return; +end +key='iIlL'; +cid={'int8','int16','int32','int64'}; +for i=1:4 + if((num>0 && num<2^(i*8-1)) || (num<0 && num>=-2^(i*8-1))) + val=[key(i) data2byte(swapbytes(cast(num,cid{i})),'uint8')]; + return; + end +end +error('unsupported integer'); + +%%------------------------------------------------------------------------- +function val=D_(num) +import jsonlab.* +if(~isfloat(num)) + error('input is not a float'); +end + +if(isa(num,'single')) + val=['d' data2byte(swapbytes(num),'uint8')]; +else + val=['D' data2byte(swapbytes(num),'uint8')]; +end +%%------------------------------------------------------------------------- +function data=I_a(num,type,dim,format) +import jsonlab.* +id=find(ismember('iUIlL',type)); + +if(id==0) + error('unsupported integer array'); +end + +% based on UBJSON specs, all integer types are stored in big endian format + +if(id==1) + data=data2byte(swapbytes(int8(num)),'uint8'); + blen=1; +elseif(id==2) + data=data2byte(swapbytes(uint8(num)),'uint8'); + blen=1; +elseif(id==3) + data=data2byte(swapbytes(int16(num)),'uint8'); + blen=2; +elseif(id==4) + data=data2byte(swapbytes(int32(num)),'uint8'); + blen=4; +elseif(id==5) + data=data2byte(swapbytes(int64(num)),'uint8'); + blen=8; +end + +if(nargin>=3 && length(dim)>=2 && prod(dim)~=dim(2)) + format='opt'; +end +if((nargin<4 || strcmp(format,'opt')) && numel(num)>1) + if(nargin>=3 && (length(dim)==1 || (length(dim)>=2 && prod(dim)~=dim(2)))) + cid=I_(uint32(max(dim))); + data=['$' type '#' I_a(dim,cid(1)) data(:)']; + else + data=['$' type '#' I_(int32(numel(data)/blen)) data(:)']; + end + data=['[' data(:)']; +else + data=reshape(data,blen,numel(data)/blen); + data(2:blen+1,:)=data; + data(1,:)=type; + data=data(:)'; + data=['[' data(:)' ']']; +end +%%------------------------------------------------------------------------- +function data=D_a(num,type,dim,format) +import jsonlab.* +id=find(ismember('dD',type)); + +if(id==0) + error('unsupported float array'); +end + +if(id==1) + data=data2byte(swapbytes(single(num)),'uint8'); +elseif(id==2) + data=data2byte(swapbytes(double(num)),'uint8'); +end + +if(nargin>=3 && length(dim)>=2 && prod(dim)~=dim(2)) + format='opt'; +end +if((nargin<4 || strcmp(format,'opt')) && numel(num)>1) + if(nargin>=3 && (length(dim)==1 || (length(dim)>=2 && prod(dim)~=dim(2)))) + cid=I_(uint32(max(dim))); + data=['$' type '#' I_a(dim,cid(1)) data(:)']; + else + data=['$' type '#' I_(int32(numel(data)/(id*4))) data(:)']; + end + data=['[' data]; +else + data=reshape(data,(id*4),length(data)/(id*4)); + data(2:(id*4+1),:)=data; + data(1,:)=type; + data=data(:)'; + data=['[' data(:)' ']']; +end +%%------------------------------------------------------------------------- +function bytes=data2byte(varargin) +import jsonlab.* +bytes=typecast(varargin{:}); +bytes=char(bytes(:)'); diff --git a/matlab/+jsonlab/struct2jdata.m b/matlab/+jsonlab/struct2jdata.m new file mode 100644 index 0000000..581509f --- /dev/null +++ b/matlab/+jsonlab/struct2jdata.m @@ -0,0 +1,126 @@ +function newdata=struct2jdata(data,varargin) +import jsonlab.* +% +% newdata=struct2jdata(data,opt,...) +% +% convert a JData object (in the form of a struct array) into an array +% +% authors:Qianqian Fang (q.fang neu.edu) +% +% input: +% data: a struct array. If data contains JData keywords in the first +% level children, these fields are parsed and regrouped into a +% data object (arrays, trees, graphs etc) based on JData +% specification. The JData keywords are +% "_ArrayType_", "_ArraySize_", "_ArrayData_" +% "_ArrayIsSparse_", "_ArrayIsComplex_", +% "_ArrayCompressionMethod_", "_ArrayCompressionSize", +% "_ArrayCompressedData_" +% opt: (optional) a list of 'Param',value pairs for additional options +% The supported options include +% 'Recursive', if set to 1, will apply the conversion to +% every child; 0 to disable +% 'Base64'. if set to 1, _ArrayCompressedData_ is assumed to +% be encoded with base64 format and need to be +% decoded first. This is needed for JSON but not +% UBJSON data +% +% output: +% newdata: the covnerted data if the input data does contain a JData +% structure; otherwise, the same as the input. +% +% examples: +% obj=struct('_ArrayType_','double','_ArraySize_',[2 3], +% '_ArrayIsSparse_',1 ,'_ArrayData_',null); +% ubjdata=struct2jdata(obj); +% +% license: +% BSD, see LICENSE_BSD.txt file for details +% +% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +fn=fieldnames(data); +newdata=data; +len=length(data); +needbase64=jsonopt('Base64',1,varargin{:}); +if(jsonopt('Recursive',0,varargin{:})==1) + for i=1:length(fn) % depth-first + for j=1:len + if(isstruct(getfield(data(j),fn{i}))) + newdata(j)=setfield(newdata(j),fn{i},jstruct2array(getfield(data(j),fn{i}))); + end + end + end +end +if(~isempty(strmatch('x0x5F_ArrayType_',fn)) && (~isempty(strmatch('x0x5F_ArrayData_',fn)) || ~isempty(strmatch('x0x5F_ArrayCompressedData_',fn)))) + newdata=cell(len,1); + for j=1:len + if(~isempty(strmatch('x0x5F_ArrayCompressionSize_',fn)) && ~isempty(strmatch('x0x5F_ArrayCompressedData_',fn))) + zipmethod='zip'; + if(~isempty(strmatch('x0x5F_ArrayCompressionMethod_',fn))) + zipmethod=data(j).x0x5F_ArrayCompressionMethod_; + end + if(strcmpi(zipmethod,'gzip')) + if(needbase64) + ndata=reshape(typecast(gzipdecode(base64decode(data(j).x0x5F_ArrayCompressedData_)),data(j).x0x5F_ArrayType_),data(j).x0x5F_ArrayCompressionSize_); + else + ndata=reshape(typecast(gzipdecode(data(j).x0x5F_ArrayCompressedData_),data(j).x0x5F_ArrayType_),data(j).x0x5F_ArrayCompressionSize_); + end + elseif(strcmpi(zipmethod,'zlib')) + if(needbase64) + ndata=reshape(typecast(zlibdecode(base64decode(data(j).x0x5F_ArrayCompressedData_)),data(j).x0x5F_ArrayType_),data(j).x0x5F_ArrayCompressionSize_); + else + ndata=reshape(typecast(zlibdecode(data(j).x0x5F_ArrayCompressedData_),data(j).x0x5F_ArrayType_),data(j).x0x5F_ArrayCompressionSize_); + end + else + error('compression method is not supported'); + end + else + ndata=cast(data(j).x0x5F_ArrayData_,data(j).x0x5F_ArrayType_); + end + iscpx=0; + if(~isempty(strmatch('x0x5F_ArrayIsComplex_',fn))) + if(data(j).x0x5F_ArrayIsComplex_) + iscpx=1; + end + end + if(~isempty(strmatch('x0x5F_ArrayIsSparse_',fn))) + if(data(j).x0x5F_ArrayIsSparse_) + if(~isempty(strmatch('x0x5F_ArraySize_',fn))) + dim=double(data(j).x0x5F_ArraySize_); + if(iscpx && size(ndata,2)==4-any(dim==1)) + ndata(:,end-1)=complex(ndata(:,end-1),ndata(:,end)); + end + if isempty(ndata) + % All-zeros sparse + ndata=sparse(dim(1),prod(dim(2:end))); + elseif dim(1)==1 + % Sparse row vector + ndata=sparse(1,ndata(:,1),ndata(:,2),dim(1),prod(dim(2:end))); + elseif dim(2)==1 + % Sparse column vector + ndata=sparse(ndata(:,1),1,ndata(:,2),dim(1),prod(dim(2:end))); + else + % Generic sparse array. + ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3),dim(1),prod(dim(2:end))); + end + else + if(iscpx && size(ndata,2)==4) + ndata(:,3)=complex(ndata(:,3),ndata(:,4)); + end + ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3)); + end + end + elseif(~isempty(strmatch('x0x5F_ArraySize_',fn))) + if(iscpx && size(ndata,2)==2) + ndata=complex(ndata(:,1),ndata(:,2)); + end + ndata=reshape(ndata(:),data(j).x0x5F_ArraySize_); + end + newdata{j}=ndata; + end + if(len==1) + newdata=newdata{1}; + end +end \ No newline at end of file diff --git a/matlab/+jsonlab/varargin2struct.m b/matlab/+jsonlab/varargin2struct.m new file mode 100644 index 0000000..131bdc1 --- /dev/null +++ b/matlab/+jsonlab/varargin2struct.m @@ -0,0 +1,41 @@ +function opt=varargin2struct(varargin) +import jsonlab.* +% +% opt=varargin2struct('param1',value1,'param2',value2,...) +% or +% opt=varargin2struct(...,optstruct,...) +% +% convert a series of input parameters into a structure +% +% authors:Qianqian Fang (q.fang neu.edu) +% date: 2012/12/22 +% +% input: +% 'param', value: the input parameters should be pairs of a string and a value +% optstruct: if a parameter is a struct, the fields will be merged to the output struct +% +% output: +% opt: a struct where opt.param1=value1, opt.param2=value2 ... +% +% license: +% BSD, see LICENSE_BSD.txt file for details +% +% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +len=length(varargin); +opt=struct; +if(len==0) return; end +i=1; +while(i<=len) + if(isstruct(varargin{i})) + opt=mergestruct(opt,varargin{i}); + elseif(ischar(varargin{i}) && i 1 + msgpack = dumpbin(data); + elseif islogical(data) + if data + msgpack = {uint8(195)}; % encode true + else + msgpack = {uint8(194)}; % encode false + end + elseif isinteger(data) + msgpack = {dumpinteger(data)}; + elseif isnumeric(data) + msgpack = {dumpfloat(data)}; + elseif ischar(data) + msgpack = dumpstring(data); + elseif iscell(data) + msgpack = dumpcell(data); + elseif isa(data, 'containers.Map') + msgpack = dumpmap(data); + else + error('transplant:dumpmsgpack:unknowntype', ... + ['Unknown type "' class(data) '"']); + end +end + +function bytes = scalar2bytes(value) + % reverse byte order to convert from little endian to big endian + bytes = typecast(swapbytes(value), 'uint8'); +end + +function msgpack = dumpinteger(value) + % if the values are small enough, encode as fixnum: + if value >= 0 && value < 128 + % first bit is 0, last 7 bits are value + msgpack = uint8(value); + return + elseif value < 0 && value > -32 + % first three bits are 111, last 5 bytes are value + msgpack = typecast(int8(value), 'uint8'); + return + end + + % otherwise, encode by type: + switch class(value) + case 'uint8' % encode as uint8 + msgpack = uint8([204, value]); + case 'uint16' % encode as uint16 + msgpack = uint8([205, scalar2bytes(value)]); + case 'uint32' % encode as uint32 + msgpack = uint8([206, scalar2bytes(value)]); + case 'uint64' % encode as uint64 + msgpack = uint8([207, scalar2bytes(value)]); + case 'int8' % encode as int8 + msgpack = uint8([208, scalar2bytes(value)]); + case 'int16' % encode as int16 + msgpack = uint8([209, scalar2bytes(value)]); + case 'int32' % encode as int32 + msgpack = uint8([210, scalar2bytes(value)]); + case 'int64' % encode as int64 + msgpack = uint8([211, scalar2bytes(value)]); + otherwise + error('transplant:dumpmsgpack:unknowninteger', ... + ['Unknown integer type "' class(value) '"']); + end +end + +function msgpack = dumpfloat(value) + % do double first, as it is more common in Matlab + if isa(value, 'double') % encode as float64 + msgpack = uint8([203, scalar2bytes(value)]); + elseif isa(value, 'single') % encode as float32 + msgpack = uint8([202, scalar2bytes(value)]); + else + error('transplant:dumpmsgpack:unknownfloat', ... + ['Unknown float type "' class(value) '"']); + end +end + +function msgpack = dumpstring(value) + b10100000 = 160; + + encoded = unicode2native(value, 'utf-8'); + len = length(encoded); + + if len < 32 % encode as fixint: + % first three bits are 101, last 5 are length: + msgpack = {uint8(bitor(len, b10100000)), encoded}; + elseif len < 256 % encode as str8 + msgpack = {uint8([217, len]), encoded}; + elseif len < 2^16 % encode as str16 + msgpack = {uint8(218), scalar2bytes(uint16(len)), encoded}; + elseif len < 2^32 % encode as str32 + msgpack = {uint8(219), scalar2bytes(uint32(len)), encoded}; + else + error('transplant:dumpmsgpack:stringtoolong', ... + sprintf('String is too long (%d bytes)', len)); + end +end + +function msgpack = dumpbin(value) + len = length(value); + if len < 256 % encode as bin8 + msgpack = {uint8([196, len]) value(:)'}; + elseif len < 2^16 % encode as bin16 + msgpack = {uint8(197), scalar2bytes(uint16(len)), value(:)'}; + elseif len < 2^32 % encode as bin32 + msgpack = {uint8(198), scalar2bytes(uint32(len)), value(:)'}; + else + error('transplant:dumpmsgpack:bintoolong', ... + sprintf('Bin is too long (%d bytes)', len)); + end +end + +function msgpack = dumpcell(value) + b10010000 = 144; + + % Msgpack can only work with 1D-arrays. Thus, Convert a + % multidimensional AxBxC array into a cell-of-cell-of-cell, so + % that indexing value{a, b, c} becomes value{a}{b}{c}. + if length(value) ~= prod(size(value)) + for n=ndims(value):-1:2 + value = cellfun(@squeeze, num2cell(value, n), ... + 'uniformoutput', false); + end + end + + % write header + len = length(value); + if len < 16 % encode as fixarray + % first four bits are 1001, last 4 are length + msgpack = {uint8(bitor(len, b10010000))}; + elseif len < 2^16 % encode as array16 + msgpack = {uint8(220), scalar2bytes(uint16(len))}; + elseif len < 2^32 % encode as array32 + msgpack = {uint8(221), scalar2bytes(uint32(len))}; + else + error('transplant:dumpmsgpack:arraytoolong', ... + sprintf('Array is too long (%d elements)', len)); + end + + % write values + for n=1:len + stuff = dump(value{n}); + msgpack = [msgpack stuff{:}]; + end +end + +function msgpack = dumpmap(value) + b10000000 = 128; + + % write header + len = length(value); + if len < 16 % encode as fixmap + % first four bits are 1000, last 4 are length + msgpack = {uint8(bitor(len, b10000000))}; + elseif len < 2^16 % encode as map16 + msgpack = {uint8(222), scalar2bytes(uint16(len))}; + elseif len < 2^32 % encode as map32 + msgpack = {uint8(223), scalar2bytes(uint32(len))}; + else + error('transplant:dumpmsgpack:maptoolong', ... + sprintf('Map is too long (%d elements)', len)); + end + + % write key-value pairs + keys = value.keys(); + values = value.values(); + for n=1:len + keystuff = dump(keys{n}); + valuestuff = dump(values{n}); + msgpack = [msgpack, keystuff{:}, valuestuff{:}]; + end +end diff --git a/matlab/+msgpack/dumpmsgpack_test.m b/matlab/+msgpack/dumpmsgpack_test.m new file mode 100644 index 0000000..a1af48f --- /dev/null +++ b/matlab/+msgpack/dumpmsgpack_test.m @@ -0,0 +1,96 @@ +%% positive integer dumping +if dumpmsgpack(int8(0)) ~= uint8(0) + error('Dumping 0 failed') +end +if any(dumpmsgpack(uint8(1)) ~= uint8(1)) + error('Dumping positive fixnum failed') +end +if any(dumpmsgpack(uint8(128)) ~= uint8([204, 128])) + error('Dumping uint8 failed') +end +if any(dumpmsgpack(uint16(256)) ~= uint8([205, 1, 0])) + error('Dumping uint16 failed') +end +if any(dumpmsgpack(uint32(2^16)) ~= uint8([206, 0, 1, 0, 0])) + error('Dumping uint32 failed') +end +if any(dumpmsgpack(uint64(2^32)) ~= uint8([207, 0, 0, 0, 1, 0, 0, 0, 0])) + error('Dumping uint64 failed') +end + +%% negative integer dumping +if dumpmsgpack(int8(-1)) ~= uint8(255) + error('Dumping negative fixnum failed') +end +if any(dumpmsgpack(int8(-128)) ~= uint8([208, 128])) + error('Dumping int8 failed') +end +if any(dumpmsgpack(int16(-256)) ~= uint8([209, 255, 0])) + error('Dumping int16 failed') +end +if any(dumpmsgpack(int32(-2^16)) ~= uint8([210, 255, 255, 0, 0])) + error('Dumping int32 failed') +end +if any(dumpmsgpack(int64(-2^32)) ~= uint8([211, 255, 255, 255, 255, 0, 0, 0, 0])) + error('Dumping int64 failed') +end + +%% float dumping +if any(dumpmsgpack(single(1.5)) ~= uint8([202, 63, 192, 0, 0])) + error('Dumping float32 failed') +end +if any(dumpmsgpack(double(1.5)) ~= uint8([203, 63, 248, 0, 0, 0, 0, 0, 0])) + error('Dumping float64 failed') +end + +%% string dumping +if any(dumpmsgpack('foo') ~= uint8([163, 102, 111, 111])) + error('Dumping fixstr failed') +end +if any(dumpmsgpack(repmat('a', [1, 32])) ~= uint8([217, 32, ones(1, 32)*'a'])) + error('Dumping str8 failed') +end +if any(dumpmsgpack(repmat('a', [1, 2^8])) ~= uint8([218, 1, 0, ones(1, 2^8)*'a'])) + error('Dumping str16 failed') +end +if any(dumpmsgpack(repmat('a', [1, 2^16])) ~= uint8([219, 0, 1, 0, 0, ones(1, 2^16)*'a'])) + error('Dumping str16 failed') +end + +%% bin dumping +if any(dumpmsgpack(repmat(uint8(42), [1, 32])) ~= uint8([196, 32, ones(1, 32)*42])) + error('Dumping str8 failed') +end +if any(dumpmsgpack(repmat(uint8(42), [1, 2^8])) ~= uint8([197, 1, 0, ones(1, 2^8)*42])) + error('Dumping str16 failed') +end +if any(dumpmsgpack(repmat(uint8(42), [1, 2^16])) ~= uint8([198, 0, 1, 0, 0, ones(1, 2^16)*42])) + error('Dumping str16 failed') +end + +%% array dumping +if any(dumpmsgpack({uint8(1), uint8(2)}) ~= uint8([146, 1, 2])) + error('Dumping fixarray failed') +end +if any(dumpmsgpack(num2cell(repmat(uint8(42), [1, 16]))) ~= uint8([220, 0, 16, repmat(42, [1, 16])])) + error('Dumping array16 failed') +end +% takes too long: +% if any(dumpmsgpack(num2cell(repmat(uint8(42), [1, 2^16]))) ~= uint8([221, 0, 1, 0, 0 repmat(42, [1, 2^16])])) +% error('Dumping array32 failed') +% end + +%% map dumping +if any(dumpmsgpack(struct('one', uint8(1), 'two', uint8(2))) ~= uint8([130, dumpmsgpack('one'), 1, dumpmsgpack('two'), 2])) + error('Dumping fixmap failed') +end +data = struct(); +msgpack = uint8([]); +for n=[1 10 11 12 13 14 15 16 2 3 4 5 6 7 8 9] % default struct field order + data.(['x' num2str(n)]) = uint8(n); + msgpack = [msgpack dumpmsgpack(['x' num2str(n)]) uint8(n)]; +end +if any(dumpmsgpack(data) ~= uint8([222, 0, 16, msgpack])) + error('Dumping map16 failed') +end +% map32 takes too long diff --git a/matlab/+msgpack/parsemsgpack.m b/matlab/+msgpack/parsemsgpack.m new file mode 100644 index 0000000..02cc9c3 --- /dev/null +++ b/matlab/+msgpack/parsemsgpack.m @@ -0,0 +1,194 @@ +%PARSEMSGPACK parses a msgpack byte buffer into Matlab data structures +% PARSEMSGPACK(BYTES) +% reads BYTES as msgpack data, and creates Matlab data structures +% from it. The number of bytes consumed by the parsemsgpack call +% is returned in the variable IDX. +% - strings are converted to strings +% - numbers are converted to appropriate numeric values +% - true, false are converted to logical 1, 0 +% - nil is converted to [] +% - arrays are converted to cell arrays +% - maps are converted to containers.Map + +% (c) 2016 Bastian Bechtold +% This code is licensed under the BSD 3-clause license + +function [obj, idx] = parsemsgpack(bytes) + [obj, idx] = parse(uint8(bytes(:)), 1); +end + +function [obj, idx] = parse(bytes, idx) + % masks: + b10000000 = 128; + b01111111 = 127; + b11000000 = 192; + b00111111 = 63; + b11100000 = 224; + b00011111 = 31; + b11110000 = 240; + b00001111 = 15; + % values: + b00000000 = 0; + b10010000 = 144; + b10100000 = 160; + + currentbyte = bytes(idx); + + if bitand(b10000000, currentbyte) == b00000000 + % decode positive fixint + obj = int8(currentbyte); + idx = idx + 1; + return + elseif bitand(b11100000, currentbyte) == b11100000 + % decode negative fixint + obj = typecast(currentbyte, 'int8'); + idx = idx + 1; + return + elseif bitand(b11110000, currentbyte) == b10000000 + % decode fixmap + len = double(bitand(b00001111, currentbyte)); + [obj, idx] = parsemap(len, bytes, idx+1); + return + elseif bitand(b11110000, currentbyte) == b10010000 + % decode fixarray + len = double(bitand(b00001111, currentbyte)); + [obj, idx] = parsearray(len, bytes, idx+1); + return + elseif bitand(b11100000, currentbyte) == b10100000 + % decode fixstr + len = double(bitand(b00011111, currentbyte)); + [obj, idx] = parsestring(len, bytes, idx + 1); + return + end + + switch currentbyte + case 192 % nil + obj = []; + idx = idx+1; + % case 193 % unused + case 194 % false + obj = false; + idx = idx+1; + case 195 % true + obj = true; + idx = idx+1; + case 196 % bin8 + len = double(bytes(idx+1)); + [obj, idx] = parsebytes(len, bytes, idx+2); + case 197 % bin16 + len = double(bytes2scalar(bytes(idx+1:idx+2), 'uint16')); + [obj, idx] = parsebytes(len, bytes, idx+3); + case 198 % bin32 + len = double(bytes2scalar(bytes(idx+1:idx+4), 'uint32')); + [obj, idx] = parsebytes(len, bytes, idx+5); + case 199 % ext8 + len = double(bytes(idx+1)); + [obj, idx] = parseext(len, bytes, idx+2); + case 200 % ext16 + len = double(bytes2scalar(bytes(idx+1:idx+2), 'uint16')); + [obj, idx] = parseext(len, bytes, idx+3); + case 201 % ext32 + len = double(bytes2scalar(bytes(idx+1:idx+4), 'uint32')); + [obj, idx] = parseext(len, bytes, idx+5); + case 202 % float32 + obj = bytes2scalar(bytes(idx+1:idx+4), 'single'); + idx = idx+5; + case 203 % float64 + obj = bytes2scalar(bytes(idx+1:idx+8), 'double'); + idx = idx+9; + case 204 % uint8 + obj = bytes(idx+1); + idx = idx+2; + case 205 % uint16 + obj = bytes2scalar(bytes(idx+1:idx+2), 'uint16'); + idx = idx+3; + case 206 % uint32 + obj = bytes2scalar(bytes(idx+1:idx+4), 'uint32'); + idx = idx+5; + case 207 % uint64 + obj = bytes2scalar(bytes(idx+1:idx+8), 'uint64'); + idx = idx+9; + case 208 % int8 + obj = bytes2scalar(bytes(idx+1), 'int8'); + idx = idx+2; + case 209 % int16 + obj = bytes2scalar(bytes(idx+1:idx+2), 'int16'); + idx = idx+3; + case 210 % int32 + obj = bytes2scalar(bytes(idx+1:idx+4), 'int32'); + idx = idx+5; + case 211 % int64 + obj = bytes2scalar(bytes(idx+1:idx+8), 'int64'); + idx = idx+9; + case 212 % fixext1 + [obj, idx] = parseext(1, bytes, idx+1); + case 213 % fixext2 + [obj, idx] = parseext(2, bytes, idx+1); + case 214 % fixext4 + [obj, idx] = parseext(4, bytes, idx+1); + case 215 % fixext8 + [obj, idx] = parseext(8, bytes, idx+1); + case 216 % fixext16 + [obj, idx] = parseext(16, bytes, idx+1); + case 217 % str8 + len = double(bytes(idx+1)); + [obj, idx] = parsestring(len, bytes, idx+2); + case 218 % str16 + len = double(bytes2scalar(bytes(idx+1:idx+2), 'uint16')); + [obj, idx] = parsestring(len, bytes, idx+3); + case 219 % str32 + len = double(bytes2scalar(bytes(idx+1:idx+4), 'uint32')); + [obj, idx] = parsestring(len, bytes, idx+5); + case 220 % array16 + len = double(bytes2scalar(bytes(idx+1:idx+2), 'uint16')); + [obj, idx] = parsearray(len, bytes, idx+3); + case 221 % array32 + len = double(bytes2scalar(bytes(idx+1:idx+4), 'uint32')); + [obj, idx] = parsearray(len, bytes, idx+5); + case 222 % map16 + len = double(bytes2scalar(bytes(idx+1:idx+2), 'uint16')); + [obj, idx] = parsemap(len, bytes, idx+3); + case 223 % map32 + len = double(bytes2scalar(bytes(idx+1:idx+4), 'uint32')); + [obj, idx] = parsemap(len, bytes, idx+5); + otherwise + error('transplant:parsemsgpack:unknowntype', ... + ['Unknown type "' dec2bin(currentbyte) '"']); + end +end + +function value = bytes2scalar(bytes, type) + % reverse byte order to convert from little-endian to big-endian + value = typecast(bytes(end:-1:1), type); +end + +function [str, idx] = parsestring(len, bytes, idx) + str = native2unicode(bytes(idx:idx+len-1)', 'utf-8'); + idx = idx + len; +end + +function [out, idx] = parsebytes(len, bytes, idx) + out = bytes(idx:idx+len-1); + idx = idx + len; +end + +function [out, idx] = parseext(len, bytes, idx) + out.type = bytes(idx); + out.data = bytes(idx+1:idx+len); + idx = idx + len + 1; +end + +function [out, idx] = parsearray(len, bytes, idx) + out = cell(1, len); + for n=1:len + [out{n}, idx] = parse(bytes, idx); + end +end + +function [out, idx] = parsemap(len, bytes, idx) + out = containers.Map(); + for n=1:len + [key, idx] = parse(bytes, idx); + [out(key), idx] = parse(bytes, idx); + end +end diff --git a/matlab/+msgpack/parsemsgpack_test.m b/matlab/+msgpack/parsemsgpack_test.m new file mode 100644 index 0000000..0d49039 --- /dev/null +++ b/matlab/+msgpack/parsemsgpack_test.m @@ -0,0 +1,111 @@ +%% positive integer parsing +if parsemsgpack(uint8(0)) ~= uint8(0) + error('Parsing 0 failed') +end +if any(parsemsgpack(uint8(1)) ~= uint8(1)) + error('Parsing positive fixnum failed') +end +if any(parsemsgpack(uint8([204, 128])) ~= uint8(128)) + error('Parsing uint8 failed') +end +if any(parsemsgpack(uint8([205, 1, 0])) ~= uint16(256)) + error('Parsing uint16 failed') +end +if any(parsemsgpack(uint8([206, 0, 1, 0, 0])) ~= uint32(2^16)) + error('Parsing uint32 failed') +end +if any(parsemsgpack(uint8([207, 0, 0, 0, 1, 0, 0, 0, 0])) ~= uint64(2^32)) + error('Parsing uint64 failed') +end + +%% negative integer parsing +if any(parsemsgpack(uint8(255)) ~= int8(-1)) + error('Parsing negative fixnum failed') +end +if any(parsemsgpack(uint8([208, 128])) ~= int8(-128)) + error('Parsing int8 failed') +end +if any(parsemsgpack(uint8([209, 255, 0])) ~= int16(-256)) + error('Parsing int16 failed') +end +if any(parsemsgpack(uint8([210, 255, 255, 0, 0])) ~= int32(-2^16)) + error('Parsing int32 failed') +end +if any(parsemsgpack(uint8([211, 255, 255, 255, 255, 0, 0, 0, 0])) ~= int64(-2^32)) + error('Parsing int64 failed') +end + +%% float parsing +if any(parsemsgpack(uint8([202, 63, 192, 0, 0])) ~= single(1.5)) + error('Parsing float32 failed') +end +if any(parsemsgpack(uint8([203, 63, 248, 0, 0, 0, 0, 0, 0])) ~= double(1.5)) + error('Parsing float64 failed') +end + +%% string parsing +if any(parsemsgpack(uint8([163, 102, 111, 111])) ~= 'foo') + error('Parsing fixstr failed') +end +if any(parsemsgpack(uint8([217, 32, ones(1, 32)*'a'])) ~= repmat('a', [1, 32])) + error('Parsing str8 failed') +end +if any(parsemsgpack(uint8([218, 1, 0, ones(1, 2^8)*'a'])) ~= repmat('a', [1, 2^8])) + error('Parsing str16 failed') +end +if any(parsemsgpack(uint8([219, 0, 1, 0, 0, ones(1, 2^16)*'a'])) ~= repmat('a', [1, 2^16])) + error('Parsing str16 failed') +end + +%% bin parsing +if any(parsemsgpack(uint8([196, 32, ones(1, 32)*42])) ~= repmat(uint8(42), [1, 32])) + error('Parsing str8 failed') +end +if any(parsemsgpack(uint8([197, 1, 0, ones(1, 2^8)*42])) ~= repmat(uint8(42), [1, 2^8])) + error('Parsing str16 failed') +end +if any(parsemsgpack(uint8([198, 0, 1, 0, 0, ones(1, 2^16)*42])) ~= repmat(uint8(42), [1, 2^16])) + error('Parsing str16 failed') +end + +%% array parsing +c = parsemsgpack(uint8([146, 1, 2])); +d = {uint8(1), uint8(2)}; +for n=1:max([length(c), length(d)]) + if c{n} ~= d{n} + error('Parsing fixarray failed') + end +end +c = parsemsgpack(uint8([220, 0, 16, repmat(42, [1, 16])])); +d = num2cell(repmat(uint8(42), [1, 16])); +for n=1:max([length(c), length(d)]) + if c{n} ~= d{n} + error('Parsing array16 failed') + end +end +% array32 takes too long + +%% map parsing +c = parsemsgpack(uint8([130, dumpmsgpack('one'), 1, dumpmsgpack('two'), 2])); +d = struct('one', uint8(1), 'two', uint8(2)); +f = [fieldnames(d)' c.keys()]; +for n=1:length(f) + if c(f{n}) ~= d.(f{n}) + error('Parsing fixmap failed') + end +end +data = struct(); +msgpack = uint8([222, 0, 16]); +for n=[1 10 11 12 13 14 15 16 2 3 4 5 6 7 8 9] % default struct field order + data.(['x' num2str(n)]) = uint8(n); + msgpack = [msgpack dumpmsgpack(['x' num2str(n)]) uint8(n)]; +end +c = parsemsgpack(msgpack); +d = data; +f = [fieldnames(d)' c.keys()]; +for n=1:length(f) + if c(f{n}) ~= d.(f{n}) + error('Parsing map16 failed') + end +end +% map32 takes too long diff --git a/matlab/@ustar/ustar.m b/matlab/@ustar/ustar.m index 14ea097..8c71deb 100644 --- a/matlab/@ustar/ustar.m +++ b/matlab/@ustar/ustar.m @@ -1,7 +1,8 @@ classdef ustar < handle % Low-level utilities for UNIX standard tar files. properties (Access = public) - File % file name + File % tar file name + IndexFile % index file name IOMode % file opened in read-only or read-write mode? NumberOfSubfiles % number of subfiles end @@ -65,6 +66,24 @@ classdef ustar < handle obj.scanArchive(); obj.resetCurrent(); end + function openIndexed(obj,tarfile,indexfile) + % obj.open(tarfile,indexfile) + % Opens a file in read-only mode while using available + % indexing data. + % Input + % tarfile path to TAR file + % indexfile path to index file (in json format) + obj.File = tarfile; + obj.IndexFile = indexfile; + obj.IOMode = 'read'; + obj.ioflag = 'r'; + obj.fileID = fopen(obj.File,obj.ioflag); + if obj.fileID<0 + error('Unable to open file: %s',obj.File); + end + obj.scanIndexFile(); + obj.resetCurrent(); + end end %% ------------------------------------------------------------------------%% %% PUBLIC METHODS %% @@ -178,6 +197,88 @@ classdef ustar < handle end obj.resetCurrent(); end + function scanIndexFile(obj) + % obj.scanIndexFile() + % Reads tar meta-data from index file into class variables. + + % Check encoding of indexing file + [~,~,fileExtension] = fileparts(obj.IndexFile); + switch fileExtension + case '.simplejson' + % Open and read file contents (ASCII) + indexfileID = fopen(obj.IndexFile,'r'); + if indexfileID<0 + error('Unable to open file: %s',obj.IndexFile); + end + fseek(indexfileID,0,'bof'); + jsonstr = fread(indexfileID,'char=>char')'; + fclose(indexfileID); + % Parse JSON and reconstruct filenames + if ~isempty(which('jsonlab.loadjson')) + % User function from matlab central + % This function is preferred, since filenames can be + % reconstructed safely from parsed JSON (. <=> _0x2E_) + json = jsonlab.loadjson(jsonstr); + jsonFields = fieldnames(json); + tarFileName = strrep(jsonFields,'_0x2E_','.'); + elseif ~isempty(which('jsondecode')) + % Built-in function + % Second choice only, since filename might be ambiguous + % if it has no extension, but contains underscore. (. => _) + json = jsondecode(jsonstr); + jsonFields = fieldnames(json); + idxtmp = strfind(jsonFields,'_'); + tarFileName = jsonFields; + for ifield=1:length(jsonFields) + if ~isempty(idxtmp{ifield}) + tarFileName{ifield}(idxtmp{ifield}(end)) = '.'; + end + end + else % no JSON decoder available + error('No JSON decoder available.'); + end + % Extract important fields + nsubfile = length(jsonFields); + for isub=1:nsubfile + tarFileOffset(isub) = json.(jsonFields{isub}).offset; + tarFileSize(isub) = json.(jsonFields{isub}).size; + end + case '.msgpack' + % Open and read file contents (binary) + indexfileID = fopen(obj.IndexFile,'rb'); + if indexfileID<0 + error('Unable to open file: %s',obj.IndexFile); + end + fseek(indexfileID,0,'bof'); + msgbytes = fread(indexfileID,'uint8=>uint8'); + fclose(indexfileID); + % Parse msgpack + if ~isempty(which('msgpack.parsemsgpack')) + msg = msgpack.parsemsgpack(msgbytes); + tarFileName = msg.keys; + nsubfile = length(tarFileName); + tarFileSize = zeros(1,nsubfile); + tarFileOffset = zeros(1,nsubfile); + for isub=1:nsubfile + tmp = msg(tarFileName{isub}); + tarFileOffset(isub) = double(tmp{1}); + tarFileSize(isub) = double(tmp{2}); + end + else % no msgpack decoder available + error('No msgpack decoder available.'); + end + otherwise + error('Unknown file extension of index file: %s',fileExtension); + end + + % Order by offset, i.e. file order within tarball and assign + % to class variables + [~,idxsort] = sort(tarFileOffset); + obj.subFile = {tarFileName{idxsort}}'; + obj.subFileBeg = tarFileOffset(idxsort)'; + obj.subFileSize = tarFileSize(idxsort)'; + obj.NumberOfSubfiles = nsubfile; + end function readHeader(obj,scanMode) % obj.readHeader(scanMode) % Reads header data of a subfile in tar-ball and stores information