Home > vbmeg > external > iso2mesh > loadjson.m

loadjson

PURPOSE ^

SYNOPSIS ^

function data = loadjson(fname,varargin)

DESCRIPTION ^

 data=loadjson(fname,opt)
    or
 data=loadjson(fname,'param1',value1,'param2',value2,...)

 parse a JSON (JavaScript Object Notation) file or string

 authors:Qianqian Fang (q.fang <at> neu.edu)
 created on 2011/09/09, including previous works from 

         Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713
            created on 2009/11/02
         François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393
            created on  2009/03/22
         Joel Feenstra:
         http://www.mathworks.com/matlabcentral/fileexchange/20565
            created on 2008/07/03

 $Id$

 input:
      fname: input file name, if fname contains "{}" or "[]", fname
             will be interpreted as a JSON string
      opt: a struct to store parsing options, opt can be replaced by 
           a list of ('param',value) pairs - the param string is equivallent
           to a field in opt. opt can have the following 
           fields (first in [.|.] is the default)

           opt.SimplifyCell [0|1]: if set to 1, loadjson will call cell2mat
                         for each element of the JSON data, and group 
                         arrays based on the cell2mat rules.
           opt.FastArrayParser [1|0 or integer]: if set to 1, use a
                         speed-optimized array parser when loading an 
                         array object. The fast array parser may 
                         collapse block arrays into a single large
                         array similar to rules defined in cell2mat; 0 to 
                         use a legacy parser; if set to a larger-than-1
                         value, this option will specify the minimum
                         dimension to enable the fast array parser. For
                         example, if the input is a 3D array, setting
                         FastArrayParser to 1 will return a 3D array;
                         setting to 2 will return a cell array of 2D
                         arrays; setting to 3 will return to a 2D cell
                         array of 1D vectors; setting to 4 will return a
                         3D cell array.
           opt.ShowProgress [0|1]: if set to 1, loadjson displays a progress bar.

 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 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)

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 function data = loadjson(fname,varargin)
0002 %
0003 % data=loadjson(fname,opt)
0004 %    or
0005 % data=loadjson(fname,'param1',value1,'param2',value2,...)
0006 %
0007 % parse a JSON (JavaScript Object Notation) file or string
0008 %
0009 % authors:Qianqian Fang (q.fang <at> neu.edu)
0010 % created on 2011/09/09, including previous works from
0011 %
0012 %         Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713
0013 %            created on 2009/11/02
0014 %         François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393
0015 %            created on  2009/03/22
0016 %         Joel Feenstra:
0017 %         http://www.mathworks.com/matlabcentral/fileexchange/20565
0018 %            created on 2008/07/03
0019 %
0020 % $Id$
0021 %
0022 % input:
0023 %      fname: input file name, if fname contains "{}" or "[]", fname
0024 %             will be interpreted as a JSON string
0025 %      opt: a struct to store parsing options, opt can be replaced by
0026 %           a list of ('param',value) pairs - the param string is equivallent
0027 %           to a field in opt. opt can have the following
0028 %           fields (first in [.|.] is the default)
0029 %
0030 %           opt.SimplifyCell [0|1]: if set to 1, loadjson will call cell2mat
0031 %                         for each element of the JSON data, and group
0032 %                         arrays based on the cell2mat rules.
0033 %           opt.FastArrayParser [1|0 or integer]: if set to 1, use a
0034 %                         speed-optimized array parser when loading an
0035 %                         array object. The fast array parser may
0036 %                         collapse block arrays into a single large
0037 %                         array similar to rules defined in cell2mat; 0 to
0038 %                         use a legacy parser; if set to a larger-than-1
0039 %                         value, this option will specify the minimum
0040 %                         dimension to enable the fast array parser. For
0041 %                         example, if the input is a 3D array, setting
0042 %                         FastArrayParser to 1 will return a 3D array;
0043 %                         setting to 2 will return a cell array of 2D
0044 %                         arrays; setting to 3 will return to a 2D cell
0045 %                         array of 1D vectors; setting to 4 will return a
0046 %                         3D cell array.
0047 %           opt.ShowProgress [0|1]: if set to 1, loadjson displays a progress bar.
0048 %
0049 % output:
0050 %      dat: a cell array, where {...} blocks are converted into cell arrays,
0051 %           and [...] are converted to arrays
0052 %
0053 % examples:
0054 %      dat=loadjson('{"obj":{"string":"value","array":[1,2,3]}}')
0055 %      dat=loadjson(['examples' filesep 'example1.json'])
0056 %      dat=loadjson(['examples' filesep 'example1.json'],'SimplifyCell',1)
0057 %
0058 % license:
0059 %     BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details
0060 %
0061 % -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)
0062 %
0063 
0064 global pos index_esc isoct arraytoken
0065 
0066 if(regexp(fname,'^\s*(?:\[.*\])|(?:\{.*\})\s*$','once'))
0067    string=fname;
0068 elseif(exist(fname,'file'))
0069    try
0070        string = fileread(fname);
0071    catch
0072        try
0073            string = urlread(['file://',fname]);
0074        catch
0075            string = urlread(['file://',fullfile(pwd,fname)]);
0076        end
0077    end
0078 else
0079    error('input file does not exist');
0080 end
0081 
0082 pos = 1; len = length(string); inStr = string;
0083 isoct=exist('OCTAVE_VERSION','builtin');
0084 arraytoken=find(inStr=='[' | inStr==']' | inStr=='"');
0085 jstr=regexprep(inStr,'\\\\','  ');
0086 escquote=regexp(jstr,'\\"');
0087 arraytoken=sort([arraytoken escquote]);
0088 
0089 % String delimiters and escape chars identified to improve speed:
0090 esc = find(inStr=='"' | inStr=='\' ); % comparable to: regexp(inStr, '["\\]');
0091 index_esc = 1;
0092 
0093 opt=varargin2struct(varargin{:});
0094 
0095 if(jsonopt('ShowProgress',0,opt)==1)
0096     opt.progressbar_=waitbar(0,'loading ...');
0097 end
0098 jsoncount=1;
0099 while pos <= len
0100     switch(next_char(inStr))
0101         case '{'
0102             data{jsoncount} = parse_object(inStr, esc, opt);
0103         case '['
0104             data{jsoncount} = parse_array(inStr, esc, opt);
0105         otherwise
0106             error_pos('Outer level structure must be an object or an array',inStr);
0107     end
0108     jsoncount=jsoncount+1;
0109 end % while
0110 
0111 jsoncount=length(data);
0112 if(jsoncount==1 && iscell(data))
0113     data=data{1};
0114 end
0115 
0116 if(isfield(opt,'progressbar_'))
0117     close(opt.progressbar_);
0118 end
0119 
0120 %%-------------------------------------------------------------------------
0121 function object = parse_object(inStr, esc, varargin)
0122     parse_char(inStr, '{');
0123     object = [];
0124     if next_char(inStr) ~= '}'
0125         while 1
0126             str = parseStr(inStr, esc, varargin{:});
0127             if isempty(str)
0128                 error_pos('Name of value at position %d cannot be empty',inStr);
0129             end
0130             parse_char(inStr, ':');
0131             val = parse_value(inStr, esc, varargin{:});
0132             object.(valid_field(str))=val;
0133             if next_char(inStr) == '}'
0134                 break;
0135             end
0136             parse_char(inStr, ',');
0137         end
0138     end
0139     parse_char(inStr, '}');
0140     if(isstruct(object))
0141         object=struct2jdata(object);
0142     end
0143 
0144 %%-------------------------------------------------------------------------
0145 
0146 function object = parse_array(inStr, esc, varargin) % JSON array is written in row-major order
0147     global pos isoct
0148     parse_char(inStr, '[');
0149     object = cell(0, 1);
0150     dim2=[];
0151     arraydepth=jsonopt('JSONLAB_ArrayDepth_',1,varargin{:});
0152     pbar=-1;
0153     if(isfield(varargin{1},'progressbar_'))
0154         pbar=varargin{1}.progressbar_;
0155     end
0156 
0157     if next_char(inStr) ~= ']'
0158     if(jsonopt('FastArrayParser',1,varargin{:})>=1 && arraydepth>=jsonopt('FastArrayParser',1,varargin{:}))
0159             [endpos, e1l, e1r]=matching_bracket(inStr,pos);
0160             arraystr=['[' inStr(pos:endpos)];
0161             arraystr=regexprep(arraystr,'"_NaN_"','NaN');
0162             arraystr=regexprep(arraystr,'"([-+]*)_Inf_"','$1Inf');
0163             arraystr(arraystr==sprintf('\n'))=[];
0164             arraystr(arraystr==sprintf('\r'))=[];
0165             %arraystr=regexprep(arraystr,'\s*,',','); % this is slow,sometimes needed
0166             if(~isempty(e1l) && ~isempty(e1r)) % the array is in 2D or higher D
0167             astr=inStr((e1l+1):(e1r-1));
0168             astr=regexprep(astr,'"_NaN_"','NaN');
0169             astr=regexprep(astr,'"([-+]*)_Inf_"','$1Inf');
0170             astr(astr==sprintf('\n'))=[];
0171             astr(astr==sprintf('\r'))=[];
0172             astr(astr==' ')='';
0173             if(isempty(find(astr=='[', 1))) % array is 2D
0174                     dim2=length(sscanf(astr,'%f,',[1 inf]));
0175             end
0176             else % array is 1D
0177             astr=arraystr(2:end-1);
0178             astr(astr==' ')='';
0179             [obj, count, errmsg, nextidx]=sscanf(astr,'%f,',[1,inf]);
0180             if(nextidx>=length(astr)-1)
0181                     object=obj;
0182                     pos=endpos;
0183                     parse_char(inStr, ']');
0184                     return;
0185             end
0186             end
0187 
0188             try
0189               if(~isempty(dim2))
0190             astr=arraystr;
0191             astr(astr=='[')='';
0192             astr(astr==']')='';
0193                 astr=regexprep(astr,'\s*$','');
0194             astr(astr==' ')='';
0195             [obj, count, errmsg, nextidx]=sscanf(astr,'%f,',inf);
0196             if(nextidx>=length(astr)-1)
0197                     object=reshape(obj,dim2,numel(obj)/dim2)';
0198                     pos=endpos;
0199                     parse_char(inStr, ']');
0200                     if(pbar>0)
0201                         waitbar(pos/length(inStr),pbar,'loading ...');
0202                     end
0203                     return;
0204             end
0205               end
0206               arraystr=regexprep(arraystr,'\]\s*,','];');
0207             catch
0208             end
0209     else
0210             arraystr='[';
0211     end
0212         try
0213            arraystr=regexprep(arraystr,'^\s*\[','{','once');
0214            arraystr=regexprep(arraystr,'\]\s*$','}','once');
0215            if(isoct && regexp(arraystr,'"','once'))
0216                 error('Octave eval can produce empty cells for JSON-like input');
0217            end
0218            if(regexp(arraystr,':','once'))
0219                 error('One can not use MATLAB-like ":" construct inside a JSON array');
0220            end
0221            object=eval(arraystr);
0222            pos=endpos;
0223         catch
0224          while 1
0225             newopt=varargin2struct(varargin{:},'JSONLAB_ArrayDepth_',arraydepth+1);
0226             val = parse_value(inStr, esc, newopt);
0227             object{end+1} = val;
0228             if next_char(inStr) == ']'
0229                 break;
0230             end
0231             parse_char(inStr, ',');
0232          end
0233         end
0234     end
0235     if(jsonopt('SimplifyCell',0,varargin{:})==1)
0236       try
0237         oldobj=object;
0238         object=cell2mat(object')';
0239         if(iscell(oldobj) && isstruct(object) && numel(object)>1 && jsonopt('SimplifyCellArray',1,varargin{:})==0)
0240             object=oldobj;
0241         elseif(size(object,1)>1 && ismatrix(object))
0242             object=object';
0243         end
0244       catch
0245       end
0246     end
0247     parse_char(inStr, ']');
0248     
0249     if(pbar>0)
0250         waitbar(pos/length(inStr),pbar,'loading ...');
0251     end
0252 %%-------------------------------------------------------------------------
0253 
0254 function parse_char(inStr, c)
0255     global pos
0256     pos=skip_whitespace(pos, inStr);
0257     if pos > length(inStr) || inStr(pos) ~= c
0258         error_pos(sprintf('Expected %c at position %%d', c),inStr);
0259     else
0260         pos = pos + 1;
0261         pos=skip_whitespace(pos, inStr);
0262     end
0263 
0264 %%-------------------------------------------------------------------------
0265 
0266 function c = next_char(inStr)
0267     global pos
0268     pos=skip_whitespace(pos, inStr);
0269     if pos > length(inStr)
0270         c = [];
0271     else
0272         c = inStr(pos);
0273     end
0274 
0275 %%-------------------------------------------------------------------------
0276 
0277 function newpos=skip_whitespace(pos, inStr)
0278     newpos=pos;
0279     while newpos <= length(inStr) && isspace(inStr(newpos))
0280         newpos = newpos + 1;
0281     end
0282 
0283 %%-------------------------------------------------------------------------
0284 function str = parseStr(inStr, esc, varargin)
0285     global pos index_esc
0286  % len, ns = length(inStr), keyboard
0287     if inStr(pos) ~= '"'
0288         error_pos('String starting with " expected at position %d',inStr);
0289     else
0290         pos = pos + 1;
0291     end
0292     str = '';
0293     while pos <= length(inStr)
0294         while index_esc <= length(esc) && esc(index_esc) < pos
0295             index_esc = index_esc + 1;
0296         end
0297         if index_esc > length(esc)
0298             str = [str inStr(pos:end)];
0299             pos = length(inStr) + 1;
0300             break;
0301         else
0302             str = [str inStr(pos:esc(index_esc)-1)];
0303             pos = esc(index_esc);
0304         end
0305         nstr = length(str);
0306         switch inStr(pos)
0307             case '"'
0308                 pos = pos + 1;
0309                 if(~isempty(str))
0310                     if(strcmp(str,'_Inf_'))
0311                         str=Inf;
0312                     elseif(strcmp(str,'-_Inf_'))
0313                         str=-Inf;
0314                     elseif(strcmp(str,'_NaN_'))
0315                         str=NaN;
0316                     end
0317                 end
0318                 return;
0319             case '\'
0320                 if pos+1 > length(inStr)
0321                     error_pos('End of file reached right after escape character',inStr);
0322                 end
0323                 pos = pos + 1;
0324                 switch inStr(pos)
0325                     case {'"' '\' '/'}
0326                         str(nstr+1) = inStr(pos);
0327                         pos = pos + 1;
0328                     case {'b' 'f' 'n' 'r' 't'}
0329                         str(nstr+1) = sprintf(['\' inStr(pos)]);
0330                         pos = pos + 1;
0331                     case 'u'
0332                         if pos+4 > length(inStr)
0333                             error_pos('End of file reached in escaped unicode character',inStr);
0334                         end
0335                         str(nstr+(1:6)) = inStr(pos-1:pos+4);
0336                         pos = pos + 5;
0337                 end
0338             otherwise % should never happen
0339                 str(nstr+1) = inStr(pos);
0340                 keyboard;
0341                 pos = pos + 1;
0342         end
0343     end
0344     error_pos('End of file while expecting end of inStr',inStr);
0345 
0346 %%-------------------------------------------------------------------------
0347 
0348 function num = parse_number(inStr, varargin)
0349     global pos isoct
0350     currstr=inStr(pos:min(pos+30,end));
0351     if(isoct~=0)
0352         numstr=regexp(currstr,'^\s*-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?','end');
0353         [num] = sscanf(currstr, '%f', 1);
0354         delta=numstr+1;
0355     else
0356         [num, one, err, delta] = sscanf(currstr, '%f', 1);
0357         if ~isempty(err)
0358             error_pos('Error reading number at position %d',inStr);
0359         end
0360     end
0361     pos = pos + delta-1;
0362 
0363 %%-------------------------------------------------------------------------
0364 
0365 function val = parse_value(inStr, esc, varargin)
0366     global pos
0367     len=length(inStr);
0368     if(isfield(varargin{1},'progressbar_'))
0369         waitbar(pos/len,varargin{1}.progressbar_,'loading ...');
0370     end
0371     
0372     switch(inStr(pos))
0373         case '"'
0374             val = parseStr(inStr, esc, varargin{:});
0375             return;
0376         case '['
0377             val = parse_array(inStr, esc, varargin{:});
0378             return;
0379         case '{'
0380             val = parse_object(inStr, esc, varargin{:});
0381             return;
0382         case {'-','0','1','2','3','4','5','6','7','8','9'}
0383             val = parse_number(inStr, varargin{:});
0384             return;
0385         case 't'
0386             if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'true')
0387                 val = true;
0388                 pos = pos + 4;
0389                 return;
0390             end
0391         case 'f'
0392             if pos+4 <= len && strcmpi(inStr(pos:pos+4), 'false')
0393                 val = false;
0394                 pos = pos + 5;
0395                 return;
0396             end
0397         case 'n'
0398             if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'null')
0399                 val = [];
0400                 pos = pos + 4;
0401                 return;
0402             end
0403     end
0404     error_pos('Value expected at position %d',inStr);
0405 %%-------------------------------------------------------------------------
0406 
0407 function error_pos(msg, inStr)
0408     global pos len
0409     poShow = max(min([pos-15 pos-1 pos pos+20],len),1);
0410     if poShow(3) == poShow(2)
0411         poShow(3:4) = poShow(2)+[0 -1];  % display nothing after
0412     end
0413     msg = [sprintf(msg, pos) ': ' ...
0414     inStr(poShow(1):poShow(2)) '<error>' inStr(poShow(3):poShow(4)) ];
0415     error( ['JSONparser:invalidFormat: ' msg] );
0416 
0417 %%-------------------------------------------------------------------------
0418 
0419 function str = valid_field(str)
0420 global isoct
0421 % From MATLAB doc: field names must begin with a letter, which may be
0422 % followed by any combination of letters, digits, and underscores.
0423 % Invalid characters will be converted to underscores, and the prefix
0424 % "x0x[Hex code]_" will be added if the first character is not a letter.
0425     pos=regexp(str,'^[^A-Za-z]','once');
0426     if(~isempty(pos))
0427         if(~isoct && str(1)+0 > 255)
0428             str=regexprep(str,'^([^A-Za-z])','x0x${sprintf(''%X'',unicode2native($1))}_','once');
0429         else
0430             str=sprintf('x0x%X_%s',char(str(1)),str(2:end));
0431         end
0432     end
0433     if(isempty(regexp(str,'[^0-9A-Za-z_]', 'once' )))
0434         return;
0435     end
0436     if(~isoct)
0437         str=regexprep(str,'([^0-9A-Za-z_])','_0x${sprintf(''%X'',unicode2native($1))}_');
0438     else
0439         pos=regexp(str,'[^0-9A-Za-z_]');
0440         if(isempty(pos))
0441             return;
0442         end
0443         str0=str;
0444         pos0=[0 pos(:)' length(str)];
0445         str='';
0446         for i=1:length(pos)
0447             str=[str str0(pos0(i)+1:pos(i)-1) sprintf('_0x%X_',str0(pos(i)))];
0448         end
0449         if(pos(end)~=length(str))
0450             str=[str str0(pos0(end-1)+1:pos0(end))];
0451         end
0452     end
0453     %str(~isletter(str) & ~('0' <= str & str <= '9')) = '_';
0454 
0455 %%-------------------------------------------------------------------------
0456 function endpos = matching_quote(str,pos)
0457 len=length(str);
0458 while(pos<len)
0459     if(str(pos)=='"')
0460         if(~(pos>1 && str(pos-1)=='\'))
0461             endpos=pos;
0462             return;
0463         end        
0464     end
0465     pos=pos+1;
0466 end
0467 error('unmatched quotation mark');
0468 %%-------------------------------------------------------------------------
0469 function [endpos, e1l, e1r, maxlevel] = matching_bracket(str,pos)
0470 global arraytoken
0471 level=1;
0472 maxlevel=level;
0473 endpos=0;
0474 bpos=arraytoken(arraytoken>=pos);
0475 tokens=str(bpos);
0476 len=length(tokens);
0477 pos=1;
0478 e1l=[];
0479 e1r=[];
0480 while(pos<=len)
0481     c=tokens(pos);
0482     if(c==']')
0483         level=level-1;
0484         if(isempty(e1r))
0485             e1r=bpos(pos);
0486         end
0487         if(level==0)
0488             endpos=bpos(pos);
0489             return
0490         end
0491     elseif(c=='[')
0492         if(isempty(e1l))
0493             e1l=bpos(pos);
0494         end
0495         level=level+1;
0496         maxlevel=max(maxlevel,level);
0497     elseif(c=='"')
0498         pos=matching_quote(tokens,pos+1);
0499     end
0500     pos=pos+1;
0501 end
0502 if(endpos==0) 
0503     error('unmatched "]"');
0504 end

Generated on Mon 22-May-2023 06:53:56 by m2html © 2005