From cbde60700314d7320ff77e2a5aebb375d3b9c767 Mon Sep 17 00:00:00 2001 From: Qianqian Fang Date: Mon, 8 Jun 2020 23:08:21 -0400 Subject: [PATCH] add savebj and loadbj to dedicate to loading and saving bjdata --- Contents.m | 186 +++- examples/demo_ubjson_basic.m | 162 +-- examples/jsonlab_selftest.m | 8 +- gendocs.sh | 4 + loadbj.m | 412 +++++++ loadubjson.m | 392 +------ savebj.m | 1028 ++++++++++++++++++ savemsgpack.m | 10 +- saveubjson.m | 1009 +---------------- test/{run_save_test.m => run_jsonlab_test.m} | 4 +- test/testjsonlab.m | 232 ++-- 11 files changed, 1855 insertions(+), 1592 deletions(-) create mode 100644 loadbj.m create mode 100644 savebj.m rename test/{run_save_test.m => run_jsonlab_test.m} (90%) diff --git a/Contents.m b/Contents.m index 34c8d75..0ea9b97 100644 --- a/Contents.m +++ b/Contents.m @@ -14,6 +14,7 @@ % jsave - jsave(fname,'param1',value1,'param2',value2,...) % jload - jload(fname,'param1',value1,'param2',value2,...) % jsonopt - val=jsonopt(key,default,optstruct) +% loadbj - data=loadbj(fname,opt) % loadjson - data=loadjson(fname,opt) % loadmsgpack - PARSEMSGPACK parses a msgpack byte buffer into Matlab data structures % loadubjson - data=loadubjson(fname,opt) @@ -28,6 +29,7 @@ % match_bracket - [endpos, maxlevel] = match_bracket(str,startpos,brackets) % mergestruct - s=mergestruct(s1,s2) % nestbracket2dim - [dims, maxlevel, count] = nestbracket2dim(str,brackets) +% savebj - bjd=savebj(rootname,obj,filename) % savejson - json=savejson(rootname,obj,filename) % savemsgpack - msgpk=savemsgpack(rootname,obj,filename) % saveubjson - json=saveubjson(rootname,obj,filename) @@ -44,8 +46,11 @@ % jdata=jdataencode(data, options) % jdata=jdataencode(data, 'Param1',value1, 'Param2',value2,...) % -% Serialize a MATLAB struct or cell array into a JData-compliant -% structure as defined in the JData spec: http://github.com/fangq/jdata +% Annotate a MATLAB struct or cell array into a JData-compliant data +% structure as defined in the JData spec: http://github.com/fangq/jdata. +% This encoded form servers as an intermediate format that allows unambiguous +% storage, exchange of complex data structures and easy-to-serialize by +% json encoders such as savejson and jsonencode (MATLAB R2016b or newer) % % This function implements the JData Specification Draft 3 (Jun. 2020) % see http://github.com/fangq/jdata for details @@ -55,6 +60,11 @@ % data: a structure (array) or cell (array) to be encoded. % options: (optional) a struct or Param/value pairs for user % specified options (first in [.|.] is the default) +% AnnotateArray: [0|1] - if set to 1, convert all 1D/2D matrices +% to the annotated JData array format to preserve data types; +% N-D (N>2), complex and sparse arrays are encoded using the +% annotated format by default. Please set this option to 1 if +% you intend to use MATLAB's jsonencode to convert to JSON. % Base64: [0|1] if set to 1, _ArrayZipData_ is assumed to % be encoded with base64 format and need to be % decoded first. This is needed for JSON but not @@ -88,6 +98,14 @@ % example: % jd=jdataencode(struct('a',rand(5)+1i*rand(5),'b',[],'c',sparse(5,5))) % +% encodedmat=jdataencode(single(magic(5)),'annotatearray',1,'prefix','x') +% jdatadecode(jsondecode(jsonencode(encodedmat))) % serialize by jsonencode +% jdatadecode(loadjson(savejson('',encodedmat))) % serialize by savejson +% +% encodedtoeplitz=jdataencode(uint8(toeplitz([1,2,3,4],[1,5,6])),'usearrayshape',1,'prefix','x') +% jdatadecode(jsondecode(jsonencode(encodedtoeplitz))) % serialize by jsonencode +% jdatadecode(loadjson(savejson('',encodedtoeplitz))) % serialize by savejson +% % license: % BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details % @@ -98,7 +116,7 @@ % % Convert all JData object (in the form of a struct array) into an array % (accepts JData objects loaded from either loadjson/loadubjson or -% jsondecode for MATLAB R2018a or later) +% jsondecode for MATLAB R2016b or later) % % This function implements the JData Specification Draft 3 (Jun. 2020) % see http://github.com/fangq/jdata for details @@ -252,7 +270,7 @@ % FileName [''|string]: a file name to save the output JSON data % FloatFormat ['%.10g'|string]: format to show each numeric element % of a 1D/2D array; -% IntFormat ['%d'|string]: format to display integer elements +% IntFormat ['%.0f'|string]: format to display integer elements % of a 1D/2D array; % ArrayIndent [1|0]: if 1, output explicit data array with % precedent indentation; if 0, no indentation @@ -349,31 +367,32 @@ % BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details % -%%=== # UBJSON === +%%=== # BJData === -%==== function data = loadubjson(fname,varargin) ==== +%==== function data = loadbj(fname,varargin) ==== % -% data=loadubjson(fname,opt) +% data=loadbj(fname,opt) % or -% data=loadubjson(fname,'param1',value1,'param2',value2,...) +% data=loadbj(fname,'param1',value1,'param2',value2,...) % -% parse a JSON (JavaScript Object Notation) file or string +% Parse a Binary JData (BJData, Draft-1, defined in https://github.com/OpenJData/bjdata) +% file or memory buffer and convert into a MATLAB data structure % % initially created on 2013/08/01 % % input: % fname: input file name, if fname contains "{}" or "[]", fname -% will be interpreted as a UBJSON string +% will be interpreted as a BJData/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) % -% SimplifyCell [1|0]: if set to 1, loadubjson will call cell2mat +% SimplifyCell [1|0]: if set to 1, loadbj will call cell2mat % for each element of the JSON data, and group % arrays based on the cell2mat rules. % IntEndian [B|L]: specify the endianness of the integer fields -% in the UBJSON input data. B - Big-Endian format for +% in the BJData/UBJSON input data. B - Big-Endian format for % integers (as required in the UBJSON specification); % L - input integer fields are in Little-Endian order. % NameIsString [0|1]: for UBJSON Specification Draft 8 or @@ -399,26 +418,26 @@ % % 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',0) +% ubjdata=savebj('obj',obj); +% dat=loadbj(ubjdata) +% dat=loadbj(['examples' filesep 'example1.bjd']) +% dat=loadbj(['examples' filesep 'example1.bjd'],'SimplifyCell',0) % % license: % BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details % -%==== function json=saveubjson(rootname,obj,varargin) ==== +%==== function json=savebj(rootname,obj,varargin) ==== % -% json=saveubjson(obj) +% bjd=savebj(obj) % or -% json=saveubjson(rootname,obj,filename) -% json=saveubjson(rootname,obj,opt) -% json=saveubjson(rootname,obj,'param1',value1,'param2',value2,...) +% bjd=savebj(rootname,obj,filename) +% bjd=savebj(rootname,obj,opt) +% bjd=savebj(rootname,obj,'param1',value1,'param2',value2,...) % % Convert a MATLAB object (cell, struct, array, table, map, handles ...) -% into a Binary JData (BJD, Draft 1), Universal Binary JSON (UBJSON, Draft -% 12) or a MessagePack binary stream +% into a Binary JData (BJData, Draft 1), Universal Binary JSON (UBJSON, +% Draft-12) or a MessagePack binary stream % % initially created on 2013/08/17 % @@ -442,7 +461,7 @@ % opt can have the following fields (first in [.|.] is the default) % % FileName [''|string]: a file name to save the output JSON data -% ArrayToStruct[0|1]: when set to 0, saveubjson outputs 1D/2D +% ArrayToStruct[0|1]: when set to 0, savebj 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 @@ -470,7 +489,7 @@ % 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. -% ForceRootName [0|1]: when set to 1 and rootname is empty, saveubjson +% ForceRootName [0|1]: when set to 1 and rootname is empty, savebj % 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 @@ -524,6 +543,104 @@ % 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: +% bjd: a binary string in the UBJSON format (see http://ubjson.org) +% +% examples: +% jsonmesh=struct('MeshVertex3',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],... +% 'MeshTet4',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],... +% 'MeshTri3',[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]); +% savebj(jsonmesh) +% savebj('',jsonmesh,'meshdata.bjd') +% savebj('mesh1',jsonmesh,'FileName','meshdata.msgpk','MessagePack',1) +% savebj('',jsonmesh,'ubjson',1) +% +% license: +% BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details +% + +%%=== # UBJSON === + +%==== function varargout = loadubjson(varargin) ==== +% +% data=loadubjson(fname,opt) +% or +% data=loadubjson(fname,'param1',value1,'param2',value2,...) +% +% Parse a UBJSON file or string and store the output into a MATLAB variable +% +% initially created on 2019/06/08 +% +% This function is an alias to loadbj +% +% 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. The supported options can be found by typing +% "help loadbj". +% +% output: +% data: 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',0) +% +% license: +% BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details +% + +%==== function ubj=saveubjson(rootname,obj,varargin) ==== +% +% ubj=saveubjson(obj) +% or +% ubj=saveubjson(rootname,obj,filename) +% ubj=saveubjson(rootname,obj,opt) +% ubj=saveubjson(rootname,obj,'param1',value1,'param2',value2,...) +% +% Convert a MATLAB object (cell, struct, array, table, map, handles ...) +% into a Universal Binary JSON (UBJSON, Draft 12) or a MessagePack binary stream +% +% initially created on 2013/08/17 +% +% Format specifications: +% Binary JData (BJData):https://github.com/fangq/bjdata +% UBJSON: https://github.com/ubjson/universal-binary-json +% MessagePack: https://github.com/msgpack/msgpack +% +% This function is the same as calling "savebj(...,'ubjson',1)". By , +% default this function creates UBJSON-compliant output without the +% newly added uint16(u), uint32(m), uint64(M) and half-precision float (h) +% data types. +% +% This function by default still enables an optimized ND-array format for efficient +% array storage. To ensure the output compatible to UBJSON Draft-12, one should use +% "saveubjson(...,'NestArray',1)" or "savebj(...,'ubjson',1,'NestArray',1)" +% +% 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 can be replaced by a list of ('param',value) pairs. The param +% string is equivallent to a field in opt and is case sensitive. +% +% Please type "help savebj" for details for all supported options. +% +% output: % json: a binary string in the UBJSON format (see http://ubjson.org) % % examples: @@ -534,9 +651,9 @@ % 'MeshCreator','FangQ','MeshTitle','T6 Cube',... % 'SpecialData',[nan, inf, -inf]); % saveubjson(jsonmesh) -% saveubjson('',jsonmesh,'meshdata.bjd') +% saveubjson('',jsonmesh,'meshdata.ubj') % saveubjson('mesh1',jsonmesh,'FileName','meshdata.msgpk','MessagePack',1) -% saveubjson('',jsonmesh,'ubjson',1) +% saveubjson('',jsonmesh,'KeepType',1) % % license: % BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details @@ -578,9 +695,9 @@ % % initially created on 2019/05/20 % -% This function is the same as calling saveubjson(...,'MessagePack',1) +% This function is the same as calling savebj(...,'MessagePack',1) % -% Please type "help saveubjson" for details for the supported inputs and outputs. +% Please type "help savebj" for details for the supported inputs and outputs. % % license: % BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details @@ -612,9 +729,12 @@ % ws ['base'|'wsname']: the name of the workspace in which the % variables are to be saved % vars [{'var1','var2',...}]: cell array of variable names to be saved +% matlab [0|1] if set to 1, use matlab's built-in jsonencode to +% store encoded data to a json file; output file +% must have a suffix of .jdt % % all options for saveubjson/savejson (depends on file suffix) -% can be used to adjust the output +% can be used to adjust the output unless "'matlab',1" is used % % output: % varlist: a list of variables loaded @@ -652,8 +772,11 @@ % ws ['base'|'wsname']: the name of the workspace in which the % variables are to be saved % vars [{'var1','var2',...}]: list of variables to be saved -% Header [0|1]: if set to 1, return the metadata of the variables +% header [0|1]: if set to 1, return the metadata of the variables % stored in the file +% matlab [0|1] if set to 1, use matlab's built-in jsondecode to +% parse the json file and then decode the output by +% jdatadecode; input file must have a suffix of .jdt % % all options for loadubjson/loadjson (depends on file suffix) % can be used to adjust the parsing options @@ -1303,3 +1426,4 @@ % BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details % + diff --git a/examples/demo_ubjson_basic.m b/examples/demo_ubjson_basic.m index 5f4bf77..1f8a12a 100644 --- a/examples/demo_ubjson_basic.m +++ b/examples/demo_ubjson_basic.m @@ -11,72 +11,72 @@ fprintf(1,'%%=================================================\n\n') data2json=pi -saveubjson('',data2json) -json2data=loadubjson(ans) +savebj('',data2json) +json2data=loadbj(ans) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% an empty array \n') fprintf(1,'%%=================================================\n\n') data2json=[] -saveubjson('empty',data2json) -json2data=loadubjson(ans) +savebj('empty',data2json) +json2data=loadbj(ans) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% an ampty string \n') fprintf(1,'%%=================================================\n\n') data2json='' -saveubjson('emptystr',data2json) -json2data=loadubjson(ans) +savebj('emptystr',data2json) +json2data=loadbj(ans) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a simple row vector \n') fprintf(1,'%%=================================================\n\n') data2json=1:3 -saveubjson('',data2json) -json2data=loadubjson(ans) +savebj('',data2json) +json2data=loadbj(ans) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a simple column vector \n') fprintf(1,'%%=================================================\n\n') data2json=(1:3)' -saveubjson('',data2json) -json2data=loadubjson(ans) +savebj('',data2json) +json2data=loadbj(ans) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a string array \n') fprintf(1,'%%=================================================\n\n') data2json=['AC';'EG'] -saveubjson('',data2json) -json2data=loadubjson(ans) +savebj('',data2json) +json2data=loadbj(ans) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a string with escape symbols \n') fprintf(1,'%%=================================================\n\n') data2json=sprintf('AB\tCD\none"two') -saveubjson('str',data2json) -json2data=loadubjson(ans) +savebj('str',data2json) +json2data=loadbj(ans) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a mix-typed cell \n') fprintf(1,'%%=================================================\n\n') data2json={'a',true,[2;3]} -saveubjson('',data2json) -json2data=loadubjson(ans) +savebj('',data2json) +json2data=loadbj(ans) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a 3-D array in nested array form\n') fprintf(1,'%%=================================================\n\n') data2json=reshape(1:(2*4*6),[2,4,6]); -saveubjson('',data2json,'NestArray',1) -json2data=loadubjson(ans) +savebj('',data2json,'NestArray',1) +json2data=loadbj(ans) % if(any(json2data(:)~=data2json(:)) || any(size(json2data)~=size(data2json))) % warning('conversion does not preserve original data'); % end @@ -86,8 +86,8 @@ fprintf(1,'%%=================================================\n\n') data2json=reshape(1:(2*4*6),[2,4,6]); -saveubjson('',data2json,'NestArray',0) -json2data=loadubjson(ans) +savebj('',data2json,'NestArray',0) +json2data=loadbj(ans) if(any(json2data(:)~=data2json(:)) || any(size(json2data)~=size(data2json))) warning('conversion does not preserve original data'); end @@ -97,8 +97,8 @@ fprintf(1,'%%=================================================\n\n') data2json=reshape(1:(2*4*3*2),[2,4,3,2]); -saveubjson('',data2json,'NestArray',0) % nestarray for 4-D or above is not working -json2data=loadubjson(ans) +savebj('',data2json,'NestArray',0) % nestarray for 4-D or above is not working +json2data=loadbj(ans) if(any(json2data(:)~=data2json(:)) || any(size(json2data)~=size(data2json))) warning('conversion does not preserve original data'); end @@ -108,22 +108,22 @@ fprintf(1,'%%=================================================\n\n') data2json=reshape(1:(2*4*6),[2,4,6]); -saveubjson('',data2json,'NestArray',1,'FormatVersion',1.8) +savebj('',data2json,'NestArray',1,'FormatVersion',1.8) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a 3-D array in annotated array form (JSONLab 1.9 or earlier)\n') fprintf(1,'%%=================================================\n\n') data2json=reshape(1:(2*4*6),[2,4,6]); -saveubjson('',data2json,'NestArray',0,'FormatVersion',1.8) +savebj('',data2json,'NestArray',0,'FormatVersion',1.8) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a complex number\n') fprintf(1,'%%=================================================\n\n') data2json=1+2i -saveubjson('',data2json) -json2data=loadubjson(ans) +savebj('',data2json) +json2data=loadbj(ans) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a complex matrix\n') @@ -131,96 +131,96 @@ data2json=magic(6); data2json=data2json(:,1:3)+data2json(:,4:6)*1i -saveubjson('',data2json) -json2data=loadubjson(ans) +savebj('',data2json) +json2data=loadbj(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) +savebj('specials',data2json) +json2data=loadbj(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) +savebj('sparse',data2json) +json2data=loadbj(ans) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a complex sparse matrix\n') fprintf(1,'%%=================================================\n\n') data2json=data2json-data2json*1i -saveubjson('complex_sparse',data2json) -json2data=loadubjson(ans) +savebj('complex_sparse',data2json) +json2data=loadbj(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) +savebj('all_zero_sparse',data2json) +json2data=loadbj(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) +savebj('empty_sparse',data2json) +json2data=loadbj(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) +savebj('empty_0by0_real',data2json) +json2data=loadbj(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) +savebj('empty_0by3_real',data2json) +json2data=loadbj(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) +savebj('sparse_column_vector',data2json) +json2data=loadbj(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) +savebj('complex_sparse_column_vector',data2json) +json2data=loadbj(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) +savebj('sparse_row_vector',data2json) +json2data=loadbj(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) +savebj('complex_sparse_row_vector',data2json) +json2data=loadbj(ans) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a structure\n') @@ -228,8 +228,8 @@ 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) +savebj('astruct',data2json,struct('ParseLogical',1)) +json2data=loadbj(ans) class(json2data.astruct.embedded.left) fprintf(1,'\n%%=================================================\n') @@ -239,8 +239,8 @@ 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) +savebj('Supreme Commander',data2json) +json2data=loadbj(ans) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a cell array\n') @@ -251,30 +251,30 @@ '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) +savebj('debian',data2json,struct('FloatFormat','%.2f')) +json2data=loadbj(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, "项目":"绝密"}'))) +json2data=loadbj(savebj('',loadjson('{"ValidName":1, "_InvalidName":2, ":Field:":3, "项目":"绝密"}'))) fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a function handle\n') fprintf(1,'%%=================================================\n\n') data2json=@(x) x+1 -saveubjson('handle',data2json) -json2data=loadubjson(ans) +savebj('handle',data2json) +json2data=loadbj(ans) 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 saveubjson works for cell arrays, loadubjson has issues +savebj('data2json',data2json) +json2data=loadbj(ans) % only savebj works for cell arrays, loadbj has issues fprintf(1,'\n%%=================================================\n') fprintf(1,'%% a 2D struct array\n') @@ -284,8 +284,8 @@ for i=1:6 data2json(i).idx=i; end -saveubjson('data2json',data2json) -json2data=loadubjson(ans) +savebj('data2json',data2json) +json2data=loadbj(ans) if(exist('datetime')) @@ -294,8 +294,8 @@ fprintf(1,'%%=================================================\n\n') data2json=datetime({'8 April 2015','9 May 2015'}, 'InputFormat','d MMMM yyyy') - saveubjson('',data2json) - json2data=loadubjson(ans) + savebj('',data2json) + json2data=loadbj(ans) end if(exist('containers.Map')) @@ -304,8 +304,8 @@ fprintf(1,'%%=================================================\n\n') data2json=containers.Map({'Andy','William','Om'},{21,21,22}) - saveubjson('',data2json) - json2data=loadubjson(ans) + savebj('',data2json) + json2data=loadbj(ans) end if(exist('istable')) @@ -316,8 +316,8 @@ Names={'Andy','William','Om'}'; Age=[21,21,22]'; data2json=table(Names,Age) - saveubjson('table',table(Names,Age)) - json2data=loadubjson(ans) + savebj('table',table(Names,Age)) + json2data=loadbj(ans) end if(exist('bandwidth')) @@ -331,20 +331,20 @@ data2json=full(double(data2json)); data2json(data2json~=0)=find(data2json) - saveubjson('',data2json,'usearrayshape',1) - json2data=loadubjson(ans,'fullarrayshape',1) + savebj('',data2json,'usearrayshape',1) + json2data=loadbj(ans,'fullarrayshape',1) - saveubjson('',tril(data2json),'usearrayshape',1) - json2data=loadubjson(ans,'fullarrayshape',1) + savebj('',tril(data2json),'usearrayshape',1) + json2data=loadbj(ans,'fullarrayshape',1) - saveubjson('',triu(data2json+1i*data2json),'usearrayshape',1) - json2data=loadubjson(ans,'fullarrayshape',1) + savebj('',triu(data2json+1i*data2json),'usearrayshape',1) + json2data=loadbj(ans,'fullarrayshape',1) - saveubjson('',tril(triu(int8(data2json))),'usearrayshape',1) - json2data=loadubjson(ans,'fullarrayshape',1) + savebj('',tril(triu(int8(data2json))),'usearrayshape',1) + json2data=loadbj(ans,'fullarrayshape',1) - saveubjson('',data2json(:,1:5)+data2json(:,1:5)','usearrayshape',1) - json2data=loadubjson(ans,'fullarrayshape',1) + savebj('',data2json(:,1:5)+data2json(:,1:5)','usearrayshape',1) + json2data=loadbj(ans,'fullarrayshape',1) end try @@ -355,8 +355,8 @@ data2json=eye(10); data2json(20,1)=1; - saveubjson('',data2json,'Compression','zlib','CompressArraySize',0) % nestarray for 4-D or above is not working - json2data=loadubjson(ans) + savebj('',data2json,'Compression','zlib','CompressArraySize',0) % nestarray for 4-D or above is not working + json2data=loadbj(ans) if(any(json2data(:)~=data2json(:)) || any(size(json2data)~=size(data2json))) warning('conversion does not preserve original data'); end diff --git a/examples/jsonlab_selftest.m b/examples/jsonlab_selftest.m index 27aee24..583e8ef 100644 --- a/examples/jsonlab_selftest.m +++ b/examples/jsonlab_selftest.m @@ -18,10 +18,10 @@ fname=sprintf('example%d.json',i); if(exist(fname,'file')==0) break; end fprintf(1,'===============================================\n>> %s\n',fname); - json=saveubjson('data',loadjson(fname)); + json=savebj('data',loadjson(fname)); fprintf(1,'%s\n',json); - data=loadubjson(json); + data=loadbj(json); savejson('',data); - saveubjson('data',data,'selftest.ubj'); - data=loadubjson('selftest.ubj'); + savebj('data',data,'selftest.ubj'); + data=loadbj('selftest.ubj'); end diff --git a/gendocs.sh b/gendocs.sh index 330cc2a..ebfb2d2 100755 --- a/gendocs.sh +++ b/gendocs.sh @@ -25,6 +25,7 @@ print_group() func_jdata="jdataencode jdatadecode" func_json="loadjson savejson" +func_bjdata="loadbj savebj" func_ubjson="loadubjson saveubjson" func_msgpack="loadmsgpack savemsgpack" func_space="jsave jload" @@ -40,6 +41,9 @@ print_group $func_jdata echo %%=== "#" JSON === print_group $func_json +echo %%=== "#" BJData === +print_group $func_bjdata + echo %%=== "#" UBJSON === print_group $func_ubjson diff --git a/loadbj.m b/loadbj.m new file mode 100644 index 0000000..9af4f38 --- /dev/null +++ b/loadbj.m @@ -0,0 +1,412 @@ +function data = loadbj(fname,varargin) +% +% data=loadbj(fname,opt) +% or +% data=loadbj(fname,'param1',value1,'param2',value2,...) +% +% Parse a Binary JData (BJData, Draft-1, defined in https://github.com/OpenJData/bjdata) +% file or memory buffer and convert into a MATLAB data structure +% +% authors:Qianqian Fang (q.fang neu.edu) +% initially created on 2013/08/01 +% +% input: +% fname: input file name, if fname contains "{}" or "[]", fname +% will be interpreted as a BJData/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) +% +% SimplifyCell [1|0]: if set to 1, loadbj will call cell2mat +% for each element of the JSON data, and group +% arrays based on the cell2mat rules. +% IntEndian [B|L]: specify the endianness of the integer fields +% in the BJData/UBJSON input data. B - Big-Endian format for +% integers (as required in the UBJSON specification); +% L - input integer fields are in Little-Endian order. +% 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. +% UseMap [0|1]: if set to 1, loadjson uses a containers.Map to +% store map objects; otherwise use a struct object +% ObjectID [0|interger or list]: if set to a positive number, +% it returns the specified JSON object by index +% in a multi-JSON document; if set to a vector, +% it returns a list of specified objects. +% FormatVersion [2|float]: set the JSONLab format version; since +% v2.0, JSONLab uses JData specification Draft 1 +% for output format, it is incompatible with all +% previous releases; if old output is desired, +% please set FormatVersion to 1.9 or earlier. +% +% 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=savebj('obj',obj); +% dat=loadbj(ubjdata) +% dat=loadbj(['examples' filesep 'example1.bjd']) +% dat=loadbj(['examples' filesep 'example1.bjd'],'SimplifyCell',0) +% +% license: +% BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details +% +% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + + 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; inputlen = length(string); inputstr = string; + arraytoken=find(inputstr=='[' | inputstr==']' | inputstr=='"'); + jstr=regexprep(inputstr,'\\\\',' '); + escquote=regexp(jstr,'\\"'); + arraytoken=sort([arraytoken escquote]); + + opt=varargin2struct(varargin{:}); + opt.arraytoken_=arraytoken; + opt.simplifycell=jsonopt('SimplifyCell',1,opt); + opt.simplifycellarray=jsonopt('SimplifyCellArray',0,opt); + opt.usemap=jsonopt('UseMap',0,opt); + opt.nameisstring=jsonopt('NameIsString',0,opt); + + [os,maxelem,systemendian]=computer; + opt.flipendian_=(systemendian ~= upper(jsonopt('IntEndian','B',opt))); + + objid=jsonopt('ObjectID',0,opt); + maxobjid=max(objid); + if(maxobjid==0) + maxobjid=inf; + end + + jsoncount=1; + while pos <= inputlen + [cc, pos]=next_char(inputstr, pos); + switch(cc) + case '{' + [data{jsoncount}, pos] = parse_object(inputstr, pos, opt); + case '[' + [data{jsoncount}, pos] = parse_array(inputstr, pos, opt); + otherwise + error_pos('Outer level structure must be an object or an array', inputstr, pos); + end + if(jsoncount>=maxobjid) + break; + end + jsoncount=jsoncount+1; + end % while + + if(length(objid)>1 || min(objid)>1) + data=data(objid(objid<=length(data))); + end + + jsoncount=length(data); + if(jsoncount==1 && iscell(data)) + data=data{1}; + end + + if(jsonopt('JDataDecode',1,varargin{:})==1) + data=jdatadecode(data,'Base64',0,'Recursive',1,varargin{:}); + end +end + +%%------------------------------------------------------------------------- +%% helper functions +%%------------------------------------------------------------------------- + +function [data, adv]=parse_block(inputstr, pos, type,count,varargin) + [cid,len]=elem_info(inputstr, pos, type); + datastr=inputstr(pos:pos+len*count-1); + newdata=uint8(datastr); + %id=strfind('iUIulmLMhdD',type); + if(varargin{1}.flipendian_) + newdata=swapbytes(typecast(newdata,cid)); + end + data=typecast(newdata,cid); + adv=double(len*count); +end +%%------------------------------------------------------------------------- + +function [object, pos] = parse_array(inputstr, pos, varargin) % JSON array is written in row-major order + pos=parse_char(inputstr, pos, '['); + object = cell(0, 1); + dim=[]; + type=''; + count=-1; + [cc,pos]=next_char(inputstr,pos); + if(cc == '$') + type=inputstr(pos+1); + pos=pos+2; + end + [cc,pos]=next_char(inputstr,pos); + if(cc == '#') + pos=pos+1; + [cc,pos]=next_char(inputstr,pos); + if(cc=='[') + if(isfield(varargin{1},'noembedding_') && varargin{1}.noembedding_==1) + error('ND array size specifier does not support embedding'); + end + varargin{1}.noembedding_=1; + [dim, pos]=parse_array(inputstr, pos, varargin{:}); + count=prod(double(dim)); + varargin{1}.noembedding_=0; + else + [val,pos]=parse_number(inputstr,pos, varargin{:}); + count=double(val); + end + end + if(~isempty(type)) + if(count>=0) + [object, adv]=parse_block(inputstr, pos, type,count,varargin{:}); + if(~isempty(dim)) + object=reshape(object,dim); + end + pos=pos+adv; + return; + else + endpos=match_bracket(inputstr,pos); + [cid,len]=elem_info(inputstr, pos, type); + count=(endpos-pos)/len; + [object, adv]=parse_block(inputstr, pos, type,count,varargin{:}); + pos=pos+adv; + pos=parse_char(inputstr, pos, ']'); + return; + end + end + [cc,pos]=next_char(inputstr,pos); + if cc ~= ']' + while 1 + [val, pos] = parse_value(inputstr, pos, varargin{:}); + object{end+1} = val; + [cc,pos]=next_char(inputstr,pos); + if cc == ']' + break; + end + end + end + if(varargin{1}.simplifycell) + if(iscell(object) && ~isempty(object) && isnumeric(object{1})) + if(all(cellfun(@(e) isequal(size(object{1}), size(e)) , object(2:end)))) + try + oldobj=object; + if(iscell(object) && length(object)>1 && ndims(object{1})>=2) + catdim=size(object{1}); + catdim=ndims(object{1})-(catdim(end)==1)+1; + object=cat(catdim,object{:}); + object=permute(object,ndims(object):-1:1); + else + object=cell2mat(object')'; + end + if(iscell(oldobj) && isstruct(object) && numel(object)>1 && varargin{1}.simplifycellarray==0) + object=oldobj; + end + catch + end + end + end + if(~iscell(object) && size(object,1)>1 && ndims(object)==2) + object=object'; + end + end + if(count==-1) + pos=parse_char(inputstr, pos, ']'); + end +end + +%%------------------------------------------------------------------------- + +function pos=parse_char(inputstr, pos, c) + if pos > length(inputstr) || inputstr(pos) ~= c + error_pos(sprintf('Expected %c at position %%d', c),inputstr, pos); + else + pos = pos + 1; + end +end + +%%------------------------------------------------------------------------- + +function [c, pos] = next_char(inputstr, pos) + if pos > length(inputstr) + c = []; + else + c = inputstr(pos); + end +end + +%%------------------------------------------------------------------------- +function [str, pos] = parse_name(inputstr, pos, varargin) + [val, pos]=parse_number(inputstr,pos,varargin{:}); + bytelen=double(val); + if(length(inputstr)>=pos+bytelen-1) + str=inputstr(pos:pos+bytelen-1); + pos=pos+bytelen; + else + error_pos('End of file while expecting end of name', inputstr, pos); + end +end + +%%------------------------------------------------------------------------- + +function [str, pos] = parseStr(inputstr, pos, varargin) + type=inputstr(pos); + if type ~= 'S' && type ~= 'C' && type ~= 'H' + error_pos('String starting with S expected at position %d',inputstr, pos); + else + pos = pos + 1; + end + if(type == 'C') + str=inputstr(pos); + pos=pos+1; + return; + end + [val, pos]=parse_number(inputstr,pos,varargin{:}); + bytelen=double(val); + if(length(inputstr)>=pos+bytelen-1) + str=inputstr(pos:pos+bytelen-1); + pos=pos+bytelen; + else + error_pos('End of file while expecting end of inputstr',inputstr, pos); + end +end + +%%------------------------------------------------------------------------- + +function [num, pos] = parse_number(inputstr, pos, varargin) + id=strfind('iUIulmLMhdD',inputstr(pos)); + if(isempty(id)) + error_pos('expecting a number at position %d',inputstr, pos); + end + type={'int8','uint8','int16','uint16','int32','uint32','int64','uint64','half','single','double'}; + bytelen=[1,1,2,2,4,4,8,8,2,4,8]; + if(~exist('half','builtin')) + type{9}='uint16'; + end + datastr=inputstr(pos+1:pos+bytelen(id)); + newdata=uint8(datastr); + if(varargin{1}.flipendian_) + newdata=swapbytes(typecast(newdata,type{id})); + end + num=typecast(newdata,type{id}); + pos = pos + bytelen(id)+1; +end + +%%------------------------------------------------------------------------- + +function [val, pos] = parse_value(inputstr, pos, varargin) + switch(inputstr(pos)) + case {'S','C','H'} + [val, pos] = parseStr(inputstr, pos, varargin{:}); + return; + case '[' + [val, pos] = parse_array(inputstr, pos, varargin{:}); + return; + case '{' + [val, pos] = parse_object(inputstr, pos, varargin{:}); + return; + case {'i','U','I','u','l','m','L','M','h','d','D'} + [val, pos] = parse_number(inputstr, pos, 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', inputstr, pos); +end +%%------------------------------------------------------------------------- + +function pos=error_pos(msg, inputstr, pos) + poShow = max(min([pos-15 pos-1 pos pos+20],length(inputstr)),1); + if poShow(3) == poShow(2) + poShow(3:4) = poShow(2)+[0 -1]; % display nothing after + end + msg = [sprintf(msg, pos) ': ' ... + inputstr(poShow(1):poShow(2)) '' inputstr(poShow(3):poShow(4)) ]; + error( ['JSONLAB:InvalidFormat: ' msg] ); +end + +%%------------------------------------------------------------------------- +function [object, pos] = parse_object(inputstr, pos, varargin) + pos=parse_char(inputstr,pos,'{'); + usemap=varargin{1}.usemap; + if(usemap) + object = containers.Map(); + else + object = []; + end + count=-1; + [cc, pos]=next_char(inputstr,pos); + if(cc == '$') + pos=pos+2; + end + [cc, pos]=next_char(inputstr,pos); + if(cc == '#') + pos=pos+1; + [val,pos]=parse_number(inputstr, pos, varargin{:}); + count=double(val); + end + [cc, pos]=next_char(inputstr,pos); + if cc ~= '}' + num=0; + while 1 + if(varargin{1}.nameisstring) + [str, pos] = parseStr(inputstr, pos, varargin{:}); + else + [str, pos] = parse_name(inputstr, pos, varargin{:}); + end + if isempty(str) + error_pos('Name of value at position %d cannot be empty', inputstr, pos); + end + [val, pos] = parse_value(inputstr, pos, varargin{:}); + num=num+1; + if(usemap) + object(str)=val; + else + object.(encodevarname(str,varargin{:}))=val; + end + [cc, pos]=next_char(inputstr,pos); + if cc == '}' || (count>=0 && num>=count) + break; + end + end + end + if(count==-1) + pos=parse_char(inputstr, pos, '}'); + end +end + +%%------------------------------------------------------------------------- +function [cid,len]=elem_info(inputstr, pos, type) + id=strfind('iUIulmLMhdD',type); + type={'int8','uint8','int16','uint16','int32','uint32','int64','uint64','half','single','double'}; + bytelen=[1,1,2,2,4,4,8,8,2,4,8]; + if(~exist('half','builtin')) + type{9}='uint16'; + end + if(id>0) + cid=type{id}; + len=bytelen(id); + else + error_pos('unsupported type at position %d',inputstr, pos); + end +end diff --git a/loadubjson.m b/loadubjson.m index 77f8b9b..c9497ef 100644 --- a/loadubjson.m +++ b/loadubjson.m @@ -1,48 +1,26 @@ -function data = loadubjson(fname,varargin) +function varargout = loadubjson(varargin) % % data=loadubjson(fname,opt) % or % data=loadubjson(fname,'param1',value1,'param2',value2,...) % -% parse a JSON (JavaScript Object Notation) file or string +% Parse a UBJSON file or string and store the output into a MATLAB variable % -% authors:Qianqian Fang (q.fang neu.edu) -% initially created on 2013/08/01 +% author: Qianqian Fang (q.fang neu.edu) +% initially created on 2019/06/08 +% +% This function is an alias to loadbj % % 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) -% -% SimplifyCell [1|0]: if set to 1, loadubjson will call cell2mat -% for each element of the JSON data, and group -% arrays based on the cell2mat rules. -% 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. -% 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. -% UseMap [0|1]: if set to 1, loadjson uses a containers.Map to -% store map objects; otherwise use a struct object -% ObjectID [0|interger or list]: if set to a positive number, -% it returns the specified JSON object by index -% in a multi-JSON document; if set to a vector, -% it returns a list of specified objects. -% FormatVersion [2|float]: set the JSONLab format version; since -% v2.0, JSONLab uses JData specification Draft 1 -% for output format, it is incompatible with all -% previous releases; if old output is desired, -% please set FormatVersion to 1.9 or earlier. +% to a field in opt. The supported options can be found by typing +% "help loadbj". % % output: -% dat: a cell array, where {...} blocks are converted into cell arrays, +% data: a cell array, where {...} blocks are converted into cell arrays, % and [...] are converted to arrays % % examples: @@ -58,354 +36,4 @@ % -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) % - 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; inputlen = length(string); inputstr = string; - arraytoken=find(inputstr=='[' | inputstr==']' | inputstr=='"'); - jstr=regexprep(inputstr,'\\\\',' '); - escquote=regexp(jstr,'\\"'); - arraytoken=sort([arraytoken escquote]); - - opt=varargin2struct(varargin{:}); - opt.arraytoken_=arraytoken; - opt.simplifycell=jsonopt('SimplifyCell',1,opt); - opt.simplifycellarray=jsonopt('SimplifyCellArray',0,opt); - opt.usemap=jsonopt('UseMap',0,opt); - opt.nameisstring=jsonopt('NameIsString',0,opt); - - [os,maxelem,systemendian]=computer; - opt.flipendian_=(systemendian ~= upper(jsonopt('IntEndian','B',opt))); - - objid=jsonopt('ObjectID',0,opt); - maxobjid=max(objid); - if(maxobjid==0) - maxobjid=inf; - end - - jsoncount=1; - while pos <= inputlen - [cc, pos]=next_char(inputstr, pos); - switch(cc) - case '{' - [data{jsoncount}, pos] = parse_object(inputstr, pos, opt); - case '[' - [data{jsoncount}, pos] = parse_array(inputstr, pos, opt); - otherwise - error_pos('Outer level structure must be an object or an array', inputstr, pos); - end - if(jsoncount>=maxobjid) - break; - end - jsoncount=jsoncount+1; - end % while - - if(length(objid)>1 || min(objid)>1) - data=data(objid(objid<=length(data))); - end - - jsoncount=length(data); - if(jsoncount==1 && iscell(data)) - data=data{1}; - end - - if(jsonopt('JDataDecode',1,varargin{:})==1) - data=jdatadecode(data,'Base64',0,'Recursive',1,varargin{:}); - end -end - -%%------------------------------------------------------------------------- -%% helper functions -%%------------------------------------------------------------------------- - -function [data, adv]=parse_block(inputstr, pos, type,count,varargin) - [cid,len]=elem_info(inputstr, pos, type); - datastr=inputstr(pos:pos+len*count-1); - newdata=uint8(datastr); - %id=strfind('iUIulmLMhdD',type); - if(varargin{1}.flipendian_) - newdata=swapbytes(typecast(newdata,cid)); - end - data=typecast(newdata,cid); - adv=double(len*count); -end -%%------------------------------------------------------------------------- - -function [object, pos] = parse_array(inputstr, pos, varargin) % JSON array is written in row-major order - pos=parse_char(inputstr, pos, '['); - object = cell(0, 1); - dim=[]; - type=''; - count=-1; - [cc,pos]=next_char(inputstr,pos); - if(cc == '$') - type=inputstr(pos+1); - pos=pos+2; - end - [cc,pos]=next_char(inputstr,pos); - if(cc == '#') - pos=pos+1; - [cc,pos]=next_char(inputstr,pos); - if(cc=='[') - if(isfield(varargin{1},'noembedding_') && varargin{1}.noembedding_==1) - error('ND array size specifier does not support embedding'); - end - varargin{1}.noembedding_=1; - [dim, pos]=parse_array(inputstr, pos, varargin{:}); - count=prod(double(dim)); - varargin{1}.noembedding_=0; - else - [val,pos]=parse_number(inputstr,pos, varargin{:}); - count=double(val); - end - end - if(~isempty(type)) - if(count>=0) - [object, adv]=parse_block(inputstr, pos, type,count,varargin{:}); - if(~isempty(dim)) - object=reshape(object,dim); - end - pos=pos+adv; - return; - else - endpos=match_bracket(inputstr,pos); - [cid,len]=elem_info(inputstr, pos, type); - count=(endpos-pos)/len; - [object, adv]=parse_block(inputstr, pos, type,count,varargin{:}); - pos=pos+adv; - pos=parse_char(inputstr, pos, ']'); - return; - end - end - [cc,pos]=next_char(inputstr,pos); - if cc ~= ']' - while 1 - [val, pos] = parse_value(inputstr, pos, varargin{:}); - object{end+1} = val; - [cc,pos]=next_char(inputstr,pos); - if cc == ']' - break; - end - end - end - if(varargin{1}.simplifycell) - if(iscell(object) && ~isempty(object) && isnumeric(object{1})) - if(all(cellfun(@(e) isequal(size(object{1}), size(e)) , object(2:end)))) - try - oldobj=object; - if(iscell(object) && length(object)>1 && ndims(object{1})>=2) - catdim=size(object{1}); - catdim=ndims(object{1})-(catdim(end)==1)+1; - object=cat(catdim,object{:}); - object=permute(object,ndims(object):-1:1); - else - object=cell2mat(object')'; - end - if(iscell(oldobj) && isstruct(object) && numel(object)>1 && varargin{1}.simplifycellarray==0) - object=oldobj; - end - catch - end - end - end - if(~iscell(object) && size(object,1)>1 && ndims(object)==2) - object=object'; - end - end - if(count==-1) - pos=parse_char(inputstr, pos, ']'); - end -end - -%%------------------------------------------------------------------------- - -function pos=parse_char(inputstr, pos, c) - if pos > length(inputstr) || inputstr(pos) ~= c - error_pos(sprintf('Expected %c at position %%d', c),inputstr, pos); - else - pos = pos + 1; - end -end - -%%------------------------------------------------------------------------- - -function [c, pos] = next_char(inputstr, pos) - if pos > length(inputstr) - c = []; - else - c = inputstr(pos); - end -end - -%%------------------------------------------------------------------------- -function [str, pos] = parse_name(inputstr, pos, varargin) - [val, pos]=parse_number(inputstr,pos,varargin{:}); - bytelen=double(val); - if(length(inputstr)>=pos+bytelen-1) - str=inputstr(pos:pos+bytelen-1); - pos=pos+bytelen; - else - error_pos('End of file while expecting end of name', inputstr, pos); - end -end - -%%------------------------------------------------------------------------- - -function [str, pos] = parseStr(inputstr, pos, varargin) - type=inputstr(pos); - if type ~= 'S' && type ~= 'C' && type ~= 'H' - error_pos('String starting with S expected at position %d',inputstr, pos); - else - pos = pos + 1; - end - if(type == 'C') - str=inputstr(pos); - pos=pos+1; - return; - end - [val, pos]=parse_number(inputstr,pos,varargin{:}); - bytelen=double(val); - if(length(inputstr)>=pos+bytelen-1) - str=inputstr(pos:pos+bytelen-1); - pos=pos+bytelen; - else - error_pos('End of file while expecting end of inputstr',inputstr, pos); - end -end - -%%------------------------------------------------------------------------- - -function [num, pos] = parse_number(inputstr, pos, varargin) - id=strfind('iUIulmLMhdD',inputstr(pos)); - if(isempty(id)) - error_pos('expecting a number at position %d',inputstr, pos); - end - type={'int8','uint8','int16','uint16','int32','uint32','int64','uint64','half','single','double'}; - bytelen=[1,1,2,2,4,4,8,8,2,4,8]; - if(~exist('half','builtin')) - type{9}='uint16'; - end - datastr=inputstr(pos+1:pos+bytelen(id)); - newdata=uint8(datastr); - if(varargin{1}.flipendian_) - newdata=swapbytes(typecast(newdata,type{id})); - end - num=typecast(newdata,type{id}); - pos = pos + bytelen(id)+1; -end - -%%------------------------------------------------------------------------- - -function [val, pos] = parse_value(inputstr, pos, varargin) - switch(inputstr(pos)) - case {'S','C','H'} - [val, pos] = parseStr(inputstr, pos, varargin{:}); - return; - case '[' - [val, pos] = parse_array(inputstr, pos, varargin{:}); - return; - case '{' - [val, pos] = parse_object(inputstr, pos, varargin{:}); - return; - case {'i','U','I','u','l','m','L','M','h','d','D'} - [val, pos] = parse_number(inputstr, pos, 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', inputstr, pos); -end -%%------------------------------------------------------------------------- - -function pos=error_pos(msg, inputstr, pos) - poShow = max(min([pos-15 pos-1 pos pos+20],length(inputstr)),1); - if poShow(3) == poShow(2) - poShow(3:4) = poShow(2)+[0 -1]; % display nothing after - end - msg = [sprintf(msg, pos) ': ' ... - inputstr(poShow(1):poShow(2)) '' inputstr(poShow(3):poShow(4)) ]; - error( ['JSONLAB:InvalidFormat: ' msg] ); -end - -%%------------------------------------------------------------------------- -function [object, pos] = parse_object(inputstr, pos, varargin) - pos=parse_char(inputstr,pos,'{'); - usemap=varargin{1}.usemap; - if(usemap) - object = containers.Map(); - else - object = []; - end - count=-1; - [cc, pos]=next_char(inputstr,pos); - if(cc == '$') - pos=pos+2; - end - [cc, pos]=next_char(inputstr,pos); - if(cc == '#') - pos=pos+1; - [val,pos]=parse_number(inputstr, pos, varargin{:}); - count=double(val); - end - [cc, pos]=next_char(inputstr,pos); - if cc ~= '}' - num=0; - while 1 - if(varargin{1}.nameisstring) - [str, pos] = parseStr(inputstr, pos, varargin{:}); - else - [str, pos] = parse_name(inputstr, pos, varargin{:}); - end - if isempty(str) - error_pos('Name of value at position %d cannot be empty', inputstr, pos); - end - [val, pos] = parse_value(inputstr, pos, varargin{:}); - num=num+1; - if(usemap) - object(str)=val; - else - object.(encodevarname(str,varargin{:}))=val; - end - [cc, pos]=next_char(inputstr,pos); - if cc == '}' || (count>=0 && num>=count) - break; - end - end - end - if(count==-1) - pos=parse_char(inputstr, pos, '}'); - end -end - -%%------------------------------------------------------------------------- -function [cid,len]=elem_info(inputstr, pos, type) - id=strfind('iUIulmLMhdD',type); - type={'int8','uint8','int16','uint16','int32','uint32','int64','uint64','half','single','double'}; - bytelen=[1,1,2,2,4,4,8,8,2,4,8]; - if(~exist('half','builtin')) - type{9}='uint16'; - end - if(id>0) - cid=type{id}; - len=bytelen(id); - else - error_pos('unsupported type at position %d',inputstr, pos); - end -end +[varargout{1:nargout}]=loadbj(varargin{:}); \ No newline at end of file diff --git a/savebj.m b/savebj.m new file mode 100644 index 0000000..98ccf69 --- /dev/null +++ b/savebj.m @@ -0,0 +1,1028 @@ +function json=savebj(rootname,obj,varargin) +% +% bjd=savebj(obj) +% or +% bjd=savebj(rootname,obj,filename) +% bjd=savebj(rootname,obj,opt) +% bjd=savebj(rootname,obj,'param1',value1,'param2',value2,...) +% +% Convert a MATLAB object (cell, struct, array, table, map, handles ...) +% into a Binary JData (BJData, Draft 1), Universal Binary JSON (UBJSON, +% Draft-12) or a MessagePack binary stream +% +% author: Qianqian Fang (q.fang neu.edu) +% initially created on 2013/08/17 +% +% By default, this function creates BJD-compliant output. The BJD +% specification is largely similar to UBJSON, with additional data types +% including uint16(u), uint32(m), uint64(M) and half-precision float (h) +% +% Format specifications: +% Binary JData (BJD): https://github.com/fangq/bjdata +% UBJSON: https://github.com/ubjson/universal-binary-json +% MessagePack: https://github.com/msgpack/msgpack +% +% 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) +% +% FileName [''|string]: a file name to save the output JSON data +% ArrayToStruct[0|1]: when set to 0, savebj 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_":true will be added +% with a value of 1; for a complex array, the +% "_ArrayData_" array will include two rows +% (4 for sparse) to record the real and imaginary +% parts, and also "_ArrayIsComplex_":true is added. +% Other annotations include "_ArrayShape_" and +% "_ArrayOrder_", "_ArrayZipLevel_" etc. +% NestArray [0|1]: If set to 1, use nested array constructs +% to store N-dimensional arrays (compatible with +% UBJSON specification Draft 12); if set to 0, +% use the JData (v0.5) optimized N-D array header; +% NestArray is automatically set to 1 when +% MessagePack is set to 1 +% ParseLogical [1|0]: if this is set to 1, logical array elem +% will use true/false rather than 1/0. +% 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. +% 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. +% ForceRootName [0|1]: when set to 1 and rootname is empty, savebj +% 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. +% 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(...);' +% UnpackHex [1|0]: conver the 0x[hex code] output by loadjson +% back to the string form +% Compression 'zlib', 'gzip', 'lzma', 'lzip', 'lz4' or 'lz4hc': specify array +% compression method; currently only supports 6 methods. 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 +% "_ArrayZipType_": the opt.Compression value. +% "_ArrayZipSize_": a 1D interger array to +% store the pre-compressed (but post-processed) +% array dimensions, and +% "_ArrayZipData_": the binary stream of +% the compressed binary array data WITHOUT +% 'base64' encoding +% CompressArraySize [100|int]: only to compress an array if the total +% element count is larger than this number. +% CompressStringSize [400|int]: only to compress a string if the total +% element count is larger than this number. +% MessagePack [0|1]: output MessagePack (https://msgpack.org/) +% binary stream instead of BJD/UBJSON +% UBJSON [0|1]: 0: (default)-encode data based on BJData Draft 1 +% (supports uint16(u)/uint32(m)/uint64(M)/half(h) markers) +% 1: encode data based on UBJSON Draft 12 (without +% u/m/M/h markers) +% FormatVersion [2|float]: set the JSONLab output version; since +% v2.0, JSONLab uses JData specification Draft 3 +% for output format, it is incompatible with releases +% older than v1.9.8; if old output is desired, +% please set FormatVersion to 1.9 or earlier. +% KeepType [0|1]: if set to 1, use the original data type to store +% integers instead of converting to the integer type +% of the minimum length without losing accuracy (default) +% Debug [0|1]: output binary numbers in <%g> format for debugging +% Append [0|1]: if set to 1, append a new object at the end of the file. +% Endian ['n'|'b','l']: Endianness of the output file ('n': native, +% 'b': big endian, 'l': little-endian) +% PreEncode [1|0]: if set to 1, call jdataencode first to preprocess +% the input data before saving +% +% 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: +% bjd: a binary string in the UBJSON format (see http://ubjson.org) +% +% examples: +% jsonmesh=struct('MeshVertex3',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],... +% 'MeshTet4',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],... +% 'MeshTri3',[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]); +% savebj(jsonmesh) +% savebj('',jsonmesh,'meshdata.bjd') +% savebj('mesh1',jsonmesh,'FileName','meshdata.msgpk','MessagePack',1) +% savebj('',jsonmesh,'ubjson',1) +% +% license: +% BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files 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; + 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=isoctavemesh; +opt.compression=jsonopt('Compression','',opt); +opt.nestarray=jsonopt('NestArray',0,opt); +opt.formatversion=jsonopt('FormatVersion',2,opt); +opt.compressarraysize=jsonopt('CompressArraySize',100,opt); +opt.compressstringsize=jsonopt('CompressStringSize',opt.compressarraysize*4,opt); +opt.singletcell=jsonopt('SingletCell',1,opt); +opt.singletarray=jsonopt('SingletArray',0,opt); +opt.arraytostruct=jsonopt('ArrayToStruct',0,opt); +opt.debug=jsonopt('Debug',0,opt); +opt.messagepack=jsonopt('MessagePack',0,opt); +opt.num2cell_=0; +opt.ubjson=bitand(jsonopt('UBJSON',0,opt), ~opt.messagepack); +opt.keeptype=jsonopt('KeepType',0,opt); +opt.nosubstruct_=0; + +if(jsonopt('PreEncode',1,opt)) + obj=jdataencode(obj,'Base64',0,'UseArrayZipSize',opt.messagepack,opt); +end + +dozip=opt.compression; +if(~isempty(dozip)) + if(isempty(strmatch(dozip,{'zlib','gzip','lzma','lzip','lz4','lz4hc'}))) + error('compression method "%s" is not supported',dozip); + end + if(exist('zmat','file')~=2 && exist('zmat','file')~=3) + try + error(javachk('jvm')); + try + base64decode('test'); + catch + matlab.net.base64decode('test'); + end + catch + error('java-based compression is not supported'); + end + end +end + + +if(~opt.messagepack) + if(~opt.ubjson) + opt.IM_='UiuImlML'; + opt.IType_={'uint8','int8','uint16','int16','uint32','int32','uint64','int64'}; + opt.IByte_=[1,1,2,2,4,4,8,8]; + opt.FM_='hdD'; + opt.FType_={'half','single','double'}; + opt.FByte_=[2,4,8]; + else + opt.IM_='UiIlL'; + opt.IType_={'uint8','int8','int16','int32','int64'}; + opt.IByte_=[1,1,2,4,8]; + opt.FM_='IdD'; + opt.FType_={'int16','single','double'}; + opt.FByte_=[2,4,8]; + end + opt.FTM_='FT'; + opt.SM_='CS'; + opt.ZM_='Z'; + opt.OM_={'{','}'}; + opt.AM_={'[',']'}; +else + opt.IM_=char([hex2dec('cc') hex2dec('d0') hex2dec('cd') hex2dec('d1') hex2dec('ce') hex2dec('d2') hex2dec('cf') hex2dec('d3')]); + opt.IType_={'uint8','int8','uint16','int16','uint32','int32','uint64','int64'}; + opt.IByte_=[1,1,2,2,4,4,8,8]; + opt.FM_=char([hex2dec('cd') hex2dec('ca') hex2dec('cb')]); % MsgPack does not have half-precision, map to uint16 + opt.FType_={'int16','single','double'}; + opt.FByte_=[2,4,8]; + opt.FTM_=char([hex2dec('c2') hex2dec('c3')]); + opt.SM_=char([hex2dec('a1') hex2dec('db')]); + opt.ZM_=char(hex2dec('c0')); + opt.OM_={char(hex2dec('df')),''}; + opt.AM_={char(hex2dec('dd')),''}; +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) + if(opt.messagepack) + json=[char(129) json opt.OM_{2}]; + else + json=[opt.OM_{1} json opt.OM_{2}]; + end +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)) + encoding = jsonopt('Encoding','',opt); + endian = jsonopt('Endian','n',opt); + writemode='w'; + if(jsonopt('Append',0,opt)) + writemode='a'; + end + if(~exist('OCTAVE_VERSION','builtin')) + fid = fopen(filename, writemode, endian, encoding); + else + fid = fopen(filename, writemode, endian); + end + fwrite(fid,json); + fclose(fid); +end + +%%------------------------------------------------------------------------- +function txt=obj2ubjson(name,item,level,varargin) + +if(iscell(item) || isa(item,'string')) + txt=cell2ubjson(name,item,level,varargin{:}); +elseif(isstruct(item)) + txt=struct2ubjson(name,item,level,varargin{:}); +elseif(isnumeric(item) || islogical(item)) + txt=mat2ubjson(name,item,level,varargin{:}); +elseif(ischar(item)) + if(numel(item)>=varargin{1}.compressstringsize) + txt=mat2ubjson(name,item,level,varargin{:}); + else + txt=str2ubjson(name,item,level,varargin{:}); + end +elseif(isa(item,'function_handle')) + txt=struct2ubjson(name,functions(item),level,varargin{:}); +elseif(isa(item,'containers.Map')) + txt=map2ubjson(name,item,level,varargin{:}); +elseif(isa(item,'categorical')) + txt=cell2ubjson(name,cellstr(item),level,varargin{:}); +elseif(isa(item,'table')) + txt=matlabtable2ubjson(name,item,level,varargin{:}); +elseif(isa(item,'graph') || isa(item,'digraph')) + txt=struct2ubjson(name,jdataencode(item),level,varargin{:}); +elseif(isobject(item)) + txt=matlabobject2ubjson(name,item,level,varargin{:}); +else + txt=any2ubjson(name,item,level,varargin{:}); +end + +%%------------------------------------------------------------------------- +function txt=cell2ubjson(name,item,level,varargin) +txt=''; +if(~iscell(item) && ~isa(item,'string')) + error('input is not a cell'); +end +isnum2cell=varargin{1}.num2cell_; +if(isnum2cell) + item=squeeze(item); +else + format=varargin{1}.formatversion; + if(format>1.9 && ~isvector(item)) + item=permute(item,ndims(item):-1:1); + end +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=~varargin{1}.singletcell; +Zmarker=varargin{1}.ZM_; +Imarker=varargin{1}.IM_; +Amarker=varargin{1}.AM_; + +if(~strcmp(Amarker{1},'[')) + am0=Imsgpk_(dim(2),220,144,varargin{:}); +else + am0=Amarker{1}; +end +len=numel(item); % let's handle 1D cell first +if(len>bracketlevel) + if(~isempty(name)) + txt=[N_(decodevarname(name,varargin{:}),varargin{:}) am0]; name=''; + else + txt=am0; + end +elseif(len==0) + if(~isempty(name)) + txt=[N_(decodevarname(name,varargin{:}),varargin{:}) Zmarker]; name=''; + else + txt=Zmarker; + end +end +if(~strcmp(Amarker{1},'[')) + am0=Imsgpk_(dim(1),220,144,varargin{:}); +end +for j=1:dim(2) + if(dim(1)>1) + txt=[txt am0]; + end + for i=1:dim(1) + txt=[txt char(obj2ubjson(name,item{i,j},level+(len>bracketlevel),varargin{:}))]; + end + if(dim(1)>1) + txt=[txt Amarker{2}]; + end +end +if(len>bracketlevel) + txt=[txt Amarker{2}]; +end + +%%------------------------------------------------------------------------- +function txt=struct2ubjson(name,item,level,varargin) +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 || (varargin{1}.singletarray==1 && level>0)); +Imarker=varargin{1}.IM_; +Amarker=varargin{1}.AM_; +Omarker=varargin{1}.OM_; + +if(isfield(item,encodevarname('_ArrayType_',varargin{:}))) + varargin{1}.nosubstruct_=1; +end + +if(~strcmp(Amarker{1},'[')) + am0=Imsgpk_(dim(2),220,144,varargin{:}); +else + am0=Amarker{1}; +end + +if(~isempty(name)) + if(forcearray) + txt=[N_(decodevarname(name,varargin{:}),varargin{:}) am0]; + end +else + if(forcearray) + txt=am0; + end +end +if(~strcmp(Amarker{1},'[')) + am0=Imsgpk_(dim(1),220,144,varargin{:}); +end +for j=1:dim(2) + if(dim(1)>1) + txt=[txt am0]; + end + for i=1:dim(1) + names = fieldnames(item(i,j)); + if(~strcmp(Omarker{1},'{')) + om0=Imsgpk_(length(names),222,128,varargin{:}); + else + om0=Omarker{1}; + end + if(~isempty(name) && len==1 && ~forcearray) + txt=[txt N_(decodevarname(name,varargin{:}),varargin{:}) om0]; + else + txt=[txt om0]; + 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 Omarker{2}]; + end + if(dim(1)>1) + txt=[txt Amarker{2}]; + end +end +if(forcearray) + txt=[txt Amarker{2}]; +end + +%%------------------------------------------------------------------------- +function txt=map2ubjson(name,item,level,varargin) +txt=''; +if(~isa(item,'containers.Map')) + error('input is not a struct'); +end +dim=size(item); +names = keys(item); +val= values(item); +Omarker=varargin{1}.OM_; + +if(~strcmp(Omarker{1},'{')) + om0=Imsgpk_(length(names),222,128,varargin{:}); +else + om0=Omarker{1}; +end +len=prod(dim); +forcearray= (len>1 || (varargin{1}.singletarray==1 && level>0)); + +if(~isempty(name)) + if(forcearray) + txt=[N_(decodevarname(name,varargin{:}),varargin{:}) om0]; + end +else + if(forcearray) + txt=om0; + end +end +for i=1:dim(1) + if(~isempty(names{i})) + txt=[txt obj2ubjson(names{i},val{i},... + level+(dim(1)>1),varargin{:})]; + end +end +if(forcearray) + txt=[txt Omarker{2}]; +end + +%%------------------------------------------------------------------------- +function txt=str2ubjson(name,item,level,varargin) +txt=''; +if(~ischar(item)) + error('input is not a string'); +end +item=reshape(item, max(size(item),[1 0])); +len=size(item,1); +Amarker=varargin{1}.AM_; + +if(~strcmp(Amarker{1},'[')) + am0=Imsgpk_(len,220,144,varargin{:}); +else + am0=Amarker{1}; +end +if(~isempty(name)) + if(len>1) + txt=[N_(decodevarname(name,varargin{:}),varargin{:}) am0]; + end +else + if(len>1) + txt=am0; + end +end +for e=1:len + val=item(e,:); + if(len==1) + obj=[N_(decodevarname(name,varargin{:}),varargin{:}) '' '',S_(val,varargin{:}),'']; + if(isempty(name)) + obj=['',S_(val,varargin{:}),'']; + end + txt=[txt,'',obj]; + else + txt=[txt,'',['',S_(val,varargin{:}),'']]; + end +end +if(len>1) + txt=[txt Amarker{2}]; +end + +%%------------------------------------------------------------------------- +function txt=mat2ubjson(name,item,level,varargin) +if(~isnumeric(item) && ~islogical(item) && ~ischar(item)) + error('input is not an array'); +end + +dozip=varargin{1}.compression; +zipsize=varargin{1}.compressarraysize; +format=varargin{1}.formatversion; + +Zmarker=varargin{1}.ZM_; +FTmarker=varargin{1}.FTM_; +Imarker=varargin{1}.IM_; +Omarker=varargin{1}.OM_; +isnest=varargin{1}.nestarray; +ismsgpack=varargin{1}.messagepack; + +opt=varargin{1}; + +if(ismsgpack) + isnest=1; +end +if(~varargin{1}.nosubstruct_ && ((length(size(item))>2 && isnest==0) || ... + issparse(item) || ~isreal(item) || varargin{1}.arraytostruct || ... + (~isempty(dozip) && numel(item)>zipsize)) ) + cid=I_(uint32(max(size(item))),varargin{:}); + if(isempty(name)) + txt=[Omarker{1} N_('_ArrayType_',opt),S_(class(item),opt),N_('_ArraySize_',opt),I_a(size(item),cid(1),varargin{:}) ]; + else + if(isempty(item)) + txt=[N_(decodevarname(name,varargin{:}),opt),Zmarker]; + return; + else + txt=[N_(decodevarname(name,varargin{:}),opt),Omarker{1},N_('_ArrayType_',opt),S_(class(item),opt),N_('_ArraySize_',opt),I_a(size(item),cid(1),varargin{:})]; + end + end + childcount=2; +else + if(isempty(name)) + txt=matdata2ubjson(item,level+1,varargin{:}); + else + if(numel(item)==1 && varargin{1}.singletarray==0) + numtxt=matdata2ubjson(item,level+1,varargin{:}); + txt=[N_(decodevarname(name,varargin{:}),opt) char(numtxt)]; + else + txt=[N_(decodevarname(name,varargin{:}),opt),char(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_',opt),FTmarker(2)]; + childcount=childcount+1; + end + txt=[txt,N_('_ArrayIsSparse_',opt),FTmarker(2)]; + childcount=childcount+1; + 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))),varargin{:}); + txt=[txt, N_('_ArrayZipSize_',opt),I_a(size(fulldata),cid(1),varargin{:})]; + txt=[txt, N_('_ArrayZipType_',opt),S_(dozip,opt)]; + compfun=str2func([dozip 'encode']); + txt=[txt,N_('_ArrayZipData_',opt), I_a(compfun(typecast(fulldata(:),'uint8')),Imarker(1),varargin{:})]; + childcount=childcount+3; + else + 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 + if(ismsgpack) + cid=I_(uint32(max(size(fulldata))),varargin{:}); + txt=[txt,N_('_ArrayZipSize_',opt),I_a(size(fulldata),cid(1),varargin{:})]; + childcount=childcount+1; + end + varargin{:}.ArrayToStruct=0; + txt=[txt,N_('_ArrayData_',opt),... + cell2ubjson('',num2cell(fulldata',2)',level+2,varargin{:})]; + childcount=childcount+1; + end +else + if(format>1.9) + item=permute(item,ndims(item):-1:1); + end + if(~isempty(dozip) && numel(item)>zipsize) + if(isreal(item)) + fulldata=item(:)'; + if(islogical(fulldata) || ischar(fulldata)) + fulldata=uint8(fulldata); + end + else + txt=[txt,N_('_ArrayIsComplex_',opt),FTmarker(2)]; + childcount=childcount+1; + fulldata=[real(item(:)) imag(item(:))]; + end + cid=I_(uint32(max(size(fulldata))),varargin{:}); + txt=[txt, N_('_ArrayZipSize_',opt),I_a(size(fulldata),cid(1),varargin{:})]; + txt=[txt, N_('_ArrayZipType_',opt),S_(dozip,opt)]; + compfun=str2func([dozip 'encode']); + txt=[txt,N_('_ArrayZipData_',opt), I_a(compfun(typecast(fulldata(:),'uint8')),Imarker(1),varargin{:})]; + childcount=childcount+3; + else + if(ismsgpack) + cid=I_(uint32(length(item(:))),varargin{:}); + txt=[txt,N_('_ArrayZipSize_',opt),I_a([~isreal(item)+1 length(item(:))],cid(1),varargin{:})]; + childcount=childcount+1; + end + if(isreal(item)) + txt=[txt,N_('_ArrayData_',opt),... + matdata2ubjson(item(:)',level+2,varargin{:})]; + childcount=childcount+1; + else + txt=[txt,N_('_ArrayIsComplex_',opt),FTmarker(2)]; + txt=[txt,N_('_ArrayData_',opt),... + matdata2ubjson([real(item(:)) imag(item(:))]',level+2,varargin{:})]; + childcount=childcount+2; + end + end +end +if(Omarker{1}~='{') + idx=find(txt==Omarker{1},1,'first'); + if(~isempty(idx)) + txt=[txt(1:idx-1) Imsgpk_(childcount,222,128,varargin{:}) txt(idx+1:end)]; + end +end +txt=[txt,Omarker{2}]; + +%%------------------------------------------------------------------------- +function txt=matlabtable2ubjson(name,item,level,varargin) +st=containers.Map(); +st('_TableRecords_')=table2cell(item); +st('_TableRows_')=item.Properties.RowNames'; +st('_TableCols_')=item.Properties.VariableNames; +if(isempty(name)) + txt=map2ubjson(name,st,level,varargin{:}); +else + temp=struct(name,struct()); + temp.(name)=st; + txt=map2ubjson(name,temp.(name),level,varargin{:}); +end + +%%------------------------------------------------------------------------- +function txt=matlabobject2ubjson(name,item,level,varargin) +try + if numel(item) == 0 %empty object + st = struct(); + elseif numel(item) == 1 % + txt = str2ubjson(name, char(item), level, varargin(:)); + return + else + 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{:}); +catch + txt = any2ubjson(name,item, level, varargin(:)); +end + +%%------------------------------------------------------------------------- +function txt=matdata2ubjson(mat,level,varargin) +Zmarker=varargin{1}.ZM_; +if(isempty(mat)) + txt=Zmarker; + return; +end + +FTmarker=varargin{1}.FTM_; +Imarker=varargin{1}.IM_; +Fmarker=varargin{1}.FM_; +Amarker=varargin{1}.AM_; + +isnest=varargin{:}.nestarray; +ismsgpack=varargin{1}.messagepack; +format=varargin{1}.formatversion; +isnum2cell=varargin{1}.num2cell_; + +if(ismsgpack) + isnest=1; +end + +if(~isvector(mat) && isnest==1) + if(format>1.9 && isnum2cell==0) + mat=permute(mat,ndims(mat):-1:1); + end + varargin{1}.num2cell_=1; +end + +if(isa(mat,'integer') || isinteger(mat) || (~varargin{1}.keeptype && isfloat(mat) && all(mod(mat(:),1) == 0))) + if(~isvector(mat) && isnest==1) + txt=cell2ubjson('',num2cell(mat,1),level,varargin{:}); + elseif(~ismsgpack || size(mat,1)==1) + if(0 && varargin{1}.keeptype) + itype=class(mat); + idx=find(ismember(varargin{1}.IType_,itype)); + if(isempty(idx)) + idx=find(ismember(varargin{1}.IType_,itype(2:end))); + end + type=Imarker(idx); + varargin{1}.inttype_=idx; + elseif(~any(mat<0)) + cid=varargin{1}.IType_; + type=Imarker(end); + maxdata=max(double(mat(:))); + for i=1:length(cid) + if(maxdata==cast(maxdata,cid{i})) + type=Imarker(i); + break; + end + end + else + cid=varargin{1}.IType_; + type=Imarker(end); + mindata=min(double(mat(:))); + maxdata=max(double(mat(:))); + for i=1:length(cid) + if(maxdata==cast(maxdata,cid{i}) && mindata==cast(mindata,cid{i})) + type=Imarker(i); + break; + end + end + end + if(numel(mat)==1) + if(mat<0) + txt=I_(int64(mat),varargin{:}); + else + txt=I_(uint64(mat),varargin{:}); + end + else + txt=I_a(mat(:),type,size(mat),varargin{:}); + end + else + txt=cell2ubjson('',num2cell(mat,2),level,varargin{:}); + end +elseif(islogical(mat)) + logicalval=FTmarker; + if(numel(mat)==1) + txt=logicalval(mat+1); + else + if(~isvector(mat) && isnest==1) + txt=cell2ubjson('',num2cell(uint8(mat),1),level,varargin{:}); + else + txt=I_a(uint8(mat(:)),Imarker(1),size(mat),varargin{:}); + end + end +else + am0=Amarker{1}; + if(Amarker{1}~='[') + am0=char(145); + end + if(numel(mat)==1) + txt=[am0 D_(mat,varargin{:}) Amarker{2}]; + else + if(~isvector(mat) && isnest==1) + txt=cell2ubjson('',num2cell(mat,1),level,varargin{:}); + else + txt=D_a(mat(:),Fmarker(3),size(mat),varargin{:}); + end + end +end + +%%------------------------------------------------------------------------- +function val=N_(str,varargin) +ismsgpack=varargin{1}.messagepack; +if(~ismsgpack) + val=[I_(int32(length(str)),varargin{:}) str]; +else + val=S_(str,varargin{:}); +end +%%------------------------------------------------------------------------- +function val=S_(str,varargin) +ismsgpack=varargin{1}.messagepack; +Smarker=varargin{1}.SM_; +if(length(str)==1) + val=[Smarker(1) str]; +else + if(ismsgpack) + val=[Imsgpk_(length(str),218,160,varargin{:}) str]; + else + val=['S' I_(int32(length(str)),varargin{:}) str]; + end +end + +%%------------------------------------------------------------------------- +function val=Imsgpk_(num,base1,base0,varargin) +if(num<16) + val=char(uint8(num)+uint8(base0)); + return; +end +val=I_(uint32(num),varargin{:}); +if(val(1)>char(210)) + num=uint32(num); + val=[char(210) data2byte(swapbytes(cast(num,'uint32')),'uint8')]; +elseif(val(1)',num)]; + else + val=[Imarker(varargin{1}.inttype_) data2byte(swapbytes(cast(num,cid{varargin{1}.inttype_})),'uint8')]; + end + return; +end +if(Imarker(1)~='U') + if(num>=0 && num<127) + val=uint8(num); + return; + end + if(num<0 && num>=-31) + val=typecast(int8(num), 'uint8'); + return; + end +end +if(Imarker(1)~='U' && num<0 && num<127) + val=data2byte((swapbytes(cast(num,'uint8')) & 127),'uint8'); + return; +end +key=Imarker; +for i=1:length(cid) + if(num==cast(num,cid{i})) + if(isdebug) + val=[key(i) sprintf('<%.0f>',num)]; + else + val=[key(i) data2byte(swapbytes(cast(num,cid{i})),'uint8')]; + end + return; + end +end +val=S_(sprintf('%.0f',num),varargin{:}); +if(Imarker(1)=='U') + val(1)='H'; +end +%%------------------------------------------------------------------------- +function val=D_(num, varargin) +if(~isfloat(num)) + error('input is not a float'); +end +isdebug=varargin{1}.debug; +if(isdebug) + output=sprintf('<%g>',num); +else + output=data2byte(swapbytes(num),'uint8'); +end +Fmarker=varargin{1}.FM_; + +if(isa(num,'half')) + val=[Fmarker(1) output(:)']; +elseif(isa(num,'single')) + val=[Fmarker(2) output(:)']; +else + val=[Fmarker(3) output(:)']; +end +%%------------------------------------------------------------------------- +function data=I_a(num,type,dim,varargin) +if(isstruct(dim)) + varargin={dim}; +end +Imarker=varargin{1}.IM_; +Amarker=varargin{1}.AM_; + +if(Imarker(1)~='U' && type<=127) + type=char(204); +end +id=find(ismember(Imarker,type)); + +if(id==0) + error('unsupported integer array'); +end + +% based on UBJSON specs, all integer types are stored in big endian format + +cid=varargin{1}.IType_; +data=data2byte(swapbytes(cast(num,cid{id})),'uint8'); +blen=varargin{1}.IByte_(id); + + +isnest=varargin{1}.nestarray; +isdebug=varargin{1}.debug; +if(isdebug) + output=sprintf('<%g>',num); +else + output=data(:); +end + +if(isnest==0 && numel(num)>1 && Imarker(1)=='U') + if(nargin>=4 && ~isstruct(dim) && (length(dim)==1 || (length(dim)>=2 && prod(dim)~=dim(2)))) + cid=I_(uint32(max(dim)),varargin{:}); + data=['$' type '#' I_a(dim,cid(1),varargin{:}) output(:)']; + else + data=['$' type '#' I_(int32(numel(data)/blen),varargin{:}) output(:)']; + end + data=['[' data(:)']; +else + am0=Amarker{1}; + if(Imarker(1)~='U') + Amarker={char(hex2dec('dd')),''}; + am0=Imsgpk_(numel(num),220,144,varargin{:}); + end + if(isdebug) + data=sprintf([type '<%g>'],num); + else + data=reshape(data,blen,numel(data)/blen); + data(2:blen+1,:)=data; + data(1,:)=type; + end + data=[am0 data(:)' Amarker{2}]; +end +%%------------------------------------------------------------------------- +function data=D_a(num,type,dim,varargin) +Fmarker=varargin{1}.FM_; +Amarker=varargin{1}.AM_; + +id=find(ismember(Fmarker,type)); + +if(id==0) + error('unsupported float array'); +end + +data=data2byte(swapbytes(cast(num,varargin{1}.FType_{id})),'uint8'); +blen=varargin{1}.FByte_(id); + +isnest=varargin{1}.nestarray; +isdebug=varargin{1}.debug; +if(isdebug) + output=sprintf('<%g>',num); +else + output=data(:); +end + +if(isnest==0 && numel(num)>1 && Fmarker(end)=='D') + if(nargin>=4 && (length(dim)==1 || (length(dim)>=2 && prod(dim)~=dim(2)))) + cid=I_(uint32(max(dim)),varargin{:}); + data=['$' type '#' I_a(dim,cid(1),varargin{:}) output(:)']; + else + data=['$' type '#' I_(int32(numel(data)/blen),varargin{:}) output(:)']; + end + data=['[' data]; +else + am0=Amarker{1}; + if(Fmarker(end)~='D') + Amarker={char(hex2dec('dd')),''}; + am0=Imsgpk_(numel(num),220,144,varargin{:}); + end + if(isdebug) + data=sprintf([type '<%g>'],num); + else + data=reshape(data,blen,length(data)/blen); + data(2:(blen+1),:)=data; + data(1,:)=type; + end + data=[am0 data(:)' Amarker{2}]; +end + +%%------------------------------------------------------------------------- +function txt=any2ubjson(name,item,level,varargin) +st=containers.Map(); +st('_DataInfo_')=struct('MATLABObjectClass',class(item),'MATLABObjectSize',size(item)); +st('_ByteStream_')=getByteStreamFromArray(item); + +if(isempty(name)) + txt=map2ubjson(name,st,level,varargin{:}); +else + temp=struct(name,struct()); + temp.(name)=st; + txt=map2ubjson(name,temp.(name),level,varargin{:}); +end + +%%------------------------------------------------------------------------- +function bytes=data2byte(varargin) +bytes=typecast(varargin{:}); +bytes=char(bytes(:)'); diff --git a/savemsgpack.m b/savemsgpack.m index 46a79d3..fd5823f 100644 --- a/savemsgpack.m +++ b/savemsgpack.m @@ -12,9 +12,9 @@ % author: Qianqian Fang (q.fang neu.edu) % initially created on 2019/05/20 % -% This function is the same as calling saveubjson(...,'MessagePack',1) +% This function is the same as calling savebj(...,'MessagePack',1) % -% Please type "help saveubjson" for details for the supported inputs and outputs. +% Please type "help savebj" for details for the supported inputs and outputs. % % license: % BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details @@ -23,9 +23,9 @@ % if(nargin==1) - msgpk=saveubjson('',rootname,'MessagePack',1); + msgpk=savebj('',rootname,'MessagePack',1); elseif(length(varargin)==1 && ischar(varargin{1})) - msgpk=saveubjson(rootname,obj,'FileName',varargin{1},'MessagePack',1); + msgpk=savebj(rootname,obj,'FileName',varargin{1},'MessagePack',1); else - msgpk=saveubjson(rootname,obj,varargin{:},'MessagePack',1); + msgpk=savebj(rootname,obj,varargin{:},'MessagePack',1); end \ No newline at end of file diff --git a/saveubjson.m b/saveubjson.m index 85b2d88..cc14cd8 100644 --- a/saveubjson.m +++ b/saveubjson.m @@ -1,27 +1,31 @@ -function json=saveubjson(rootname,obj,varargin) +function ubj=saveubjson(rootname,obj,varargin) % -% json=saveubjson(obj) +% ubj=saveubjson(obj) % or -% json=saveubjson(rootname,obj,filename) -% json=saveubjson(rootname,obj,opt) -% json=saveubjson(rootname,obj,'param1',value1,'param2',value2,...) +% ubj=saveubjson(rootname,obj,filename) +% ubj=saveubjson(rootname,obj,opt) +% ubj=saveubjson(rootname,obj,'param1',value1,'param2',value2,...) % % Convert a MATLAB object (cell, struct, array, table, map, handles ...) -% into a Binary JData (BJD, Draft 1), Universal Binary JSON (UBJSON, Draft -% 12) or a MessagePack binary stream +% into a Universal Binary JSON (UBJSON, Draft 12) or a MessagePack binary stream % % author: Qianqian Fang (q.fang neu.edu) % initially created on 2013/08/17 % -% By default, this function creates BJD-compliant output. The BJD -% specification is largely similar to UBJSON, with additional data types -% including uint16(u), uint32(m), uint64(M) and half-precision float (h) -% % Format specifications: -% Binary JData (BJD): https://github.com/fangq/bjdata +% Binary JData (BJData):https://github.com/fangq/bjdata % UBJSON: https://github.com/ubjson/universal-binary-json % MessagePack: https://github.com/msgpack/msgpack % +% This function is the same as calling "savebj(...,'ubjson',1)". By , +% default this function creates UBJSON-compliant output without the +% newly added uint16(u), uint32(m), uint64(M) and half-precision float (h) +% data types. +% +% This function by default still enables an optimized ND-array format for efficient +% array storage. To ensure the output compatible to UBJSON Draft-12, one should use +% "saveubjson(...,'NestArray',1)" or "savebj(...,'ubjson',1,'NestArray',1)" +% % 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), @@ -32,88 +36,11 @@ % opt: a struct for additional options, ignore to use default values. % opt can have the following fields (first in [.|.] is the default) % -% FileName [''|string]: a file name to save the output JSON data -% 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_":true will be added -% with a value of 1; for a complex array, the -% "_ArrayData_" array will include two rows -% (4 for sparse) to record the real and imaginary -% parts, and also "_ArrayIsComplex_":true is added. -% Other annotations include "_ArrayShape_" and -% "_ArrayOrder_", "_ArrayZipLevel_" etc. -% NestArray [0|1]: If set to 1, use nested array constructs -% to store N-dimensional arrays (compatible with -% UBJSON specification Draft 12); if set to 0, -% use the JData (v0.5) optimized N-D array header; -% NestArray is automatically set to 1 when -% MessagePack is set to 1 -% ParseLogical [1|0]: if this is set to 1, logical array elem -% will use true/false rather than 1/0. -% 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. -% 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. -% 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. -% 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(...);' -% UnpackHex [1|0]: conver the 0x[hex code] output by loadjson -% back to the string form -% Compression 'zlib', 'gzip', 'lzma', 'lzip', 'lz4' or 'lz4hc': specify array -% compression method; currently only supports 6 methods. 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 -% "_ArrayZipType_": the opt.Compression value. -% "_ArrayZipSize_": a 1D interger array to -% store the pre-compressed (but post-processed) -% array dimensions, and -% "_ArrayZipData_": the binary stream of -% the compressed binary array data WITHOUT -% 'base64' encoding -% CompressArraySize [100|int]: only to compress an array if the total -% element count is larger than this number. -% CompressStringSize [400|int]: only to compress a string if the total -% element count is larger than this number. -% MessagePack [0|1]: output MessagePack (https://msgpack.org/) -% binary stream instead of BJD/UBJSON -% UBJSON [0|1]: 0: (default)-encode data based on BJData Draft 1 -% (supports uint16(u)/uint32(m)/uint64(M)/half(h) markers) -% 1: encode data based on UBJSON Draft 12 (without -% u/m/M/h markers) -% FormatVersion [2|float]: set the JSONLab output version; since -% v2.0, JSONLab uses JData specification Draft 3 -% for output format, it is incompatible with releases -% older than v1.9.8; if old output is desired, -% please set FormatVersion to 1.9 or earlier. -% KeepType [0|1]: if set to 1, use the original data type to store -% integers instead of converting to the integer type -% of the minimum length without losing accuracy (default) -% Debug [0|1]: output binary numbers in <%g> format for debugging -% Append [0|1]: if set to 1, append a new object at the end of the file. -% Endian ['n'|'b','l']: Endianness of the output file ('n': native, -% 'b': big endian, 'l': little-endian) -% PreEncode [1|0]: if set to 1, call jdataencode first to preprocess -% the input data before saving +% 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. +% +% Please type "help savebj" for details for all supported options. % -% 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) % @@ -125,9 +52,9 @@ % 'MeshCreator','FangQ','MeshTitle','T6 Cube',... % 'SpecialData',[nan, inf, -inf]); % saveubjson(jsonmesh) -% saveubjson('',jsonmesh,'meshdata.bjd') +% saveubjson('',jsonmesh,'meshdata.ubj') % saveubjson('mesh1',jsonmesh,'FileName','meshdata.msgpk','MessagePack',1) -% saveubjson('',jsonmesh,'ubjson',1) +% saveubjson('',jsonmesh,'KeepType',1) % % license: % BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details @@ -136,893 +63,9 @@ % if(nargin==1) - varname=inputname(1); - obj=rootname; - 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=isoctavemesh; -opt.compression=jsonopt('Compression','',opt); -opt.nestarray=jsonopt('NestArray',0,opt); -opt.formatversion=jsonopt('FormatVersion',2,opt); -opt.compressarraysize=jsonopt('CompressArraySize',100,opt); -opt.compressstringsize=jsonopt('CompressStringSize',opt.compressarraysize*4,opt); -opt.singletcell=jsonopt('SingletCell',1,opt); -opt.singletarray=jsonopt('SingletArray',0,opt); -opt.arraytostruct=jsonopt('ArrayToStruct',0,opt); -opt.debug=jsonopt('Debug',0,opt); -opt.messagepack=jsonopt('MessagePack',0,opt); -opt.num2cell_=0; -opt.ubjson=bitand(jsonopt('UBJSON',0,opt), ~opt.messagepack); -opt.keeptype=jsonopt('KeepType',0,opt); -opt.nosubstruct_=0; - -if(jsonopt('PreEncode',1,opt)) - obj=jdataencode(obj,'Base64',0,'UseArrayZipSize',opt.messagepack,opt); -end - -dozip=opt.compression; -if(~isempty(dozip)) - if(isempty(strmatch(dozip,{'zlib','gzip','lzma','lzip','lz4','lz4hc'}))) - error('compression method "%s" is not supported',dozip); - end - if(exist('zmat','file')~=2 && exist('zmat','file')~=3) - try - error(javachk('jvm')); - try - base64decode('test'); - catch - matlab.net.base64decode('test'); - end - catch - error('java-based compression is not supported'); - end - end -end - - -if(~opt.messagepack) - if(~opt.ubjson) - opt.IM_='UiuImlML'; - opt.IType_={'uint8','int8','uint16','int16','uint32','int32','uint64','int64'}; - opt.IByte_=[1,1,2,2,4,4,8,8]; - opt.FM_='hdD'; - opt.FType_={'half','single','double'}; - opt.FByte_=[2,4,8]; - else - opt.IM_='UiIlL'; - opt.IType_={'uint8','int8','int16','int32','int64'}; - opt.IByte_=[1,1,2,4,8]; - opt.FM_='IdD'; - opt.FType_={'int16','single','double'}; - opt.FByte_=[2,4,8]; - end - opt.FTM_='FT'; - opt.SM_='CS'; - opt.ZM_='Z'; - opt.OM_={'{','}'}; - opt.AM_={'[',']'}; -else - opt.IM_=char([hex2dec('cc') hex2dec('d0') hex2dec('cd') hex2dec('d1') hex2dec('ce') hex2dec('d2') hex2dec('cf') hex2dec('d3')]); - opt.IType_={'uint8','int8','uint16','int16','uint32','int32','uint64','int64'}; - opt.IByte_=[1,1,2,2,4,4,8,8]; - opt.FM_=char([hex2dec('cd') hex2dec('ca') hex2dec('cb')]); % MsgPack does not have half-precision, map to uint16 - opt.FType_={'int16','single','double'}; - opt.FByte_=[2,4,8]; - opt.FTM_=char([hex2dec('c2') hex2dec('c3')]); - opt.SM_=char([hex2dec('a1') hex2dec('db')]); - opt.ZM_=char(hex2dec('c0')); - opt.OM_={char(hex2dec('df')),''}; - opt.AM_={char(hex2dec('dd')),''}; -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) - if(opt.messagepack) - json=[char(129) json opt.OM_{2}]; - else - json=[opt.OM_{1} json opt.OM_{2}]; - end -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)) - encoding = jsonopt('Encoding','',opt); - endian = jsonopt('Endian','n',opt); - writemode='w'; - if(jsonopt('Append',0,opt)) - writemode='a'; - end - if(~exist('OCTAVE_VERSION','builtin')) - fid = fopen(filename, writemode, endian, encoding); - else - fid = fopen(filename, writemode, endian); - end - fwrite(fid,json); - fclose(fid); -end - -%%------------------------------------------------------------------------- -function txt=obj2ubjson(name,item,level,varargin) - -if(iscell(item) || isa(item,'string')) - txt=cell2ubjson(name,item,level,varargin{:}); -elseif(isstruct(item)) - txt=struct2ubjson(name,item,level,varargin{:}); -elseif(isnumeric(item) || islogical(item)) - txt=mat2ubjson(name,item,level,varargin{:}); -elseif(ischar(item)) - if(numel(item)>=varargin{1}.compressstringsize) - txt=mat2ubjson(name,item,level,varargin{:}); - else - txt=str2ubjson(name,item,level,varargin{:}); - end -elseif(isa(item,'function_handle')) - txt=struct2ubjson(name,functions(item),level,varargin{:}); -elseif(isa(item,'containers.Map')) - txt=map2ubjson(name,item,level,varargin{:}); -elseif(isa(item,'categorical')) - txt=cell2ubjson(name,cellstr(item),level,varargin{:}); -elseif(isa(item,'table')) - txt=matlabtable2ubjson(name,item,level,varargin{:}); -elseif(isa(item,'graph') || isa(item,'digraph')) - txt=struct2ubjson(name,jdataencode(item),level,varargin{:}); -elseif(isobject(item)) - txt=matlabobject2ubjson(name,item,level,varargin{:}); -else - txt=any2ubjson(name,item,level,varargin{:}); -end - -%%------------------------------------------------------------------------- -function txt=cell2ubjson(name,item,level,varargin) -txt=''; -if(~iscell(item) && ~isa(item,'string')) - error('input is not a cell'); -end -isnum2cell=varargin{1}.num2cell_; -if(isnum2cell) - item=squeeze(item); -else - format=varargin{1}.formatversion; - if(format>1.9 && ~isvector(item)) - item=permute(item,ndims(item):-1:1); - end -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=~varargin{1}.singletcell; -Zmarker=varargin{1}.ZM_; -Imarker=varargin{1}.IM_; -Amarker=varargin{1}.AM_; - -if(~strcmp(Amarker{1},'[')) - am0=Imsgpk_(dim(2),220,144,varargin{:}); -else - am0=Amarker{1}; -end -len=numel(item); % let's handle 1D cell first -if(len>bracketlevel) - if(~isempty(name)) - txt=[N_(decodevarname(name,varargin{:}),varargin{:}) am0]; name=''; - else - txt=am0; - end -elseif(len==0) - if(~isempty(name)) - txt=[N_(decodevarname(name,varargin{:}),varargin{:}) Zmarker]; name=''; - else - txt=Zmarker; - end -end -if(~strcmp(Amarker{1},'[')) - am0=Imsgpk_(dim(1),220,144,varargin{:}); -end -for j=1:dim(2) - if(dim(1)>1) - txt=[txt am0]; - end - for i=1:dim(1) - txt=[txt char(obj2ubjson(name,item{i,j},level+(len>bracketlevel),varargin{:}))]; - end - if(dim(1)>1) - txt=[txt Amarker{2}]; - end -end -if(len>bracketlevel) - txt=[txt Amarker{2}]; -end - -%%------------------------------------------------------------------------- -function txt=struct2ubjson(name,item,level,varargin) -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 || (varargin{1}.singletarray==1 && level>0)); -Imarker=varargin{1}.IM_; -Amarker=varargin{1}.AM_; -Omarker=varargin{1}.OM_; - -if(isfield(item,encodevarname('_ArrayType_',varargin{:}))) - varargin{1}.nosubstruct_=1; -end - -if(~strcmp(Amarker{1},'[')) - am0=Imsgpk_(dim(2),220,144,varargin{:}); -else - am0=Amarker{1}; -end - -if(~isempty(name)) - if(forcearray) - txt=[N_(decodevarname(name,varargin{:}),varargin{:}) am0]; - end -else - if(forcearray) - txt=am0; - end -end -if(~strcmp(Amarker{1},'[')) - am0=Imsgpk_(dim(1),220,144,varargin{:}); -end -for j=1:dim(2) - if(dim(1)>1) - txt=[txt am0]; - end - for i=1:dim(1) - names = fieldnames(item(i,j)); - if(~strcmp(Omarker{1},'{')) - om0=Imsgpk_(length(names),222,128,varargin{:}); - else - om0=Omarker{1}; - end - if(~isempty(name) && len==1 && ~forcearray) - txt=[txt N_(decodevarname(name,varargin{:}),varargin{:}) om0]; - else - txt=[txt om0]; - 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 Omarker{2}]; - end - if(dim(1)>1) - txt=[txt Amarker{2}]; - end -end -if(forcearray) - txt=[txt Amarker{2}]; -end - -%%------------------------------------------------------------------------- -function txt=map2ubjson(name,item,level,varargin) -txt=''; -if(~isa(item,'containers.Map')) - error('input is not a struct'); -end -dim=size(item); -names = keys(item); -val= values(item); -Omarker=varargin{1}.OM_; - -if(~strcmp(Omarker{1},'{')) - om0=Imsgpk_(length(names),222,128,varargin{:}); -else - om0=Omarker{1}; -end -len=prod(dim); -forcearray= (len>1 || (varargin{1}.singletarray==1 && level>0)); - -if(~isempty(name)) - if(forcearray) - txt=[N_(decodevarname(name,varargin{:}),varargin{:}) om0]; - end -else - if(forcearray) - txt=om0; - end -end -for i=1:dim(1) - if(~isempty(names{i})) - txt=[txt obj2ubjson(names{i},val{i},... - level+(dim(1)>1),varargin{:})]; - end -end -if(forcearray) - txt=[txt Omarker{2}]; -end - -%%------------------------------------------------------------------------- -function txt=str2ubjson(name,item,level,varargin) -txt=''; -if(~ischar(item)) - error('input is not a string'); -end -item=reshape(item, max(size(item),[1 0])); -len=size(item,1); -Amarker=varargin{1}.AM_; - -if(~strcmp(Amarker{1},'[')) - am0=Imsgpk_(len,220,144,varargin{:}); -else - am0=Amarker{1}; -end -if(~isempty(name)) - if(len>1) - txt=[N_(decodevarname(name,varargin{:}),varargin{:}) am0]; - end -else - if(len>1) - txt=am0; - end -end -for e=1:len - val=item(e,:); - if(len==1) - obj=[N_(decodevarname(name,varargin{:}),varargin{:}) '' '',S_(val,varargin{:}),'']; - if(isempty(name)) - obj=['',S_(val,varargin{:}),'']; - end - txt=[txt,'',obj]; - else - txt=[txt,'',['',S_(val,varargin{:}),'']]; - end -end -if(len>1) - txt=[txt Amarker{2}]; -end - -%%------------------------------------------------------------------------- -function txt=mat2ubjson(name,item,level,varargin) -if(~isnumeric(item) && ~islogical(item) && ~ischar(item)) - error('input is not an array'); -end - -dozip=varargin{1}.compression; -zipsize=varargin{1}.compressarraysize; -format=varargin{1}.formatversion; - -Zmarker=varargin{1}.ZM_; -FTmarker=varargin{1}.FTM_; -Imarker=varargin{1}.IM_; -Omarker=varargin{1}.OM_; -isnest=varargin{1}.nestarray; -ismsgpack=varargin{1}.messagepack; - -opt=varargin{1}; - -if(ismsgpack) - isnest=1; -end -if(~varargin{1}.nosubstruct_ && ((length(size(item))>2 && isnest==0) || ... - issparse(item) || ~isreal(item) || varargin{1}.arraytostruct || ... - (~isempty(dozip) && numel(item)>zipsize)) ) - cid=I_(uint32(max(size(item))),varargin{:}); - if(isempty(name)) - txt=[Omarker{1} N_('_ArrayType_',opt),S_(class(item),opt),N_('_ArraySize_',opt),I_a(size(item),cid(1),varargin{:}) ]; - else - if(isempty(item)) - txt=[N_(decodevarname(name,varargin{:}),opt),Zmarker]; - return; - else - txt=[N_(decodevarname(name,varargin{:}),opt),Omarker{1},N_('_ArrayType_',opt),S_(class(item),opt),N_('_ArraySize_',opt),I_a(size(item),cid(1),varargin{:})]; - end - end - childcount=2; -else - if(isempty(name)) - txt=matdata2ubjson(item,level+1,varargin{:}); - else - if(numel(item)==1 && varargin{1}.singletarray==0) - numtxt=matdata2ubjson(item,level+1,varargin{:}); - txt=[N_(decodevarname(name,varargin{:}),opt) char(numtxt)]; - else - txt=[N_(decodevarname(name,varargin{:}),opt),char(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_',opt),FTmarker(2)]; - childcount=childcount+1; - end - txt=[txt,N_('_ArrayIsSparse_',opt),FTmarker(2)]; - childcount=childcount+1; - 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))),varargin{:}); - txt=[txt, N_('_ArrayZipSize_',opt),I_a(size(fulldata),cid(1),varargin{:})]; - txt=[txt, N_('_ArrayZipType_',opt),S_(dozip,opt)]; - compfun=str2func([dozip 'encode']); - txt=[txt,N_('_ArrayZipData_',opt), I_a(compfun(typecast(fulldata(:),'uint8')),Imarker(1),varargin{:})]; - childcount=childcount+3; - else - 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 - if(ismsgpack) - cid=I_(uint32(max(size(fulldata))),varargin{:}); - txt=[txt,N_('_ArrayZipSize_',opt),I_a(size(fulldata),cid(1),varargin{:})]; - childcount=childcount+1; - end - varargin{:}.ArrayToStruct=0; - txt=[txt,N_('_ArrayData_',opt),... - cell2ubjson('',num2cell(fulldata',2)',level+2,varargin{:})]; - childcount=childcount+1; - end -else - if(format>1.9) - item=permute(item,ndims(item):-1:1); - end - if(~isempty(dozip) && numel(item)>zipsize) - if(isreal(item)) - fulldata=item(:)'; - if(islogical(fulldata) || ischar(fulldata)) - fulldata=uint8(fulldata); - end - else - txt=[txt,N_('_ArrayIsComplex_',opt),FTmarker(2)]; - childcount=childcount+1; - fulldata=[real(item(:)) imag(item(:))]; - end - cid=I_(uint32(max(size(fulldata))),varargin{:}); - txt=[txt, N_('_ArrayZipSize_',opt),I_a(size(fulldata),cid(1),varargin{:})]; - txt=[txt, N_('_ArrayZipType_',opt),S_(dozip,opt)]; - compfun=str2func([dozip 'encode']); - txt=[txt,N_('_ArrayZipData_',opt), I_a(compfun(typecast(fulldata(:),'uint8')),Imarker(1),varargin{:})]; - childcount=childcount+3; - else - if(ismsgpack) - cid=I_(uint32(length(item(:))),varargin{:}); - txt=[txt,N_('_ArrayZipSize_',opt),I_a([~isreal(item)+1 length(item(:))],cid(1),varargin{:})]; - childcount=childcount+1; - end - if(isreal(item)) - txt=[txt,N_('_ArrayData_',opt),... - matdata2ubjson(item(:)',level+2,varargin{:})]; - childcount=childcount+1; - else - txt=[txt,N_('_ArrayIsComplex_',opt),FTmarker(2)]; - txt=[txt,N_('_ArrayData_',opt),... - matdata2ubjson([real(item(:)) imag(item(:))]',level+2,varargin{:})]; - childcount=childcount+2; - end - end -end -if(Omarker{1}~='{') - idx=find(txt==Omarker{1},1,'first'); - if(~isempty(idx)) - txt=[txt(1:idx-1) Imsgpk_(childcount,222,128,varargin{:}) txt(idx+1:end)]; - end -end -txt=[txt,Omarker{2}]; - -%%------------------------------------------------------------------------- -function txt=matlabtable2ubjson(name,item,level,varargin) -st=containers.Map(); -st('_TableRecords_')=table2cell(item); -st('_TableRows_')=item.Properties.RowNames'; -st('_TableCols_')=item.Properties.VariableNames; -if(isempty(name)) - txt=map2ubjson(name,st,level,varargin{:}); -else - temp=struct(name,struct()); - temp.(name)=st; - txt=map2ubjson(name,temp.(name),level,varargin{:}); -end - -%%------------------------------------------------------------------------- -function txt=matlabobject2ubjson(name,item,level,varargin) -try - if numel(item) == 0 %empty object - st = struct(); - elseif numel(item) == 1 % - txt = str2ubjson(name, char(item), level, varargin(:)); - return - else - 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{:}); -catch - txt = any2ubjson(name,item, level, varargin(:)); -end - -%%------------------------------------------------------------------------- -function txt=matdata2ubjson(mat,level,varargin) -Zmarker=varargin{1}.ZM_; -if(isempty(mat)) - txt=Zmarker; - return; -end - -FTmarker=varargin{1}.FTM_; -Imarker=varargin{1}.IM_; -Fmarker=varargin{1}.FM_; -Amarker=varargin{1}.AM_; - -isnest=varargin{:}.nestarray; -ismsgpack=varargin{1}.messagepack; -format=varargin{1}.formatversion; -isnum2cell=varargin{1}.num2cell_; - -if(ismsgpack) - isnest=1; -end - -if(~isvector(mat) && isnest==1) - if(format>1.9 && isnum2cell==0) - mat=permute(mat,ndims(mat):-1:1); - end - varargin{1}.num2cell_=1; -end - -if(isa(mat,'integer') || isinteger(mat) || (~varargin{1}.keeptype && isfloat(mat) && all(mod(mat(:),1) == 0))) - if(~isvector(mat) && isnest==1) - txt=cell2ubjson('',num2cell(mat,1),level,varargin{:}); - elseif(~ismsgpack || size(mat,1)==1) - if(0 && varargin{1}.keeptype) - itype=class(mat); - idx=find(ismember(varargin{1}.IType_,itype)); - if(isempty(idx)) - idx=find(ismember(varargin{1}.IType_,itype(2:end))); - end - type=Imarker(idx); - varargin{1}.inttype_=idx; - elseif(~any(mat<0)) - cid=varargin{1}.IType_; - type=Imarker(end); - maxdata=max(double(mat(:))); - for i=1:length(cid) - if(maxdata==cast(maxdata,cid{i})) - type=Imarker(i); - break; - end - end - else - cid=varargin{1}.IType_; - type=Imarker(end); - mindata=min(double(mat(:))); - maxdata=max(double(mat(:))); - for i=1:length(cid) - if(maxdata==cast(maxdata,cid{i}) && mindata==cast(mindata,cid{i})) - type=Imarker(i); - break; - end - end - end - if(numel(mat)==1) - if(mat<0) - txt=I_(int64(mat),varargin{:}); - else - txt=I_(uint64(mat),varargin{:}); - end - else - txt=I_a(mat(:),type,size(mat),varargin{:}); - end - else - txt=cell2ubjson('',num2cell(mat,2),level,varargin{:}); - end -elseif(islogical(mat)) - logicalval=FTmarker; - if(numel(mat)==1) - txt=logicalval(mat+1); - else - if(~isvector(mat) && isnest==1) - txt=cell2ubjson('',num2cell(uint8(mat),1),level,varargin{:}); - else - txt=I_a(uint8(mat(:)),Imarker(1),size(mat),varargin{:}); - end - end -else - am0=Amarker{1}; - if(Amarker{1}~='[') - am0=char(145); - end - if(numel(mat)==1) - txt=[am0 D_(mat,varargin{:}) Amarker{2}]; - else - if(~isvector(mat) && isnest==1) - txt=cell2ubjson('',num2cell(mat,1),level,varargin{:}); - else - txt=D_a(mat(:),Fmarker(3),size(mat),varargin{:}); - end - end -end - -%%------------------------------------------------------------------------- -function val=N_(str,varargin) -ismsgpack=varargin{1}.messagepack; -if(~ismsgpack) - val=[I_(int32(length(str)),varargin{:}) str]; -else - val=S_(str,varargin{:}); -end -%%------------------------------------------------------------------------- -function val=S_(str,varargin) -ismsgpack=varargin{1}.messagepack; -Smarker=varargin{1}.SM_; -if(length(str)==1) - val=[Smarker(1) str]; -else - if(ismsgpack) - val=[Imsgpk_(length(str),218,160,varargin{:}) str]; - else - val=['S' I_(int32(length(str)),varargin{:}) str]; - end -end - -%%------------------------------------------------------------------------- -function val=Imsgpk_(num,base1,base0,varargin) -if(num<16) - val=char(uint8(num)+uint8(base0)); - return; -end -val=I_(uint32(num),varargin{:}); -if(val(1)>char(210)) - num=uint32(num); - val=[char(210) data2byte(swapbytes(cast(num,'uint32')),'uint8')]; -elseif(val(1)',num)]; - else - val=[Imarker(varargin{1}.inttype_) data2byte(swapbytes(cast(num,cid{varargin{1}.inttype_})),'uint8')]; - end - return; -end -if(Imarker(1)~='U') - if(num>=0 && num<127) - val=uint8(num); - return; - end - if(num<0 && num>=-31) - val=typecast(int8(num), 'uint8'); - return; - end -end -if(Imarker(1)~='U' && num<0 && num<127) - val=data2byte((swapbytes(cast(num,'uint8')) & 127),'uint8'); - return; -end -key=Imarker; -for i=1:length(cid) - if(num==cast(num,cid{i})) - if(isdebug) - val=[key(i) sprintf('<%.0f>',num)]; - else - val=[key(i) data2byte(swapbytes(cast(num,cid{i})),'uint8')]; - end - return; - end -end -val=S_(sprintf('%.0f',num),varargin{:}); -if(Imarker(1)=='U') - val(1)='H'; -end -%%------------------------------------------------------------------------- -function val=D_(num, varargin) -if(~isfloat(num)) - error('input is not a float'); -end -isdebug=varargin{1}.debug; -if(isdebug) - output=sprintf('<%g>',num); -else - output=data2byte(swapbytes(num),'uint8'); -end -Fmarker=varargin{1}.FM_; - -if(isa(num,'half')) - val=[Fmarker(1) output(:)']; -elseif(isa(num,'single')) - val=[Fmarker(2) output(:)']; + ubj=saveubjson('',rootname,'ubjson',1); +elseif(length(varargin)==1 && ischar(varargin{1})) + ubj=saveubjson(rootname,obj,'FileName',varargin{1},'ubjson',1); else - val=[Fmarker(3) output(:)']; + ubj=saveubjson(rootname,obj,varargin{:},'ubjson',1); end -%%------------------------------------------------------------------------- -function data=I_a(num,type,dim,varargin) -if(isstruct(dim)) - varargin={dim}; -end -Imarker=varargin{1}.IM_; -Amarker=varargin{1}.AM_; - -if(Imarker(1)~='U' && type<=127) - type=char(204); -end -id=find(ismember(Imarker,type)); - -if(id==0) - error('unsupported integer array'); -end - -% based on UBJSON specs, all integer types are stored in big endian format - -cid=varargin{1}.IType_; -data=data2byte(swapbytes(cast(num,cid{id})),'uint8'); -blen=varargin{1}.IByte_(id); - - -isnest=varargin{1}.nestarray; -isdebug=varargin{1}.debug; -if(isdebug) - output=sprintf('<%g>',num); -else - output=data(:); -end - -if(isnest==0 && numel(num)>1 && Imarker(1)=='U') - if(nargin>=4 && ~isstruct(dim) && (length(dim)==1 || (length(dim)>=2 && prod(dim)~=dim(2)))) - cid=I_(uint32(max(dim)),varargin{:}); - data=['$' type '#' I_a(dim,cid(1),varargin{:}) output(:)']; - else - data=['$' type '#' I_(int32(numel(data)/blen),varargin{:}) output(:)']; - end - data=['[' data(:)']; -else - am0=Amarker{1}; - if(Imarker(1)~='U') - Amarker={char(hex2dec('dd')),''}; - am0=Imsgpk_(numel(num),220,144,varargin{:}); - end - if(isdebug) - data=sprintf([type '<%g>'],num); - else - data=reshape(data,blen,numel(data)/blen); - data(2:blen+1,:)=data; - data(1,:)=type; - end - data=[am0 data(:)' Amarker{2}]; -end -%%------------------------------------------------------------------------- -function data=D_a(num,type,dim,varargin) -Fmarker=varargin{1}.FM_; -Amarker=varargin{1}.AM_; - -id=find(ismember(Fmarker,type)); - -if(id==0) - error('unsupported float array'); -end - -data=data2byte(swapbytes(cast(num,varargin{1}.FType_{id})),'uint8'); -blen=varargin{1}.FByte_(id); - -isnest=varargin{1}.nestarray; -isdebug=varargin{1}.debug; -if(isdebug) - output=sprintf('<%g>',num); -else - output=data(:); -end - -if(isnest==0 && numel(num)>1 && Fmarker(end)=='D') - if(nargin>=4 && (length(dim)==1 || (length(dim)>=2 && prod(dim)~=dim(2)))) - cid=I_(uint32(max(dim)),varargin{:}); - data=['$' type '#' I_a(dim,cid(1),varargin{:}) output(:)']; - else - data=['$' type '#' I_(int32(numel(data)/blen),varargin{:}) output(:)']; - end - data=['[' data]; -else - am0=Amarker{1}; - if(Fmarker(end)~='D') - Amarker={char(hex2dec('dd')),''}; - am0=Imsgpk_(numel(num),220,144,varargin{:}); - end - if(isdebug) - data=sprintf([type '<%g>'],num); - else - data=reshape(data,blen,length(data)/blen); - data(2:(blen+1),:)=data; - data(1,:)=type; - end - data=[am0 data(:)' Amarker{2}]; -end - -%%------------------------------------------------------------------------- -function txt=any2ubjson(name,item,level,varargin) -st=containers.Map(); -st('_DataInfo_')=struct('MATLABObjectClass',class(item),'MATLABObjectSize',size(item)); -st('_ByteStream_')=getByteStreamFromArray(item); - -if(isempty(name)) - txt=map2ubjson(name,st,level,varargin{:}); -else - temp=struct(name,struct()); - temp.(name)=st; - txt=map2ubjson(name,temp.(name),level,varargin{:}); -end - -%%------------------------------------------------------------------------- -function bytes=data2byte(varargin) -bytes=typecast(varargin{:}); -bytes=char(bytes(:)'); diff --git a/test/run_save_test.m b/test/run_jsonlab_test.m similarity index 90% rename from test/run_save_test.m rename to test/run_jsonlab_test.m index dd13ff8..34e3c88 100644 --- a/test/run_save_test.m +++ b/test/run_jsonlab_test.m @@ -1,4 +1,4 @@ -function run_save_test(testname,fhandle,input,expected,varargin) +function run_jsonlab_test(testname,fhandle,input,expected,varargin) res=fhandle('',input,varargin{:}); if(~isequal(strtrim(res),expected)) warning('%s: failed: expected ''%s'', obtained ''%s''',testname,expected,res); @@ -8,7 +8,7 @@ function run_save_test(testname,fhandle,input,expected,varargin) handleinfo=functions(fhandle); loadfunname=regexprep(handleinfo.function,'^save','load'); loadfun=str2func(loadfunname); - if(strcmp(loadfunname,'loadubjson')) + if(strcmp(loadfunname,'loadbj')) newres=loadfun(fhandle('',input,varargin{:},'debug',0),varargin{:}); else newres=loadfun(res,varargin{:}); diff --git a/test/testjsonlab.m b/test/testjsonlab.m index 67f044b..47f58f1 100644 --- a/test/testjsonlab.m +++ b/test/testjsonlab.m @@ -1,69 +1,93 @@ function testjsonlab(tests) +% +% testjsonlab +% or +% testjsonlab(tests) +% testjsonlab({'js','jso','bj','bjo'}) +% +% Unit testing for JSONLab JSON, BJData/UBJSON encoders and decoders +% +% authors:Qianqian Fang (q.fang neu.edu) +% date: 2020/06/08 +% +% input: +% tests: is a cell array of strings, possible elements include +% 'js': test savejson/loadjson +% 'jso': test savejson/loadjson special options +% 'bj': test savebj/loadbj +% 'bjo': test savebj/loadbj special options +% +% license: +% BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details +% +% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% if(nargin==0) tests={'js','jso','bj','bjo'}; end + %% if(ismember('js',tests)) fprintf(sprintf('%s\n',char(ones(1,79)*61))); fprintf('Test JSON functions\n'); fprintf(sprintf('%s\n',char(ones(1,79)*61))); - run_save_test('single integer',@savejson,5,'[5]'); - run_save_test('single float',@savejson,3.14,'[3.14]'); - run_save_test('nan',@savejson,nan,'["_NaN_"]'); - run_save_test('inf',@savejson,inf,'["_Inf_"]'); - run_save_test('-inf',@savejson,-inf,'["-_Inf_"]'); - run_save_test('large integer',@savejson,uint64(2^64),'[18446744073709551616]'); - run_save_test('large negative integer',@savejson,int64(-2^63),'[-9223372036854775808]'); - run_save_test('boolean as 01',@savejson,[true,false],'[1,0]','compact',1); - run_save_test('empty array',@savejson,[],'[]'); - run_save_test('empty cell',@savejson,{},'[]'); - run_save_test('empty string',@savejson,'','""','compact',1); - run_save_test('string escape',@savejson,sprintf('jdata\n\b\awill\tprevail\t"\"\\'),'"jdata\n\b\awill\tprevail\t\"\"\\"'); + run_jsonlab_test('single integer',@savejson,5,'[5]'); + run_jsonlab_test('single float',@savejson,3.14,'[3.14]'); + run_jsonlab_test('nan',@savejson,nan,'["_NaN_"]'); + run_jsonlab_test('inf',@savejson,inf,'["_Inf_"]'); + run_jsonlab_test('-inf',@savejson,-inf,'["-_Inf_"]'); + run_jsonlab_test('large integer',@savejson,uint64(2^64),'[18446744073709551616]'); + run_jsonlab_test('large negative integer',@savejson,int64(-2^63),'[-9223372036854775808]'); + run_jsonlab_test('boolean as 01',@savejson,[true,false],'[1,0]','compact',1); + run_jsonlab_test('empty array',@savejson,[],'[]'); + run_jsonlab_test('empty cell',@savejson,{},'[]'); + run_jsonlab_test('empty string',@savejson,'','""','compact',1); + run_jsonlab_test('string escape',@savejson,sprintf('jdata\n\b\awill\tprevail\t"\"\\'),'"jdata\n\b\awill\tprevail\t\"\"\\"'); if(exist('isstring')) - run_save_test('string type',@savejson,string(sprintf('jdata\n\b\awill\tprevail')),'"jdata\n\b\awill\tprevail"','compact',1); - run_save_test('string array',@savejson,[string('jdata');string('will');string('prevail')],'["jdata","will","prevail"]','compact',1); + run_jsonlab_test('string type',@savejson,string(sprintf('jdata\n\b\awill\tprevail')),'"jdata\n\b\awill\tprevail"','compact',1); + run_jsonlab_test('string array',@savejson,[string('jdata');string('will');string('prevail')],'["jdata","will","prevail"]','compact',1); end - run_save_test('row vector',@savejson,[1,2,3],'[1,2,3]'); - run_save_test('column vector',@savejson,[1;2;3],'[[1],[2],[3]]','compact',1); - run_save_test('mixed array',@savejson,{'a',1,0.9},'["a",1,0.9]','compact',1); - run_save_test('char array',@savejson,['AC';'EG'],'["AC","EG"]','compact',1); - run_save_test('maps',@savejson,struct('a',1,'b','test'),'{"a":1,"b":"test"}','compact',1); - run_save_test('2d array',@savejson,[1,2,3;4,5,6],'[[1,2,3],[4,5,6]]','compact',1); - run_save_test('3d (row-major) nested array',@savejson,reshape(1:(2*3*2),2,3,2),... + run_jsonlab_test('row vector',@savejson,[1,2,3],'[1,2,3]'); + run_jsonlab_test('column vector',@savejson,[1;2;3],'[[1],[2],[3]]','compact',1); + run_jsonlab_test('mixed array',@savejson,{'a',1,0.9},'["a",1,0.9]','compact',1); + run_jsonlab_test('char array',@savejson,['AC';'EG'],'["AC","EG"]','compact',1); + run_jsonlab_test('maps',@savejson,struct('a',1,'b','test'),'{"a":1,"b":"test"}','compact',1); + run_jsonlab_test('2d array',@savejson,[1,2,3;4,5,6],'[[1,2,3],[4,5,6]]','compact',1); + run_jsonlab_test('3d (row-major) nested array',@savejson,reshape(1:(2*3*2),2,3,2),... '[[[1,7],[3,9],[5,11]],[[2,8],[4,10],[6,12]]]','compact',1,'nestarray',1); - run_save_test('3d (column-major) nested array',@savejson,reshape(1:(2*3*2),2,3,2),... + run_jsonlab_test('3d (column-major) nested array',@savejson,reshape(1:(2*3*2),2,3,2),... '[[[1,2],[3,4],[5,6]],[[7,8],[9,10],[11,12]]]','compact',1,'nestarray',1,'formatversion',1.9); - run_save_test('3d annotated array',@savejson,reshape(int8(1:(2*3*2)),2,3,2),... + run_jsonlab_test('3d annotated array',@savejson,reshape(int8(1:(2*3*2)),2,3,2),... '{"_ArrayType_":"int8","_ArraySize_":[2,3,2],"_ArrayData_":[1,7,3,9,5,11,2,8,4,10,6,12]}','compact',1); - run_save_test('complex number',@savejson,single(2+4i),... + run_jsonlab_test('complex number',@savejson,single(2+4i),... '{"_ArrayType_":"single","_ArraySize_":[1,1],"_ArrayIsComplex_":true,"_ArrayData_":[[2],[4]]}','compact',1); - run_save_test('empty sparse matrix',@savejson,sparse(2,3),... + run_jsonlab_test('empty sparse matrix',@savejson,sparse(2,3),... '{"_ArrayType_":"double","_ArraySize_":[2,3],"_ArrayIsSparse_":true,"_ArrayData_":[]}','compact',1); - run_save_test('real sparse matrix',@savejson,sparse([0,3,0,1,4]'),... + run_jsonlab_test('real sparse matrix',@savejson,sparse([0,3,0,1,4]'),... '{"_ArrayType_":"double","_ArraySize_":[5,1],"_ArrayIsSparse_":true,"_ArrayData_":[[2,4,5],[3,1,4]]}','compact',1); - run_save_test('complex sparse matrix',@savejson,sparse([0.0,3i,0.0,1,4i]'),... + run_jsonlab_test('complex sparse matrix',@savejson,sparse([0,3i,0,1.0,4i]'),... '{"_ArrayType_":"double","_ArraySize_":[5,1],"_ArrayIsComplex_":true,"_ArrayIsSparse_":true,"_ArrayData_":[[2,4,5],[0,1,0],[-3,0,-4]]}','compact',1); - run_save_test('heterogeneous cell',@savejson,{{1,{2,3}},{4,5},{6};{7},{8,9},{10}},... + run_jsonlab_test('heterogeneous cell',@savejson,{{1,{2,3}},{4,5},{6};{7},{8,9},{10}},... '[[[1,[2,3]],[4,5],[6]],[[7],[8,9],[10]]]','compact',1); - run_save_test('struct array',@savejson,repmat(struct('i',1.1,'d','str'),[1,2]),... + run_jsonlab_test('struct array',@savejson,repmat(struct('i',1.1,'d','str'),[1,2]),... '[{"i":1.1,"d":"str"},{"i":1.1,"d":"str"}]','compact',1); - run_save_test('encoded fieldnames',@savejson,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... + run_jsonlab_test('encoded fieldnames',@savejson,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... '{"_i":1,"i_":"str"}','compact',1); if(exist('OCTAVE_VERSION','builtin')~=0) - run_save_test('encoded fieldnames without decoding',@savejson,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... + run_jsonlab_test('encoded fieldnames without decoding',@savejson,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... '{"_i":1,"i_":"str"}','compact',1,'UnpackHex',0); else - run_save_test('encoded fieldnames without decoding',@savejson,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... + run_jsonlab_test('encoded fieldnames without decoding',@savejson,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... '{"x0x5F_i":1,"i_":"str"}','compact',1,'UnpackHex',0); end if(exist('containers.Map')) - run_save_test('containers.Map',@savejson,containers.Map({'Andy','^_^'},{true,'-_-'}),... + run_jsonlab_test('containers.Map',@savejson,containers.Map({'Andy','^_^'},{true,'-_-'}),... '{"Andy":true,"^_^":"-_-"}','compact',1,'usemap',1); end if(exist('istable')) - run_save_test('simple table',@savejson,table({'Andy','^_^'},{true,'-_-'}),... + run_jsonlab_test('simple table',@savejson,table({'Andy','^_^'},{true,'-_-'}),... '{"_TableCols_":["Var1","Var2"],"_TableRows_":[],"_TableRecords_":[["Andy","^_^"],[true,"-_-"]]}','compact',1); end if(exist('bandwidth')) @@ -72,35 +96,35 @@ function testjsonlab(tests) a=double(full(spdiags(true(4,lband+uband+1),-uband:lband,3,4))); a(a~=0)=find(a); - run_save_test('lower band matrix',@savejson,tril(a),... + run_jsonlab_test('lower band matrix',@savejson,tril(a),... '{"_ArrayType_":"double","_ArraySize_":[3,4],"_ArrayZipSize_":[2,3],"_ArrayShape_":["lowerband",1],"_ArrayData_":[1,5,9,0,2,6]}','compact',1,'usearrayshape',1); - run_save_test('upper band matrix',@savejson,triu(a),... + run_jsonlab_test('upper band matrix',@savejson,triu(a),... '{"_ArrayType_":"double","_ArraySize_":[3,4],"_ArrayZipSize_":[3,3],"_ArrayShape_":["upperband",2],"_ArrayData_":[7,11,0,4,8,12,1,5,9]}','compact',1,'usearrayshape',1); - run_save_test('diag matrix',@savejson,tril(triu(a)),... + run_jsonlab_test('diag matrix',@savejson,tril(triu(a)),... '{"_ArrayType_":"double","_ArraySize_":[3,4],"_ArrayShape_":"diag","_ArrayData_":[1,5,9]}','compact',1,'usearrayshape',1); - run_save_test('band matrix',@savejson,a,... + run_jsonlab_test('band matrix',@savejson,a,... '{"_ArrayType_":"double","_ArraySize_":[3,4],"_ArrayZipSize_":[4,3],"_ArrayShape_":["band",2,1],"_ArrayData_":[7,11,0,4,8,12,1,5,9,0,2,6]}','compact',1,'usearrayshape',1); a=a(:,1:3); a=uint8(tril(a)+tril(a)'); - run_save_test('symmetric band matrix',@savejson,a,... + run_jsonlab_test('symmetric band matrix',@savejson,a,... '{"_ArrayType_":"uint8","_ArraySize_":[3,3],"_ArrayZipSize_":[2,3],"_ArrayShape_":["lowersymmband",1],"_ArrayData_":[2,10,18,0,2,6]}','compact',1,'usearrayshape',1); a(a==0)=1; - run_save_test('lower triangular matrix',@savejson,tril(a),... + run_jsonlab_test('lower triangular matrix',@savejson,tril(a),... '{"_ArrayType_":"uint8","_ArraySize_":[3,3],"_ArrayShape_":"lower","_ArrayData_":[2,2,10,1,6,18]}','compact',1,'usearrayshape',1); - run_save_test('upper triangular matrix',@savejson,triu(a),... + run_jsonlab_test('upper triangular matrix',@savejson,triu(a),... '{"_ArrayType_":"uint8","_ArraySize_":[3,3],"_ArrayShape_":"upper","_ArrayData_":[2,2,1,10,6,18]}','compact',1,'usearrayshape',1); end try val=zlibencode('test'); a=uint8(eye(5)); a(20,1)=1; - run_save_test('zlib/zip compression (level 6)',@savejson,a,... + run_jsonlab_test('zlib/zip compression (level 6)',@savejson,a,... sprintf('{"_ArrayType_":"uint8","_ArraySize_":[20,5],"_ArrayZipSize_":[1,100],"_ArrayZipType_":"zlib","_ArrayZipData_":"eJxjZAABRhwkxQBsDAACIQAH\n"}'),... 'compact',1, 'Compression','zlib','CompressArraySize',0) % nestarray for 4-D or above is not working - run_save_test('gzip compression (level 6)',@savejson,a,... + run_jsonlab_test('gzip compression (level 6)',@savejson,a,... sprintf('{"_ArrayType_":"uint8","_ArraySize_":[20,5],"_ArrayZipSize_":[1,100],"_ArrayZipType_":"gzip","_ArrayZipData_":"H4sIAAAAAAAAA2NkAAFGHCTFAGwMAF9Xq6VkAAAA\n"}'),... 'compact',1, 'Compression','gzip','CompressArraySize',0) % nestarray for 4-D or above is not working - run_save_test('lzma compression (level 5)',@savejson,a,... + run_jsonlab_test('lzma compression (level 5)',@savejson,a,... sprintf('{"_ArrayType_":"uint8","_ArraySize_":[20,5],"_ArrayZipSize_":[1,100],"_ArrayZipType_":"lzma","_ArrayZipData_":"XQAAEABkAAAAAAAAAAAAgD1IirvlZSEY7DH///taoAA=\n"}'),... 'compact',1, 'Compression','lzma','CompressArraySize',0) % nestarray for 4-D or above is not working catch @@ -112,9 +136,9 @@ function testjsonlab(tests) fprintf('Test JSON function options\n'); fprintf(sprintf('%s\n',char(ones(1,79)*61))); - run_save_test('boolean',@savejson,[true,false],'[true,false]','compact',1,'ParseLogical',1); - run_save_test('nan option',@savejson,nan,'["_nan_"]','NaN','"_nan_"'); - run_save_test('inf option',@savejson,-inf,'["-inf"]','Inf','"$1inf"'); + run_jsonlab_test('boolean',@savejson,[true,false],'[true,false]','compact',1,'ParseLogical',1); + run_jsonlab_test('nan option',@savejson,nan,'["_nan_"]','NaN','"_nan_"'); + run_jsonlab_test('inf option',@savejson,-inf,'["-inf"]','Inf','"$1inf"'); end @@ -124,69 +148,69 @@ function testjsonlab(tests) fprintf('Test Binary JSON functions\n'); fprintf(sprintf('%s\n',char(ones(1,79)*61))); - run_save_test('uint8 integer',@saveubjson,2^8-1,'U<255>','debug',1); - run_save_test('uint16 integer',@saveubjson,2^8,'u<256>','debug',1); - run_save_test('int8 integer',@saveubjson,-2^7,'i<-128>','debug',1); - run_save_test('int16 integer',@saveubjson,-2^7-1,'I<-129>','debug',1); - run_save_test('int32 integer',@saveubjson,-2^15-1,'l<-32769>','debug',1); - run_save_test('uint16 integer',@saveubjson,2^16-1,'u<65535>','debug',1); - run_save_test('uint32 integer',@saveubjson,2^16,'m<65536>','debug',1); - run_save_test('uint32 integer',@saveubjson,2^32-1,'m<4294967295>','debug',1); - run_save_test('int32 integer',@saveubjson,-2^31,'l<-2147483648>','debug',1); - run_save_test('single float',@saveubjson,3.14,'[D<3.14>]','debug',1); - run_save_test('nan',@saveubjson,nan,'[D]','debug',1); - run_save_test('inf',@saveubjson,inf,'[D]','debug',1); - run_save_test('-inf',@saveubjson,-inf,'[D<-Inf>]','debug',1); - run_save_test('uint64 integer',@saveubjson,uint64(2^64),'M<18446744073709551616>','debug',1); - run_save_test('int64 negative integer',@saveubjson,int64(-2^63),'L<-9223372036854775808>','debug',1); - run_save_test('boolean as 01',@saveubjson,[true,false],'[U<1>U<0>]','debug',1,'nestarray',1); - run_save_test('empty array',@saveubjson,[],'Z','debug',1); - run_save_test('empty cell',@saveubjson,{},'Z','debug',1); - run_save_test('empty string',@saveubjson,'','SU<0>','debug',1); - run_save_test('string escape',@saveubjson,sprintf('jdata\n\b\awill\tprevail\t"\"\\'),sprintf('SU<24>jdata\n\b\awill\tprevail\t\"\"\\'),'debug',1); + run_jsonlab_test('uint8 integer',@savebj,2^8-1,'U<255>','debug',1); + run_jsonlab_test('uint16 integer',@savebj,2^8,'u<256>','debug',1); + run_jsonlab_test('int8 integer',@savebj,-2^7,'i<-128>','debug',1); + run_jsonlab_test('int16 integer',@savebj,-2^7-1,'I<-129>','debug',1); + run_jsonlab_test('int32 integer',@savebj,-2^15-1,'l<-32769>','debug',1); + run_jsonlab_test('uint16 integer',@savebj,2^16-1,'u<65535>','debug',1); + run_jsonlab_test('uint32 integer',@savebj,2^16,'m<65536>','debug',1); + run_jsonlab_test('uint32 integer',@savebj,2^32-1,'m<4294967295>','debug',1); + run_jsonlab_test('int32 integer',@savebj,-2^31,'l<-2147483648>','debug',1); + run_jsonlab_test('single float',@savebj,3.14,'[D<3.14>]','debug',1); + run_jsonlab_test('nan',@savebj,nan,'[D]','debug',1); + run_jsonlab_test('inf',@savebj,inf,'[D]','debug',1); + run_jsonlab_test('-inf',@savebj,-inf,'[D<-Inf>]','debug',1); + run_jsonlab_test('uint64 integer',@savebj,uint64(2^64),'M<18446744073709551616>','debug',1); + run_jsonlab_test('int64 negative integer',@savebj,int64(-2^63),'L<-9223372036854775808>','debug',1); + run_jsonlab_test('boolean as 01',@savebj,[true,false],'[U<1>U<0>]','debug',1,'nestarray',1); + run_jsonlab_test('empty array',@savebj,[],'Z','debug',1); + run_jsonlab_test('empty cell',@savebj,{},'Z','debug',1); + run_jsonlab_test('empty string',@savebj,'','SU<0>','debug',1); + run_jsonlab_test('string escape',@savebj,sprintf('jdata\n\b\awill\tprevail\t"\"\\'),sprintf('SU<24>jdata\n\b\awill\tprevail\t\"\"\\'),'debug',1); if(exist('isstring')) - run_save_test('string type',@saveubjson,string(sprintf('jdata\n\b\awill\tprevail')),sprintf('[SU<20>jdata\n\b\awill\tprevail]'),'debug',1); - run_save_test('string array',@saveubjson,[string('jdata');string('will');string('prevail')],'[[SU<5>jdataSU<4>willSU<7>prevail]]','debug',1); + run_jsonlab_test('string type',@savebj,string(sprintf('jdata\n\b\awill\tprevail')),sprintf('[SU<20>jdata\n\b\awill\tprevail]'),'debug',1); + run_jsonlab_test('string array',@savebj,[string('jdata');string('will');string('prevail')],'[[SU<5>jdataSU<4>willSU<7>prevail]]','debug',1); end - run_save_test('row vector',@saveubjson,[1,2,3],'[$U#U<3><1><2><3>','debug',1); - run_save_test('column vector',@saveubjson,[1;2;3],'[$U#[$U#U<2><3><1><1><2><3>','debug',1); - run_save_test('mixed array',@saveubjson,{'a',1,0.9},'[CaU<1>[D<0.9>]]','debug',1); - run_save_test('char array',@saveubjson,['AC';'EG'],'[SU<2>ACSU<2>EG]','debug',1); - run_save_test('maps',@saveubjson,struct('a',1,'b','test'),'{U<1>aU<1>U<1>bSU<4>test}','debug',1); - run_save_test('2d array',@saveubjson,[1,2,3;4,5,6],'[$U#[$U#U<2><2><3><1><4><2><5><3><6>','debug',1); - run_save_test('3d (row-major) nested array',@saveubjson,reshape(1:(2*3*2),2,3,2),... + run_jsonlab_test('row vector',@savebj,[1,2,3],'[$U#U<3><1><2><3>','debug',1); + run_jsonlab_test('column vector',@savebj,[1;2;3],'[$U#[$U#U<2><3><1><1><2><3>','debug',1); + run_jsonlab_test('mixed array',@savebj,{'a',1,0.9},'[CaU<1>[D<0.9>]]','debug',1); + run_jsonlab_test('char array',@savebj,['AC';'EG'],'[SU<2>ACSU<2>EG]','debug',1); + run_jsonlab_test('maps',@savebj,struct('a',1,'b','test'),'{U<1>aU<1>U<1>bSU<4>test}','debug',1); + run_jsonlab_test('2d array',@savebj,[1,2,3;4,5,6],'[$U#[$U#U<2><2><3><1><4><2><5><3><6>','debug',1); + run_jsonlab_test('3d (row-major) nested array',@savebj,reshape(1:(2*3*2),2,3,2),... '[[[U<1>U<7>][U<3>U<9>][U<5>U<11>]][[U<2>U<8>][U<4>U<10>][U<6>U<12>]]]','debug',1,'nestarray',1); - run_save_test('3d (column-major) nested array',@saveubjson,reshape(1:(2*3*2),2,3,2),... + run_jsonlab_test('3d (column-major) nested array',@savebj,reshape(1:(2*3*2),2,3,2),... '[[[U<1>U<2>][U<3>U<4>][U<5>U<6>]][[U<7>U<8>][U<9>U<10>][U<11>U<12>]]]','debug',1,'nestarray',1,'formatversion',1.9); - run_save_test('3d annotated array',@saveubjson,reshape(int8(1:(2*3*2)),2,3,2),... + run_jsonlab_test('3d annotated array',@savebj,reshape(int8(1:(2*3*2)),2,3,2),... '{U<11>_ArrayType_SU<4>int8U<11>_ArraySize_[$U#U<3><2><3><2>U<11>_ArrayData_[$U#U<12><1><7><3><9><5><11><2><8><4><10><6><12>}','debug',1); - run_save_test('complex number',@saveubjson,single(2+4i),... + run_jsonlab_test('complex number',@savebj,single(2+4i),... '{U<11>_ArrayType_SU<6>singleU<11>_ArraySize_[$U#U<2><1><1>U<16>_ArrayIsComplex_TU<11>_ArrayData_[$U#[$U#U<2><2><1><2><4>}','debug',1); - run_save_test('empty sparse matrix',@saveubjson,sparse(2,3),... + run_jsonlab_test('empty sparse matrix',@savebj,sparse(2,3),... '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><2><3>U<15>_ArrayIsSparse_TU<11>_ArrayData_Z}','debug',1); - run_save_test('real sparse matrix',@saveubjson,sparse([0,3,0,1,4]'),... + run_jsonlab_test('real sparse matrix',@savebj,sparse([0,3,0,1,4]'),... '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><5><1>U<15>_ArrayIsSparse_TU<11>_ArrayData_[$U#[$U#U<2><2><3><2><3><4><1><5><4>}','debug',1); - run_save_test('complex sparse matrix',@saveubjson,sparse([0.0,3i,0.0,1,4i]'),... + run_jsonlab_test('complex sparse matrix',@savebj,sparse([0,3i,0,1.0,4i]'),... '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><5><1>U<16>_ArrayIsComplex_TU<15>_ArrayIsSparse_TU<11>_ArrayData_[$i#[$U#U<2><3><3><2><0><-3><4><1><0><5><0><-4>}','debug',1); - run_save_test('heterogeneous cell',@saveubjson,{{1,{2,3}},{4,5},{6};{7},{8,9},{10}},... + run_jsonlab_test('heterogeneous cell',@savebj,{{1,{2,3}},{4,5},{6};{7},{8,9},{10}},... '[[[U<1>[U<2>U<3>]][U<4>U<5>][U<6>]][[U<7>][U<8>U<9>][U<10>]]]','debug',1); - run_save_test('struct array',@saveubjson,repmat(struct('i',1.1,'d','str'),[1,2]),... + run_jsonlab_test('struct array',@savebj,repmat(struct('i',1.1,'d','str'),[1,2]),... '[{U<1>i[D<1.1>]U<1>dSU<3>str}{U<1>i[D<1.1>]U<1>dSU<3>str}]','debug',1); - run_save_test('encoded fieldnames',@saveubjson,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... + run_jsonlab_test('encoded fieldnames',@savebj,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... '{U<2>_iU<1>U<2>i_SU<3>str}','debug',1); if(exist('OCTAVE_VERSION','builtin')~=0) - run_save_test('encoded fieldnames without decoding',@saveubjson,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... + run_jsonlab_test('encoded fieldnames without decoding',@savebj,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... '{U<2>_iU<1>U<2>i_SU<3>str}','debug',1,'UnpackHex',0); else - run_save_test('encoded fieldnames without decoding',@saveubjson,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... + run_jsonlab_test('encoded fieldnames without decoding',@savebj,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... '{U<7>x0x5F_iU<1>U<2>i_SU<3>str}','debug',1,'UnpackHex',0); end if(exist('containers.Map')) - run_save_test('containers.Map',@saveubjson,containers.Map({'Andy','^_^'},{true,'-_-'}),... + run_jsonlab_test('containers.Map',@savebj,containers.Map({'Andy','^_^'},{true,'-_-'}),... '{U<4>AndyTU<3>^_^SU<3>-_-}','debug',1,'usemap',1); end if(exist('istable')) - run_save_test('simple table',@saveubjson,table({'Andy','^_^'},{true,'-_-'}),... + run_jsonlab_test('simple table',@savebj,table({'Andy','^_^'},{true,'-_-'}),... '{U<11>_TableCols_[SU<4>Var1SU<4>Var2]U<11>_TableRows_ZU<14>_TableRecords_[[SU<4>AndySU<3>^_^][TSU<3>-_-]]}','debug',1); end if(exist('bandwidth')) @@ -195,35 +219,35 @@ function testjsonlab(tests) a=double(full(spdiags(true(4,lband+uband+1),-uband:lband,3,4))); a(a~=0)=find(a); - run_save_test('lower band matrix',@saveubjson,tril(a),... + run_jsonlab_test('lower band matrix',@savebj,tril(a),... '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><3><4>U<14>_ArrayZipSize_[$U#U<2><2><3>U<12>_ArrayShape_[SU<9>lowerbandU<1>]U<11>_ArrayData_[$U#U<6><1><5><9><0><2><6>}','debug',1,'usearrayshape',1); - run_save_test('upper band matrix',@saveubjson,triu(a),... + run_jsonlab_test('upper band matrix',@savebj,triu(a),... '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><3><4>U<14>_ArrayZipSize_[$U#U<2><3><3>U<12>_ArrayShape_[SU<9>upperbandU<2>]U<11>_ArrayData_[$U#U<9><7><11><0><4><8><12><1><5><9>}','debug',1,'usearrayshape',1); - run_save_test('diag matrix',@saveubjson,tril(triu(a)),... + run_jsonlab_test('diag matrix',@savebj,tril(triu(a)),... '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><3><4>U<12>_ArrayShape_SU<4>diagU<11>_ArrayData_[$U#U<3><1><5><9>}','debug',1,'usearrayshape',1); - run_save_test('band matrix',@saveubjson,a,... + run_jsonlab_test('band matrix',@savebj,a,... '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><3><4>U<14>_ArrayZipSize_[$U#U<2><4><3>U<12>_ArrayShape_[SU<4>bandU<2>U<1>]U<11>_ArrayData_[$U#U<12><7><11><0><4><8><12><1><5><9><0><2><6>}','debug',1,'usearrayshape',1); a=a(:,1:3); a=uint8(tril(a)+tril(a)'); - run_save_test('symmetric band matrix',@saveubjson,a,... + run_jsonlab_test('symmetric band matrix',@savebj,a,... '{U<11>_ArrayType_SU<5>uint8U<11>_ArraySize_[$U#U<2><3><3>U<14>_ArrayZipSize_[$U#U<2><2><3>U<12>_ArrayShape_[SU<13>lowersymmbandU<1>]U<11>_ArrayData_[$U#U<6><2><10><18><0><2><6>}','debug',1,'usearrayshape',1); a(a==0)=1; - run_save_test('lower triangular matrix',@saveubjson,tril(a),... + run_jsonlab_test('lower triangular matrix',@savebj,tril(a),... '{U<11>_ArrayType_SU<5>uint8U<11>_ArraySize_[$U#U<2><3><3>U<12>_ArrayShape_SU<5>lowerU<11>_ArrayData_[$U#U<6><2><2><10><1><6><18>}','debug',1,'usearrayshape',1); - run_save_test('upper triangular matrix',@saveubjson,triu(a),... + run_jsonlab_test('upper triangular matrix',@savebj,triu(a),... '{U<11>_ArrayType_SU<5>uint8U<11>_ArraySize_[$U#U<2><3><3>U<12>_ArrayShape_SU<5>upperU<11>_ArrayData_[$U#U<6><2><2><1><10><6><18>}','debug',1,'usearrayshape',1); end try val=zlibencode('test'); a=uint8(eye(5)); a(20,1)=1; - run_save_test('zlib/zip compression (level 6)',@saveubjson,a,... + run_jsonlab_test('zlib/zip compression (level 6)',@savebj,a,... '{U<11>_ArrayType_SU<5>uint8U<11>_ArraySize_[$U#U<2><20><5>U<14>_ArrayZipSize_[$U#U<2><1><100>U<14>_ArrayZipType_SU<4>zlibU<14>_ArrayZipData_[$U#U<18><120><156><99><100><0><1><70><28><36><197><0><108><12><0><2><33><0><7>}',... 'debug',1, 'Compression','zlib','CompressArraySize',0) % nestarray for 4-D or above is not working - run_save_test('gzip compression (level 6)',@saveubjson,a,... + run_jsonlab_test('gzip compression (level 6)',@savebj,a,... '{U<11>_ArrayType_SU<5>uint8U<11>_ArraySize_[$U#U<2><20><5>U<14>_ArrayZipSize_[$U#U<2><1><100>U<14>_ArrayZipType_SU<4>gzipU<14>_ArrayZipData_[$U#U<30><31><139><8><0><0><0><0><0><0><3><99><100><0><1><70><28><36><197><0><108><12><0><95><87><171><165><100><0><0><0>}',... 'debug',1, 'Compression','gzip','CompressArraySize',0) % nestarray for 4-D or above is not working - run_save_test('lzma compression (level 5)',@saveubjson,a,... + run_jsonlab_test('lzma compression (level 5)',@savebj,a,... '{U<11>_ArrayType_SU<5>uint8U<11>_ArraySize_[$U#U<2><20><5>U<14>_ArrayZipSize_[$U#U<2><1><100>U<14>_ArrayZipType_SU<4>lzmaU<14>_ArrayZipData_[$U#U<32><93><0><0><16><0><100><0><0><0><0><0><0><0><0><0><128><61><72><138><187><229><101><33><24><236><49><255><255><251><90><160><0>}',... 'debug',1, 'Compression','lzma','CompressArraySize',0) % nestarray for 4-D or above is not working catch @@ -236,9 +260,9 @@ function testjsonlab(tests) fprintf('Test JSON function options\n'); fprintf(sprintf('%s\n',char(ones(1,79)*61))); - run_save_test('row vector',@saveubjson,[1,2,3],'[$U#U<3><1><2><3>','debug',1); - run_save_test('single integer',@saveubjson,256,'I<256>','debug',1,'ubjson',1); - run_save_test('single integer',@saveubjson,2^32-1,'L<4294967295>','debug',1,'ubjson',1); - run_save_test('single integer',@saveubjson,2^64-1,'HU<20>18446744073709551616','debug',1,'ubjson',1); - run_save_test('inf option',@savejson,-inf,'["-inf"]','Inf','"$1inf"'); + run_jsonlab_test('row vector',@savebj,[1,2,3],'[$U#U<3><1><2><3>','debug',1); + run_jsonlab_test('single integer',@savebj,256,'I<256>','debug',1,'ubjson',1); + run_jsonlab_test('single integer',@savebj,2^32-1,'L<4294967295>','debug',1,'ubjson',1); + run_jsonlab_test('single integer',@savebj,2^64-1,'HU<20>18446744073709551616','debug',1,'ubjson',1); + run_jsonlab_test('inf option',@savejson,-inf,'["-inf"]','Inf','"$1inf"'); end \ No newline at end of file