538 lines
17 KiB
Matlab
538 lines
17 KiB
Matlab
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 (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.
|
|
% opt.ParseStringArray [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 file for details
|
|
%
|
|
% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)
|
|
%
|
|
import jsonlab.*
|
|
|
|
global pos index_esc isoct arraytoken
|
|
|
|
if(regexp(fname,'^\s*(?:\[.*\])|(?:\{.*\})\s*$','once'))
|
|
string=fname;
|
|
elseif(exist(fname,'file'))
|
|
try
|
|
string = fileread(fname);
|
|
catch
|
|
try
|
|
string = urlread(['file://',fname]);
|
|
catch
|
|
string = urlread(['file://',fullfile(pwd,fname)]);
|
|
end
|
|
end
|
|
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;
|
|
|
|
opt=varargin2struct(varargin{:});
|
|
|
|
if(jsonopt('ShowProgress',0,opt)==1)
|
|
opt.progressbar_=waitbar(0,'loading ...');
|
|
end
|
|
jsoncount=1;
|
|
while pos <= len
|
|
switch(next_char(inStr))
|
|
case '{'
|
|
data{jsoncount} = parse_object(inStr, esc, opt);
|
|
case '['
|
|
data{jsoncount} = parse_array(inStr, esc, opt);
|
|
otherwise
|
|
error_pos('Outer level structure must be an object or an array',inStr);
|
|
end
|
|
jsoncount=jsoncount+1;
|
|
end % while
|
|
|
|
jsoncount=length(data);
|
|
if(jsoncount==1 && iscell(data))
|
|
data=data{1};
|
|
end
|
|
|
|
if(isfield(opt,'progressbar_'))
|
|
close(opt.progressbar_);
|
|
end
|
|
|
|
%%-------------------------------------------------------------------------
|
|
function object = parse_object(inStr, esc, varargin)
|
|
import jsonlab.*
|
|
parse_char(inStr, '{');
|
|
object = [];
|
|
if next_char(inStr) ~= '}'
|
|
while 1
|
|
str = parseStr(inStr, esc, varargin{:});
|
|
if isempty(str)
|
|
error_pos('Name of value at position %d cannot be empty',inStr);
|
|
end
|
|
parse_char(inStr, ':');
|
|
val = parse_value(inStr, esc, varargin{:});
|
|
object.(valid_field(str))=val;
|
|
if next_char(inStr) == '}'
|
|
break;
|
|
end
|
|
parse_char(inStr, ',');
|
|
end
|
|
end
|
|
parse_char(inStr, '}');
|
|
if(isstruct(object))
|
|
object=struct2jdata(object);
|
|
end
|
|
|
|
%%-------------------------------------------------------------------------
|
|
|
|
function object = parse_array(inStr, esc, varargin) % JSON array is written in row-major order
|
|
import jsonlab.*
|
|
global pos isoct
|
|
parse_char(inStr, '[');
|
|
object = cell(0, 1);
|
|
dim2=[];
|
|
arraydepth=jsonopt('JSONLAB_ArrayDepth_',1,varargin{:});
|
|
pbar=-1;
|
|
if(isfield(varargin{1},'progressbar_'))
|
|
pbar=varargin{1}.progressbar_;
|
|
end
|
|
|
|
if next_char(inStr) ~= ']'
|
|
if(jsonopt('FastArrayParser',1,varargin{:})>=1 && arraydepth>=jsonopt('FastArrayParser',1,varargin{:}))
|
|
[endpos, e1l, e1r]=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(inStr, ']');
|
|
return;
|
|
end
|
|
end
|
|
|
|
try
|
|
if(~isempty(dim2))
|
|
astr=arraystr;
|
|
astr(astr=='[')='';
|
|
astr(astr==']')='';
|
|
astr=regexprep(astr,'\s*$','');
|
|
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(inStr, ']');
|
|
if(pbar>0)
|
|
waitbar(pos/length(inStr),pbar,'loading ...');
|
|
end
|
|
return;
|
|
end
|
|
end
|
|
arraystr=regexprep(arraystr,'\]\s*,','];');
|
|
catch
|
|
end
|
|
else
|
|
arraystr='[';
|
|
end
|
|
try
|
|
arraystr=regexprep(arraystr,'^\s*\[','{','once');
|
|
arraystr=regexprep(arraystr,'\]\s*$','}','once');
|
|
if(isoct && regexp(arraystr,'"','once'))
|
|
error('Octave eval can produce empty cells for JSON-like input');
|
|
end
|
|
if(regexp(arraystr,':','once'))
|
|
error('One can not use MATLAB-like ":" construct inside a JSON array');
|
|
end
|
|
if(jsonopt('ParseStringArray',0,varargin{:})==0)
|
|
arraystr=regexprep(arraystr,'\"','''');
|
|
end
|
|
object=eval(arraystr);
|
|
if(iscell(object))
|
|
object=cellfun(@unescapejsonstring,object,'UniformOutput',false);
|
|
end
|
|
pos=endpos;
|
|
catch
|
|
while 1
|
|
newopt=varargin2struct(varargin{:},'JSONLAB_ArrayDepth_',arraydepth+1);
|
|
val = parse_value(inStr, esc, newopt);
|
|
object{end+1} = val;
|
|
if next_char(inStr) == ']'
|
|
break;
|
|
end
|
|
parse_char(inStr, ',');
|
|
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 && ismatrix(object))
|
|
object=object';
|
|
end
|
|
catch
|
|
end
|
|
end
|
|
parse_char(inStr, ']');
|
|
|
|
if(pbar>0)
|
|
waitbar(pos/length(inStr),pbar,'loading ...');
|
|
end
|
|
%%-------------------------------------------------------------------------
|
|
|
|
function parse_char(inStr, c)
|
|
import jsonlab.*
|
|
global pos
|
|
pos=skip_whitespace(pos, inStr);
|
|
if pos > length(inStr) || inStr(pos) ~= c
|
|
error_pos(sprintf('Expected %c at position %%d', c),inStr);
|
|
else
|
|
pos = pos + 1;
|
|
pos=skip_whitespace(pos, inStr);
|
|
end
|
|
|
|
%%-------------------------------------------------------------------------
|
|
|
|
function c = next_char(inStr)
|
|
import jsonlab.*
|
|
global pos
|
|
pos=skip_whitespace(pos, inStr);
|
|
if pos > length(inStr)
|
|
c = [];
|
|
else
|
|
c = inStr(pos);
|
|
end
|
|
|
|
%%-------------------------------------------------------------------------
|
|
|
|
function newpos=skip_whitespace(pos, inStr)
|
|
import jsonlab.*
|
|
newpos=pos;
|
|
while newpos <= length(inStr) && isspace(inStr(newpos))
|
|
newpos = newpos + 1;
|
|
end
|
|
|
|
%%-------------------------------------------------------------------------
|
|
function str = parseStr(inStr, esc, varargin)
|
|
import jsonlab.*
|
|
global pos index_esc
|
|
% len, ns = length(inStr), keyboard
|
|
if inStr(pos) ~= '"'
|
|
error_pos('String starting with " expected at position %d',inStr);
|
|
else
|
|
pos = pos + 1;
|
|
end
|
|
str = '';
|
|
while pos <= length(inStr)
|
|
while index_esc <= length(esc) && esc(index_esc) < pos
|
|
index_esc = index_esc + 1;
|
|
end
|
|
if index_esc > length(esc)
|
|
str = [str inStr(pos:end)];
|
|
pos = length(inStr) + 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 > length(inStr)
|
|
error_pos('End of file reached right after escape character',inStr);
|
|
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 > length(inStr)
|
|
error_pos('End of file reached in escaped unicode character',inStr);
|
|
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
|
|
str=unescapejsonstring(str);
|
|
error_pos('End of file while expecting end of inStr',inStr);
|
|
|
|
%%-------------------------------------------------------------------------
|
|
|
|
function num = parse_number(inStr, varargin)
|
|
import jsonlab.*
|
|
global pos isoct
|
|
currstr=inStr(pos:min(pos+30,end));
|
|
if(isoct~=0)
|
|
numstr=regexp(currstr,'^\s*-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?','end');
|
|
[num] = 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',inStr);
|
|
end
|
|
end
|
|
pos = pos + delta-1;
|
|
|
|
%%-------------------------------------------------------------------------
|
|
|
|
function val = parse_value(inStr, esc, varargin)
|
|
import jsonlab.*
|
|
global pos
|
|
len=length(inStr);
|
|
if(isfield(varargin{1},'progressbar_'))
|
|
waitbar(pos/len,varargin{1}.progressbar_,'loading ...');
|
|
end
|
|
|
|
switch(inStr(pos))
|
|
case '"'
|
|
val = parseStr(inStr, esc, varargin{:});
|
|
return;
|
|
case '['
|
|
val = parse_array(inStr, esc, varargin{:});
|
|
return;
|
|
case '{'
|
|
val = parse_object(inStr, esc, varargin{:});
|
|
return;
|
|
case {'-','0','1','2','3','4','5','6','7','8','9'}
|
|
val = parse_number(inStr, 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',inStr);
|
|
%%-------------------------------------------------------------------------
|
|
|
|
function error_pos(msg, inStr)
|
|
import jsonlab.*
|
|
global pos 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)
|
|
import jsonlab.*
|
|
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(1)+0 > 255)
|
|
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)
|
|
import jsonlab.*
|
|
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)
|
|
import jsonlab.*
|
|
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
|
|
elseif(c=='[')
|
|
if(isempty(e1l))
|
|
e1l=bpos(pos);
|
|
end
|
|
level=level+1;
|
|
maxlevel=max(maxlevel,level);
|
|
elseif(c=='"')
|
|
pos=matching_quote(tokens,pos+1);
|
|
end
|
|
pos=pos+1;
|
|
end
|
|
if(endpos==0)
|
|
error('unmatched "]"');
|
|
end
|
|
|
|
function newstr=unescapejsonstring(str)
|
|
import jsonlab.*
|
|
newstr=str;
|
|
if(~ischar(str))
|
|
return;
|
|
end
|
|
escapechars={'\\','\"','\/','\a','\b','\f','\n','\r','\t','\v'};
|
|
for i=1:length(escapechars);
|
|
newstr=regexprep(newstr,regexprep(escapechars{i},'\\','\\\\'), escapechars{i});
|
|
end
|
|
newstr=regexprep(newstr,'\\u([0-9A-Fa-f]{4})', '${char(base2dec($1,16))}');
|