123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 |
- function data = loadubjson(fname,varargin)
- %
- % data=loadubjson(fname,opt)
- % or
- % data=loadubjson(fname,'param1',value1,'param2',value2,...)
- %
- % parse a JSON (JavaScript Object Notation) file or string
- %
- % authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
- % created on 2013/08/01
- %
- % $Id: loadubjson.m 460 2015-01-03 00:30:45Z fangq $
- %
- % input:
- % fname: input file name, if fname contains "{}" or "[]", fname
- % will be interpreted as a UBJSON string
- % opt: a struct to store parsing options, opt can be replaced by
- % a list of ('param',value) pairs - the param string is equivallent
- % to a field in opt. opt can have the following
- % fields (first in [.|.] is the default)
- %
- % opt.SimplifyCell [0|1]: if set to 1, loadubjson will call cell2mat
- % for each element of the JSON data, and group
- % arrays based on the cell2mat rules.
- % opt.IntEndian [B|L]: specify the endianness of the integer fields
- % in the UBJSON input data. B - Big-Endian format for
- % integers (as required in the UBJSON specification);
- % L - input integer fields are in Little-Endian order.
- %
- % output:
- % dat: a cell array, where {...} blocks are converted into cell arrays,
- % and [...] are converted to arrays
- %
- % examples:
- % obj=struct('string','value','array',[1 2 3]);
- % ubjdata=saveubjson('obj',obj);
- % dat=loadubjson(ubjdata)
- % dat=loadubjson(['examples' filesep 'example1.ubj'])
- % dat=loadubjson(['examples' filesep 'example1.ubj'],'SimplifyCell',1)
- %
- % license:
- % BSD, see LICENSE_BSD.txt files for details
- %
- % -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)
- %
- global pos inStr len esc index_esc len_esc isoct arraytoken fileendian systemendian
- if(regexp(fname,'[\{\}\]\[]','once'))
- string=fname;
- elseif(exist(fname,'file'))
- fid = fopen(fname,'rb');
- string = fread(fid,inf,'uint8=>char')';
- fclose(fid);
- else
- error('input file does not exist');
- end
- pos = 1; len = length(string); inStr = string;
- isoct=exist('OCTAVE_VERSION','builtin');
- arraytoken=find(inStr=='[' | inStr==']' | inStr=='"');
- jstr=regexprep(inStr,'\\\\',' ');
- escquote=regexp(jstr,'\\"');
- arraytoken=sort([arraytoken escquote]);
- % String delimiters and escape chars identified to improve speed:
- esc = find(inStr=='"' | inStr=='\' ); % comparable to: regexp(inStr, '["\\]');
- index_esc = 1; len_esc = length(esc);
- opt=varargin2struct(varargin{:});
- fileendian=upper(jsonopt('IntEndian','B',opt));
- [os,maxelem,systemendian]=computer;
- jsoncount=1;
- while pos <= len
- switch(next_char)
- case '{'
- data{jsoncount} = parse_object(opt);
- case '['
- data{jsoncount} = parse_array(opt);
- otherwise
- error_pos('Outer level structure must be an object or an array');
- end
- jsoncount=jsoncount+1;
- end % while
- jsoncount=length(data);
- if(jsoncount==1 && iscell(data))
- data=data{1};
- end
- if(~isempty(data))
- if(isstruct(data)) % data can be a struct array
- data=jstruct2array(data);
- elseif(iscell(data))
- data=jcell2array(data);
- end
- end
- %%
- function newdata=parse_collection(id,data,obj)
- if(jsoncount>0 && exist('data','var'))
- if(~iscell(data))
- newdata=cell(1);
- newdata{1}=data;
- data=newdata;
- end
- end
- %%
- function newdata=jcell2array(data)
- len=length(data);
- newdata=data;
- for i=1:len
- if(isstruct(data{i}))
- newdata{i}=jstruct2array(data{i});
- elseif(iscell(data{i}))
- newdata{i}=jcell2array(data{i});
- end
- end
- %%-------------------------------------------------------------------------
- function newdata=jstruct2array(data)
- fn=fieldnames(data);
- newdata=data;
- len=length(data);
- for i=1:length(fn) % depth-first
- for j=1:len
- if(isstruct(getfield(data(j),fn{i})))
- newdata(j)=setfield(newdata(j),fn{i},jstruct2array(getfield(data(j),fn{i})));
- end
- end
- end
- if(~isempty(strmatch('x0x5F_ArrayType_',fn)) && ~isempty(strmatch('x0x5F_ArrayData_',fn)))
- newdata=cell(len,1);
- for j=1:len
- ndata=cast(data(j).x0x5F_ArrayData_,data(j).x0x5F_ArrayType_);
- iscpx=0;
- if(~isempty(strmatch('x0x5F_ArrayIsComplex_',fn)))
- if(data(j).x0x5F_ArrayIsComplex_)
- iscpx=1;
- end
- end
- if(~isempty(strmatch('x0x5F_ArrayIsSparse_',fn)))
- if(data(j).x0x5F_ArrayIsSparse_)
- if(~isempty(strmatch('x0x5F_ArraySize_',fn)))
- dim=double(data(j).x0x5F_ArraySize_);
- if(iscpx && size(ndata,2)==4-any(dim==1))
- ndata(:,end-1)=complex(ndata(:,end-1),ndata(:,end));
- end
- if isempty(ndata)
- % All-zeros sparse
- ndata=sparse(dim(1),prod(dim(2:end)));
- elseif dim(1)==1
- % Sparse row vector
- ndata=sparse(1,ndata(:,1),ndata(:,2),dim(1),prod(dim(2:end)));
- elseif dim(2)==1
- % Sparse column vector
- ndata=sparse(ndata(:,1),1,ndata(:,2),dim(1),prod(dim(2:end)));
- else
- % Generic sparse array.
- ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3),dim(1),prod(dim(2:end)));
- end
- else
- if(iscpx && size(ndata,2)==4)
- ndata(:,3)=complex(ndata(:,3),ndata(:,4));
- end
- ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3));
- end
- end
- elseif(~isempty(strmatch('x0x5F_ArraySize_',fn)))
- if(iscpx && size(ndata,2)==2)
- ndata=complex(ndata(:,1),ndata(:,2));
- end
- ndata=reshape(ndata(:),data(j).x0x5F_ArraySize_);
- end
- newdata{j}=ndata;
- end
- if(len==1)
- newdata=newdata{1};
- end
- end
- %%-------------------------------------------------------------------------
- function object = parse_object(varargin)
- parse_char('{');
- object = [];
- type='';
- count=-1;
- if(next_char == '$')
- type=inStr(pos+1); % TODO
- pos=pos+2;
- end
- if(next_char == '#')
- pos=pos+1;
- count=double(parse_number());
- end
- if next_char ~= '}'
- num=0;
- while 1
- str = parseStr(varargin{:});
- if isempty(str)
- error_pos('Name of value at position %d cannot be empty');
- end
- %parse_char(':');
- val = parse_value(varargin{:});
- num=num+1;
- eval( sprintf( 'object.%s = val;', valid_field(str) ) );
- if next_char == '}' || (count>=0 && num>=count)
- break;
- end
- %parse_char(',');
- end
- end
- if(count==-1)
- parse_char('}');
- end
- %%-------------------------------------------------------------------------
- function [cid,len]=elem_info(type)
- id=strfind('iUIlLdD',type);
- dataclass={'int8','uint8','int16','int32','int64','single','double'};
- bytelen=[1,1,2,4,8,4,8];
- if(id>0)
- cid=dataclass{id};
- len=bytelen(id);
- else
- error_pos('unsupported type at position %d');
- end
- %%-------------------------------------------------------------------------
- function [data adv]=parse_block(type,count,varargin)
- global pos inStr isoct fileendian systemendian
- [cid,len]=elem_info(type);
- datastr=inStr(pos:pos+len*count-1);
- if(isoct)
- newdata=int8(datastr);
- else
- newdata=uint8(datastr);
- end
- id=strfind('iUIlLdD',type);
- if(id<=5 && fileendian~=systemendian)
- newdata=swapbytes(typecast(newdata,cid));
- end
- data=typecast(newdata,cid);
- adv=double(len*count);
- %%-------------------------------------------------------------------------
- function object = parse_array(varargin) % JSON array is written in row-major order
- global pos inStr isoct
- parse_char('[');
- object = cell(0, 1);
- dim=[];
- type='';
- count=-1;
- if(next_char == '$')
- type=inStr(pos+1);
- pos=pos+2;
- end
- if(next_char == '#')
- pos=pos+1;
- if(next_char=='[')
- dim=parse_array(varargin{:});
- count=prod(double(dim));
- else
- count=double(parse_number());
- end
- end
- if(~isempty(type))
- if(count>=0)
- [object adv]=parse_block(type,count,varargin{:});
- if(~isempty(dim))
- object=reshape(object,dim);
- end
- pos=pos+adv;
- return;
- else
- endpos=matching_bracket(inStr,pos);
- [cid,len]=elem_info(type);
- count=(endpos-pos)/len;
- [object adv]=parse_block(type,count,varargin{:});
- pos=pos+adv;
- parse_char(']');
- return;
- end
- end
- if next_char ~= ']'
- while 1
- val = parse_value(varargin{:});
- object{end+1} = val;
- if next_char == ']'
- break;
- end
- %parse_char(',');
- end
- end
- if(jsonopt('SimplifyCell',0,varargin{:})==1)
- try
- oldobj=object;
- object=cell2mat(object')';
- if(iscell(oldobj) && isstruct(object) && numel(object)>1 && jsonopt('SimplifyCellArray',1,varargin{:})==0)
- object=oldobj;
- elseif(size(object,1)>1 && ndims(object)==2)
- object=object';
- end
- catch
- end
- end
- if(count==-1)
- parse_char(']');
- end
- %%-------------------------------------------------------------------------
- function parse_char(c)
- global pos inStr len
- skip_whitespace;
- if pos > len || inStr(pos) ~= c
- error_pos(sprintf('Expected %c at position %%d', c));
- else
- pos = pos + 1;
- skip_whitespace;
- end
- %%-------------------------------------------------------------------------
- function c = next_char
- global pos inStr len
- skip_whitespace;
- if pos > len
- c = [];
- else
- c = inStr(pos);
- end
- %%-------------------------------------------------------------------------
- function skip_whitespace
- global pos inStr len
- while pos <= len && isspace(inStr(pos))
- pos = pos + 1;
- end
- %%-------------------------------------------------------------------------
- function str = parseStr(varargin)
- global pos inStr esc index_esc len_esc
- % len, ns = length(inStr), keyboard
- type=inStr(pos);
- if type ~= 'S' && type ~= 'C' && type ~= 'H'
- error_pos('String starting with S expected at position %d');
- else
- pos = pos + 1;
- end
- if(type == 'C')
- str=inStr(pos);
- pos=pos+1;
- return;
- end
- bytelen=double(parse_number());
- if(length(inStr)>=pos+bytelen-1)
- str=inStr(pos:pos+bytelen-1);
- pos=pos+bytelen;
- else
- error_pos('End of file while expecting end of inStr');
- end
- %%-------------------------------------------------------------------------
- function num = parse_number(varargin)
- global pos inStr len isoct fileendian systemendian
- id=strfind('iUIlLdD',inStr(pos));
- if(isempty(id))
- error_pos('expecting a number at position %d');
- end
- type={'int8','uint8','int16','int32','int64','single','double'};
- bytelen=[1,1,2,4,8,4,8];
- datastr=inStr(pos+1:pos+bytelen(id));
- if(isoct)
- newdata=int8(datastr);
- else
- newdata=uint8(datastr);
- end
- if(id<=5 && fileendian~=systemendian)
- newdata=swapbytes(typecast(newdata,type{id}));
- end
- num=typecast(newdata,type{id});
- pos = pos + bytelen(id)+1;
- %%-------------------------------------------------------------------------
- function val = parse_value(varargin)
- global pos inStr len
- true = 1; false = 0;
- switch(inStr(pos))
- case {'S','C','H'}
- val = parseStr(varargin{:});
- return;
- case '['
- val = parse_array(varargin{:});
- return;
- case '{'
- val = parse_object(varargin{:});
- if isstruct(val)
- if(~isempty(strmatch('x0x5F_ArrayType_',fieldnames(val), 'exact')))
- val=jstruct2array(val);
- end
- elseif isempty(val)
- val = struct;
- end
- return;
- case {'i','U','I','l','L','d','D'}
- val = parse_number(varargin{:});
- return;
- case 'T'
- val = true;
- pos = pos + 1;
- return;
- case 'F'
- val = false;
- pos = pos + 1;
- return;
- case {'Z','N'}
- val = [];
- pos = pos + 1;
- return;
- end
- error_pos('Value expected at position %d');
- %%-------------------------------------------------------------------------
- function error_pos(msg)
- global pos inStr len
- poShow = max(min([pos-15 pos-1 pos pos+20],len),1);
- if poShow(3) == poShow(2)
- poShow(3:4) = poShow(2)+[0 -1]; % display nothing after
- end
- msg = [sprintf(msg, pos) ': ' ...
- inStr(poShow(1):poShow(2)) '<error>' inStr(poShow(3):poShow(4)) ];
- error( ['JSONparser:invalidFormat: ' msg] );
- %%-------------------------------------------------------------------------
- function str = valid_field(str)
- global isoct
- % From MATLAB doc: field names must begin with a letter, which may be
- % followed by any combination of letters, digits, and underscores.
- % Invalid characters will be converted to underscores, and the prefix
- % "x0x[Hex code]_" will be added if the first character is not a letter.
- pos=regexp(str,'^[^A-Za-z]','once');
- if(~isempty(pos))
- if(~isoct)
- str=regexprep(str,'^([^A-Za-z])','x0x${sprintf(''%X'',unicode2native($1))}_','once');
- else
- str=sprintf('x0x%X_%s',char(str(1)),str(2:end));
- end
- end
- if(isempty(regexp(str,'[^0-9A-Za-z_]', 'once' ))) return; end
- if(~isoct)
- str=regexprep(str,'([^0-9A-Za-z_])','_0x${sprintf(''%X'',unicode2native($1))}_');
- else
- pos=regexp(str,'[^0-9A-Za-z_]');
- if(isempty(pos)) return; end
- str0=str;
- pos0=[0 pos(:)' length(str)];
- str='';
- for i=1:length(pos)
- str=[str str0(pos0(i)+1:pos(i)-1) sprintf('_0x%X_',str0(pos(i)))];
- end
- if(pos(end)~=length(str))
- str=[str str0(pos0(end-1)+1:pos0(end))];
- end
- end
- %str(~isletter(str) & ~('0' <= str & str <= '9')) = '_';
- %%-------------------------------------------------------------------------
- function endpos = matching_quote(str,pos)
- len=length(str);
- while(pos<len)
- if(str(pos)=='"')
- if(~(pos>1 && str(pos-1)=='\'))
- endpos=pos;
- return;
- end
- end
- pos=pos+1;
- end
- error('unmatched quotation mark');
- %%-------------------------------------------------------------------------
- function [endpos e1l e1r maxlevel] = matching_bracket(str,pos)
- global arraytoken
- level=1;
- maxlevel=level;
- endpos=0;
- bpos=arraytoken(arraytoken>=pos);
- tokens=str(bpos);
- len=length(tokens);
- pos=1;
- e1l=[];
- e1r=[];
- while(pos<=len)
- c=tokens(pos);
- if(c==']')
- level=level-1;
- if(isempty(e1r)) e1r=bpos(pos); end
- if(level==0)
- endpos=bpos(pos);
- return
- end
- end
- if(c=='[')
- if(isempty(e1l)) e1l=bpos(pos); end
- level=level+1;
- maxlevel=max(maxlevel,level);
- end
- if(c=='"')
- pos=matching_quote(tokens,pos+1);
- end
- pos=pos+1;
- end
- if(endpos==0)
- error('unmatched "]"');
- end
|