123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- function data = loadjson(fname,varargin)
- %
- % data=loadjson(fname,opt)
- % or
- % data=loadjson(fname,'param1',value1,'param2',value2,...)
- %
- % parse a JSON (JavaScript Object Notation) file or string
- %
- % authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
- % created on 2011/09/09, including previous works from
- %
- % Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713
- % created on 2009/11/02
- % François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393
- % created on 2009/03/22
- % Joel Feenstra:
- % http://www.mathworks.com/matlabcentral/fileexchange/20565
- % created on 2008/07/03
- %
- % $Id: loadjson.m 460 2015-01-03 00:30:45Z fangq $
- %
- % input:
- % fname: input file name, if fname contains "{}" or "[]", fname
- % will be interpreted as a JSON string
- % opt: a struct to store parsing options, opt can be replaced by
- % a list of ('param',value) pairs - the param string is equivallent
- % to a field in opt. opt can have the following
- % fields (first in [.|.] is the default)
- %
- % opt.SimplifyCell [0|1]: if set to 1, loadjson will call cell2mat
- % for each element of the JSON data, and group
- % arrays based on the cell2mat rules.
- % opt.FastArrayParser [1|0 or integer]: if set to 1, use a
- % speed-optimized array parser when loading an
- % array object. The fast array parser may
- % collapse block arrays into a single large
- % array similar to rules defined in cell2mat; 0 to
- % use a legacy parser; if set to a larger-than-1
- % value, this option will specify the minimum
- % dimension to enable the fast array parser. For
- % example, if the input is a 3D array, setting
- % FastArrayParser to 1 will return a 3D array;
- % setting to 2 will return a cell array of 2D
- % arrays; setting to 3 will return to a 2D cell
- % array of 1D vectors; setting to 4 will return a
- % 3D cell array.
- % opt.ShowProgress [0|1]: if set to 1, loadjson displays a progress bar.
- %
- % output:
- % dat: a cell array, where {...} blocks are converted into cell arrays,
- % and [...] are converted to arrays
- %
- % examples:
- % dat=loadjson('{"obj":{"string":"value","array":[1,2,3]}}')
- % dat=loadjson(['examples' filesep 'example1.json'])
- % dat=loadjson(['examples' filesep 'example1.json'],'SimplifyCell',1)
- %
- % license:
- % BSD, see LICENSE_BSD.txt 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
- 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{:});
- if(jsonopt('ShowProgress',0,opt)==1)
- opt.progressbar_=waitbar(0,'loading ...');
- end
- 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
- if(isfield(opt,'progressbar_'))
- close(opt.progressbar_);
- 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=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 = [];
- if next_char ~= '}'
- 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{:});
- eval( sprintf( 'object.%s = val;', valid_field(str) ) );
- if next_char == '}'
- break;
- end
- parse_char(',');
- end
- end
- parse_char('}');
- %%-------------------------------------------------------------------------
- function object = parse_array(varargin) % JSON array is written in row-major order
- global pos inStr isoct
- parse_char('[');
- object = cell(0, 1);
- dim2=[];
- arraydepth=jsonopt('JSONLAB_ArrayDepth_',1,varargin{:});
- pbar=jsonopt('progressbar_',-1,varargin{:});
- if next_char ~= ']'
- if(jsonopt('FastArrayParser',1,varargin{:})>=1 && arraydepth>=jsonopt('FastArrayParser',1,varargin{:}))
- [endpos, e1l, e1r, maxlevel]=matching_bracket(inStr,pos);
- arraystr=['[' inStr(pos:endpos)];
- arraystr=regexprep(arraystr,'"_NaN_"','NaN');
- arraystr=regexprep(arraystr,'"([-+]*)_Inf_"','$1Inf');
- arraystr(arraystr==sprintf('\n'))=[];
- arraystr(arraystr==sprintf('\r'))=[];
- %arraystr=regexprep(arraystr,'\s*,',','); % this is slow,sometimes needed
- if(~isempty(e1l) && ~isempty(e1r)) % the array is in 2D or higher D
- astr=inStr((e1l+1):(e1r-1));
- astr=regexprep(astr,'"_NaN_"','NaN');
- astr=regexprep(astr,'"([-+]*)_Inf_"','$1Inf');
- astr(astr==sprintf('\n'))=[];
- astr(astr==sprintf('\r'))=[];
- astr(astr==' ')='';
- if(isempty(find(astr=='[', 1))) % array is 2D
- dim2=length(sscanf(astr,'%f,',[1 inf]));
- end
- else % array is 1D
- astr=arraystr(2:end-1);
- astr(astr==' ')='';
- [obj, count, errmsg, nextidx]=sscanf(astr,'%f,',[1,inf]);
- if(nextidx>=length(astr)-1)
- object=obj;
- pos=endpos;
- parse_char(']');
- return;
- end
- end
- if(~isempty(dim2))
- astr=arraystr;
- astr(astr=='[')='';
- astr(astr==']')='';
- astr(astr==' ')='';
- [obj, count, errmsg, nextidx]=sscanf(astr,'%f,',inf);
- if(nextidx>=length(astr)-1)
- object=reshape(obj,dim2,numel(obj)/dim2)';
- pos=endpos;
- parse_char(']');
- if(pbar>0)
- waitbar(pos/length(inStr),pbar,'loading ...');
- end
- return;
- end
- end
- arraystr=regexprep(arraystr,'\]\s*,','];');
- else
- arraystr='[';
- end
- try
- if(isoct && regexp(arraystr,'"','once'))
- error('Octave eval can produce empty cells for JSON-like input');
- end
- object=eval(arraystr);
- pos=endpos;
- catch
- while 1
- newopt=varargin2struct(varargin{:},'JSONLAB_ArrayDepth_',arraydepth+1);
- val = parse_value(newopt);
- object{end+1} = val;
- if next_char == ']'
- break;
- end
- parse_char(',');
- end
- end
- end
- if(jsonopt('SimplifyCell',0,varargin{:})==1)
- try
- oldobj=object;
- object=cell2mat(object')';
- if(iscell(oldobj) && isstruct(object) && numel(object)>1 && jsonopt('SimplifyCellArray',1,varargin{:})==0)
- object=oldobj;
- elseif(size(object,1)>1 && ndims(object)==2)
- object=object';
- end
- catch
- end
- end
- parse_char(']');
-
- if(pbar>0)
- waitbar(pos/length(inStr),pbar,'loading ...');
- 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 len esc index_esc len_esc
- % len, ns = length(inStr), keyboard
- if inStr(pos) ~= '"'
- error_pos('String starting with " expected at position %d');
- else
- pos = pos + 1;
- end
- str = '';
- while pos <= len
- while index_esc <= len_esc && esc(index_esc) < pos
- index_esc = index_esc + 1;
- end
- if index_esc > len_esc
- str = [str inStr(pos:len)];
- pos = len + 1;
- break;
- else
- str = [str inStr(pos:esc(index_esc)-1)];
- pos = esc(index_esc);
- end
- nstr = length(str); switch inStr(pos)
- case '"'
- pos = pos + 1;
- if(~isempty(str))
- if(strcmp(str,'_Inf_'))
- str=Inf;
- elseif(strcmp(str,'-_Inf_'))
- str=-Inf;
- elseif(strcmp(str,'_NaN_'))
- str=NaN;
- end
- end
- return;
- case '\'
- if pos+1 > len
- error_pos('End of file reached right after escape character');
- end
- pos = pos + 1;
- switch inStr(pos)
- case {'"' '\' '/'}
- str(nstr+1) = inStr(pos);
- pos = pos + 1;
- case {'b' 'f' 'n' 'r' 't'}
- str(nstr+1) = sprintf(['\' inStr(pos)]);
- pos = pos + 1;
- case 'u'
- if pos+4 > len
- error_pos('End of file reached in escaped unicode character');
- end
- str(nstr+(1:6)) = inStr(pos-1:pos+4);
- pos = pos + 5;
- end
- otherwise % should never happen
- str(nstr+1) = inStr(pos), keyboard
- pos = pos + 1;
- end
- end
- error_pos('End of file while expecting end of inStr');
- %%-------------------------------------------------------------------------
- function num = parse_number(varargin)
- global pos inStr len isoct
- currstr=inStr(pos:end);
- numstr=0;
- if(isoct~=0)
- numstr=regexp(currstr,'^\s*-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?','end');
- [num, one] = sscanf(currstr, '%f', 1);
- delta=numstr+1;
- else
- [num, one, err, delta] = sscanf(currstr, '%f', 1);
- if ~isempty(err)
- error_pos('Error reading number at position %d');
- end
- end
- pos = pos + delta-1;
- %%-------------------------------------------------------------------------
- function val = parse_value(varargin)
- global pos inStr len
- true = 1; false = 0;
-
- pbar=jsonopt('progressbar_',-1,varargin{:});
- if(pbar>0)
- waitbar(pos/len,pbar,'loading ...');
- end
-
- switch(inStr(pos))
- case '"'
- 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 {'-','0','1','2','3','4','5','6','7','8','9'}
- val = parse_number(varargin{:});
- return;
- case 't'
- if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'true')
- val = true;
- pos = pos + 4;
- return;
- end
- case 'f'
- if pos+4 <= len && strcmpi(inStr(pos:pos+4), 'false')
- val = false;
- pos = pos + 5;
- return;
- end
- case 'n'
- if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'null')
- val = [];
- pos = pos + 4;
- return;
- end
- end
- error_pos('Value expected at position %d');
- %%-------------------------------------------------------------------------
- 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
|