Path: blob/master/Week 9/Programming Assignment - 8/ex8/lib/jsonlab/saveubjson.m
626 views
function json=saveubjson(rootname,obj,varargin)1%2% json=saveubjson(rootname,obj,filename)3% or4% json=saveubjson(rootname,obj,opt)5% json=saveubjson(rootname,obj,'param1',value1,'param2',value2,...)6%7% convert a MATLAB object (cell, struct or array) into a Universal8% Binary JSON (UBJSON) binary string9%10% author: Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)11% created on 2013/08/1712%13% $Id: saveubjson.m 460 2015-01-03 00:30:45Z fangq $14%15% input:16% rootname: the name of the root-object, when set to '', the root name17% is ignored, however, when opt.ForceRootName is set to 1 (see below),18% the MATLAB variable name will be used as the root name.19% obj: a MATLAB object (array, cell, cell array, struct, struct array)20% filename: a string for the file name to save the output UBJSON data21% opt: a struct for additional options, ignore to use default values.22% opt can have the following fields (first in [.|.] is the default)23%24% opt.FileName [''|string]: a file name to save the output JSON data25% opt.ArrayToStruct[0|1]: when set to 0, saveubjson outputs 1D/2D26% array in JSON array format; if sets to 1, an27% array will be shown as a struct with fields28% "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for29% sparse arrays, the non-zero elements will be30% saved to _ArrayData_ field in triplet-format i.e.31% (ix,iy,val) and "_ArrayIsSparse_" will be added32% with a value of 1; for a complex array, the33% _ArrayData_ array will include two columns34% (4 for sparse) to record the real and imaginary35% parts, and also "_ArrayIsComplex_":1 is added.36% opt.ParseLogical [1|0]: if this is set to 1, logical array elem37% will use true/false rather than 1/0.38% opt.NoRowBracket [1|0]: if this is set to 1, arrays with a single39% numerical element will be shown without a square40% bracket, unless it is the root object; if 0, square41% brackets are forced for any numerical arrays.42% opt.ForceRootName [0|1]: when set to 1 and rootname is empty, saveubjson43% will use the name of the passed obj variable as the44% root object name; if obj is an expression and45% does not have a name, 'root' will be used; if this46% is set to 0 and rootname is empty, the root level47% will be merged down to the lower level.48% opt.JSONP [''|string]: to generate a JSONP output (JSON with padding),49% for example, if opt.JSON='foo', the JSON data is50% wrapped inside a function call as 'foo(...);'51% opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson52% back to the string form53%54% opt can be replaced by a list of ('param',value) pairs. The param55% string is equivallent to a field in opt and is case sensitive.56% output:57% json: a binary string in the UBJSON format (see http://ubjson.org)58%59% examples:60% jsonmesh=struct('MeshNode',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],...61% 'MeshTetra',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],...62% 'MeshTri',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;...63% 2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],...64% 'MeshCreator','FangQ','MeshTitle','T6 Cube',...65% 'SpecialData',[nan, inf, -inf]);66% saveubjson('jsonmesh',jsonmesh)67% saveubjson('jsonmesh',jsonmesh,'meshdata.ubj')68%69% license:70% BSD, see LICENSE_BSD.txt files for details71%72% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)73%7475if(nargin==1)76varname=inputname(1);77obj=rootname;78if(isempty(varname))79varname='root';80end81rootname=varname;82else83varname=inputname(2);84end85if(length(varargin)==1 && ischar(varargin{1}))86opt=struct('FileName',varargin{1});87else88opt=varargin2struct(varargin{:});89end90opt.IsOctave=exist('OCTAVE_VERSION','builtin');91rootisarray=0;92rootlevel=1;93forceroot=jsonopt('ForceRootName',0,opt);94if((isnumeric(obj) || islogical(obj) || ischar(obj) || isstruct(obj) || iscell(obj)) && isempty(rootname) && forceroot==0)95rootisarray=1;96rootlevel=0;97else98if(isempty(rootname))99rootname=varname;100end101end102if((isstruct(obj) || iscell(obj))&& isempty(rootname) && forceroot)103rootname='root';104end105json=obj2ubjson(rootname,obj,rootlevel,opt);106if(~rootisarray)107json=['{' json '}'];108end109110jsonp=jsonopt('JSONP','',opt);111if(~isempty(jsonp))112json=[jsonp '(' json ')'];113end114115% save to a file if FileName is set, suggested by Patrick Rapin116if(~isempty(jsonopt('FileName','',opt)))117fid = fopen(opt.FileName, 'wb');118fwrite(fid,json);119fclose(fid);120end121122%%-------------------------------------------------------------------------123function txt=obj2ubjson(name,item,level,varargin)124125if(iscell(item))126txt=cell2ubjson(name,item,level,varargin{:});127elseif(isstruct(item))128txt=struct2ubjson(name,item,level,varargin{:});129elseif(ischar(item))130txt=str2ubjson(name,item,level,varargin{:});131else132txt=mat2ubjson(name,item,level,varargin{:});133end134135%%-------------------------------------------------------------------------136function txt=cell2ubjson(name,item,level,varargin)137txt='';138if(~iscell(item))139error('input is not a cell');140end141142dim=size(item);143if(ndims(squeeze(item))>2) % for 3D or higher dimensions, flatten to 2D for now144item=reshape(item,dim(1),numel(item)/dim(1));145dim=size(item);146end147len=numel(item); % let's handle 1D cell first148if(len>1)149if(~isempty(name))150txt=[S_(checkname(name,varargin{:})) '[']; name='';151else152txt='[';153end154elseif(len==0)155if(~isempty(name))156txt=[S_(checkname(name,varargin{:})) 'Z']; name='';157else158txt='Z';159end160end161for j=1:dim(2)162if(dim(1)>1) txt=[txt '[']; end163for i=1:dim(1)164txt=[txt obj2ubjson(name,item{i,j},level+(len>1),varargin{:})];165end166if(dim(1)>1) txt=[txt ']']; end167end168if(len>1) txt=[txt ']']; end169170%%-------------------------------------------------------------------------171function txt=struct2ubjson(name,item,level,varargin)172txt='';173if(~isstruct(item))174error('input is not a struct');175end176dim=size(item);177if(ndims(squeeze(item))>2) % for 3D or higher dimensions, flatten to 2D for now178item=reshape(item,dim(1),numel(item)/dim(1));179dim=size(item);180end181len=numel(item);182183if(~isempty(name))184if(len>1) txt=[S_(checkname(name,varargin{:})) '[']; end185else186if(len>1) txt='['; end187end188for j=1:dim(2)189if(dim(1)>1) txt=[txt '[']; end190for i=1:dim(1)191names = fieldnames(item(i,j));192if(~isempty(name) && len==1)193txt=[txt S_(checkname(name,varargin{:})) '{'];194else195txt=[txt '{'];196end197if(~isempty(names))198for e=1:length(names)199txt=[txt obj2ubjson(names{e},getfield(item(i,j),...200names{e}),level+(dim(1)>1)+1+(len>1),varargin{:})];201end202end203txt=[txt '}'];204end205if(dim(1)>1) txt=[txt ']']; end206end207if(len>1) txt=[txt ']']; end208209%%-------------------------------------------------------------------------210function txt=str2ubjson(name,item,level,varargin)211txt='';212if(~ischar(item))213error('input is not a string');214end215item=reshape(item, max(size(item),[1 0]));216len=size(item,1);217218if(~isempty(name))219if(len>1) txt=[S_(checkname(name,varargin{:})) '[']; end220else221if(len>1) txt='['; end222end223isoct=jsonopt('IsOctave',0,varargin{:});224for e=1:len225val=item(e,:);226if(len==1)227obj=['' S_(checkname(name,varargin{:})) '' '',S_(val),''];228if(isempty(name)) obj=['',S_(val),'']; end229txt=[txt,'',obj];230else231txt=[txt,'',['',S_(val),'']];232end233end234if(len>1) txt=[txt ']']; end235236%%-------------------------------------------------------------------------237function txt=mat2ubjson(name,item,level,varargin)238if(~isnumeric(item) && ~islogical(item))239error('input is not an array');240end241242if(length(size(item))>2 || issparse(item) || ~isreal(item) || ...243isempty(item) || jsonopt('ArrayToStruct',0,varargin{:}))244cid=I_(uint32(max(size(item))));245if(isempty(name))246txt=['{' S_('_ArrayType_'),S_(class(item)),S_('_ArraySize_'),I_a(size(item),cid(1)) ];247else248if(isempty(item))249txt=[S_(checkname(name,varargin{:})),'Z'];250return;251else252txt=[S_(checkname(name,varargin{:})),'{',S_('_ArrayType_'),S_(class(item)),S_('_ArraySize_'),I_a(size(item),cid(1))];253end254end255else256if(isempty(name))257txt=matdata2ubjson(item,level+1,varargin{:});258else259if(numel(item)==1 && jsonopt('NoRowBracket',1,varargin{:})==1)260numtxt=regexprep(regexprep(matdata2ubjson(item,level+1,varargin{:}),'^\[',''),']','');261txt=[S_(checkname(name,varargin{:})) numtxt];262else263txt=[S_(checkname(name,varargin{:})),matdata2ubjson(item,level+1,varargin{:})];264end265end266return;267end268if(issparse(item))269[ix,iy]=find(item);270data=full(item(find(item)));271if(~isreal(item))272data=[real(data(:)),imag(data(:))];273if(size(item,1)==1)274% Kludge to have data's 'transposedness' match item's.275% (Necessary for complex row vector handling below.)276data=data';277end278txt=[txt,S_('_ArrayIsComplex_'),'T'];279end280txt=[txt,S_('_ArrayIsSparse_'),'T'];281if(size(item,1)==1)282% Row vector, store only column indices.283txt=[txt,S_('_ArrayData_'),...284matdata2ubjson([iy(:),data'],level+2,varargin{:})];285elseif(size(item,2)==1)286% Column vector, store only row indices.287txt=[txt,S_('_ArrayData_'),...288matdata2ubjson([ix,data],level+2,varargin{:})];289else290% General case, store row and column indices.291txt=[txt,S_('_ArrayData_'),...292matdata2ubjson([ix,iy,data],level+2,varargin{:})];293end294else295if(isreal(item))296txt=[txt,S_('_ArrayData_'),...297matdata2ubjson(item(:)',level+2,varargin{:})];298else299txt=[txt,S_('_ArrayIsComplex_'),'T'];300txt=[txt,S_('_ArrayData_'),...301matdata2ubjson([real(item(:)) imag(item(:))],level+2,varargin{:})];302end303end304txt=[txt,'}'];305306%%-------------------------------------------------------------------------307function txt=matdata2ubjson(mat,level,varargin)308if(isempty(mat))309txt='Z';310return;311end312if(size(mat,1)==1)313level=level-1;314end315type='';316hasnegtive=(mat<0);317if(isa(mat,'integer') || isinteger(mat) || (isfloat(mat) && all(mod(mat(:),1) == 0)))318if(isempty(hasnegtive))319if(max(mat(:))<=2^8)320type='U';321end322end323if(isempty(type))324% todo - need to consider negative ones separately325id= histc(abs(max(mat(:))),[0 2^7 2^15 2^31 2^63]);326if(isempty(find(id)))327error('high-precision data is not yet supported');328end329key='iIlL';330type=key(find(id));331end332txt=[I_a(mat(:),type,size(mat))];333elseif(islogical(mat))334logicalval='FT';335if(numel(mat)==1)336txt=logicalval(mat+1);337else338txt=['[$U#' I_a(size(mat),'l') typecast(swapbytes(uint8(mat(:)')),'uint8')];339end340else341if(numel(mat)==1)342txt=['[' D_(mat) ']'];343else344txt=D_a(mat(:),'D',size(mat));345end346end347348%txt=regexprep(mat2str(mat),'\s+',',');349%txt=regexprep(txt,';',sprintf('],['));350% if(nargin>=2 && size(mat,1)>1)351% txt=regexprep(txt,'\[',[repmat(sprintf('\t'),1,level) '[']);352% end353if(any(isinf(mat(:))))354txt=regexprep(txt,'([-+]*)Inf',jsonopt('Inf','"$1_Inf_"',varargin{:}));355end356if(any(isnan(mat(:))))357txt=regexprep(txt,'NaN',jsonopt('NaN','"_NaN_"',varargin{:}));358end359360%%-------------------------------------------------------------------------361function newname=checkname(name,varargin)362isunpack=jsonopt('UnpackHex',1,varargin{:});363newname=name;364if(isempty(regexp(name,'0x([0-9a-fA-F]+)_','once')))365return366end367if(isunpack)368isoct=jsonopt('IsOctave',0,varargin{:});369if(~isoct)370newname=regexprep(name,'(^x|_){1}0x([0-9a-fA-F]+)_','${native2unicode(hex2dec($2))}');371else372pos=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','start');373pend=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','end');374if(isempty(pos)) return; end375str0=name;376pos0=[0 pend(:)' length(name)];377newname='';378for i=1:length(pos)379newname=[newname str0(pos0(i)+1:pos(i)-1) char(hex2dec(str0(pos(i)+3:pend(i)-1)))];380end381if(pos(end)~=length(name))382newname=[newname str0(pos0(end-1)+1:pos0(end))];383end384end385end386%%-------------------------------------------------------------------------387function val=S_(str)388if(length(str)==1)389val=['C' str];390else391val=['S' I_(int32(length(str))) str];392end393%%-------------------------------------------------------------------------394function val=I_(num)395if(~isinteger(num))396error('input is not an integer');397end398if(num>=0 && num<255)399val=['U' data2byte(swapbytes(cast(num,'uint8')),'uint8')];400return;401end402key='iIlL';403cid={'int8','int16','int32','int64'};404for i=1:4405if((num>0 && num<2^(i*8-1)) || (num<0 && num>=-2^(i*8-1)))406val=[key(i) data2byte(swapbytes(cast(num,cid{i})),'uint8')];407return;408end409end410error('unsupported integer');411412%%-------------------------------------------------------------------------413function val=D_(num)414if(~isfloat(num))415error('input is not a float');416end417418if(isa(num,'single'))419val=['d' data2byte(num,'uint8')];420else421val=['D' data2byte(num,'uint8')];422end423%%-------------------------------------------------------------------------424function data=I_a(num,type,dim,format)425id=find(ismember('iUIlL',type));426427if(id==0)428error('unsupported integer array');429end430431% based on UBJSON specs, all integer types are stored in big endian format432433if(id==1)434data=data2byte(swapbytes(int8(num)),'uint8');435blen=1;436elseif(id==2)437data=data2byte(swapbytes(uint8(num)),'uint8');438blen=1;439elseif(id==3)440data=data2byte(swapbytes(int16(num)),'uint8');441blen=2;442elseif(id==4)443data=data2byte(swapbytes(int32(num)),'uint8');444blen=4;445elseif(id==5)446data=data2byte(swapbytes(int64(num)),'uint8');447blen=8;448end449450if(nargin>=3 && length(dim)>=2 && prod(dim)~=dim(2))451format='opt';452end453if((nargin<4 || strcmp(format,'opt')) && numel(num)>1)454if(nargin>=3 && (length(dim)==1 || (length(dim)>=2 && prod(dim)~=dim(2))))455cid=I_(uint32(max(dim)));456data=['$' type '#' I_a(dim,cid(1)) data(:)'];457else458data=['$' type '#' I_(int32(numel(data)/blen)) data(:)'];459end460data=['[' data(:)'];461else462data=reshape(data,blen,numel(data)/blen);463data(2:blen+1,:)=data;464data(1,:)=type;465data=data(:)';466data=['[' data(:)' ']'];467end468%%-------------------------------------------------------------------------469function data=D_a(num,type,dim,format)470id=find(ismember('dD',type));471472if(id==0)473error('unsupported float array');474end475476if(id==1)477data=data2byte(single(num),'uint8');478elseif(id==2)479data=data2byte(double(num),'uint8');480end481482if(nargin>=3 && length(dim)>=2 && prod(dim)~=dim(2))483format='opt';484end485if((nargin<4 || strcmp(format,'opt')) && numel(num)>1)486if(nargin>=3 && (length(dim)==1 || (length(dim)>=2 && prod(dim)~=dim(2))))487cid=I_(uint32(max(dim)));488data=['$' type '#' I_a(dim,cid(1)) data(:)'];489else490data=['$' type '#' I_(int32(numel(data)/(id*4))) data(:)'];491end492data=['[' data];493else494data=reshape(data,(id*4),length(data)/(id*4));495data(2:(id*4+1),:)=data;496data(1,:)=type;497data=data(:)';498data=['[' data(:)' ']'];499end500%%-------------------------------------------------------------------------501function bytes=data2byte(varargin)502bytes=typecast(varargin{:});503bytes=bytes(:)';504505506