text: 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
  
  