Path: blob/master/Week 9/Programming Assignment - 8/ex8/lib/jsonlab/loadjson.m
626 views
function data = loadjson(fname,varargin)1%2% data=loadjson(fname,opt)3% or4% data=loadjson(fname,'param1',value1,'param2',value2,...)5%6% parse a JSON (JavaScript Object Notation) file or string7%8% authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)9% created on 2011/09/09, including previous works from10%11% Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/2571312% created on 2009/11/0213% François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/2339314% created on 2009/03/2215% Joel Feenstra:16% http://www.mathworks.com/matlabcentral/fileexchange/2056517% created on 2008/07/0318%19% $Id: loadjson.m 460 2015-01-03 00:30:45Z fangq $20%21% input:22% fname: input file name, if fname contains "{}" or "[]", fname23% will be interpreted as a JSON string24% opt: a struct to store parsing options, opt can be replaced by25% a list of ('param',value) pairs - the param string is equivallent26% to a field in opt. opt can have the following27% fields (first in [.|.] is the default)28%29% opt.SimplifyCell [0|1]: if set to 1, loadjson will call cell2mat30% for each element of the JSON data, and group31% arrays based on the cell2mat rules.32% opt.FastArrayParser [1|0 or integer]: if set to 1, use a33% speed-optimized array parser when loading an34% array object. The fast array parser may35% collapse block arrays into a single large36% array similar to rules defined in cell2mat; 0 to37% use a legacy parser; if set to a larger-than-138% value, this option will specify the minimum39% dimension to enable the fast array parser. For40% example, if the input is a 3D array, setting41% FastArrayParser to 1 will return a 3D array;42% setting to 2 will return a cell array of 2D43% arrays; setting to 3 will return to a 2D cell44% array of 1D vectors; setting to 4 will return a45% 3D cell array.46% opt.ShowProgress [0|1]: if set to 1, loadjson displays a progress bar.47%48% output:49% dat: a cell array, where {...} blocks are converted into cell arrays,50% and [...] are converted to arrays51%52% examples:53% dat=loadjson('{"obj":{"string":"value","array":[1,2,3]}}')54% dat=loadjson(['examples' filesep 'example1.json'])55% dat=loadjson(['examples' filesep 'example1.json'],'SimplifyCell',1)56%57% license:58% BSD, see LICENSE_BSD.txt files for details59%60% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)61%6263global pos inStr len esc index_esc len_esc isoct arraytoken6465if(regexp(fname,'[\{\}\]\[]','once'))66string=fname;67elseif(exist(fname,'file'))68fid = fopen(fname,'rb');69string = fread(fid,inf,'uint8=>char')';70fclose(fid);71else72error('input file does not exist');73end7475pos = 1; len = length(string); inStr = string;76isoct=exist('OCTAVE_VERSION','builtin');77arraytoken=find(inStr=='[' | inStr==']' | inStr=='"');78jstr=regexprep(inStr,'\\\\',' ');79escquote=regexp(jstr,'\\"');80arraytoken=sort([arraytoken escquote]);8182% String delimiters and escape chars identified to improve speed:83esc = find(inStr=='"' | inStr=='\' ); % comparable to: regexp(inStr, '["\\]');84index_esc = 1; len_esc = length(esc);8586opt=varargin2struct(varargin{:});8788if(jsonopt('ShowProgress',0,opt)==1)89opt.progressbar_=waitbar(0,'loading ...');90end91jsoncount=1;92while pos <= len93switch(next_char)94case '{'95data{jsoncount} = parse_object(opt);96case '['97data{jsoncount} = parse_array(opt);98otherwise99error_pos('Outer level structure must be an object or an array');100end101jsoncount=jsoncount+1;102end % while103104jsoncount=length(data);105if(jsoncount==1 && iscell(data))106data=data{1};107end108109if(~isempty(data))110if(isstruct(data)) % data can be a struct array111data=jstruct2array(data);112elseif(iscell(data))113data=jcell2array(data);114end115end116if(isfield(opt,'progressbar_'))117close(opt.progressbar_);118end119120%%121function newdata=jcell2array(data)122len=length(data);123newdata=data;124for i=1:len125if(isstruct(data{i}))126newdata{i}=jstruct2array(data{i});127elseif(iscell(data{i}))128newdata{i}=jcell2array(data{i});129end130end131132%%-------------------------------------------------------------------------133function newdata=jstruct2array(data)134fn=fieldnames(data);135newdata=data;136len=length(data);137for i=1:length(fn) % depth-first138for j=1:len139if(isstruct(getfield(data(j),fn{i})))140newdata(j)=setfield(newdata(j),fn{i},jstruct2array(getfield(data(j),fn{i})));141end142end143end144if(~isempty(strmatch('x0x5F_ArrayType_',fn)) && ~isempty(strmatch('x0x5F_ArrayData_',fn)))145newdata=cell(len,1);146for j=1:len147ndata=cast(data(j).x0x5F_ArrayData_,data(j).x0x5F_ArrayType_);148iscpx=0;149if(~isempty(strmatch('x0x5F_ArrayIsComplex_',fn)))150if(data(j).x0x5F_ArrayIsComplex_)151iscpx=1;152end153end154if(~isempty(strmatch('x0x5F_ArrayIsSparse_',fn)))155if(data(j).x0x5F_ArrayIsSparse_)156if(~isempty(strmatch('x0x5F_ArraySize_',fn)))157dim=data(j).x0x5F_ArraySize_;158if(iscpx && size(ndata,2)==4-any(dim==1))159ndata(:,end-1)=complex(ndata(:,end-1),ndata(:,end));160end161if isempty(ndata)162% All-zeros sparse163ndata=sparse(dim(1),prod(dim(2:end)));164elseif dim(1)==1165% Sparse row vector166ndata=sparse(1,ndata(:,1),ndata(:,2),dim(1),prod(dim(2:end)));167elseif dim(2)==1168% Sparse column vector169ndata=sparse(ndata(:,1),1,ndata(:,2),dim(1),prod(dim(2:end)));170else171% Generic sparse array.172ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3),dim(1),prod(dim(2:end)));173end174else175if(iscpx && size(ndata,2)==4)176ndata(:,3)=complex(ndata(:,3),ndata(:,4));177end178ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3));179end180end181elseif(~isempty(strmatch('x0x5F_ArraySize_',fn)))182if(iscpx && size(ndata,2)==2)183ndata=complex(ndata(:,1),ndata(:,2));184end185ndata=reshape(ndata(:),data(j).x0x5F_ArraySize_);186end187newdata{j}=ndata;188end189if(len==1)190newdata=newdata{1};191end192end193194%%-------------------------------------------------------------------------195function object = parse_object(varargin)196parse_char('{');197object = [];198if next_char ~= '}'199while 1200str = parseStr(varargin{:});201if isempty(str)202error_pos('Name of value at position %d cannot be empty');203end204parse_char(':');205val = parse_value(varargin{:});206eval( sprintf( 'object.%s = val;', valid_field(str) ) );207if next_char == '}'208break;209end210parse_char(',');211end212end213parse_char('}');214215%%-------------------------------------------------------------------------216217function object = parse_array(varargin) % JSON array is written in row-major order218global pos inStr isoct219parse_char('[');220object = cell(0, 1);221dim2=[];222arraydepth=jsonopt('JSONLAB_ArrayDepth_',1,varargin{:});223pbar=jsonopt('progressbar_',-1,varargin{:});224225if next_char ~= ']'226if(jsonopt('FastArrayParser',1,varargin{:})>=1 && arraydepth>=jsonopt('FastArrayParser',1,varargin{:}))227[endpos, e1l, e1r, maxlevel]=matching_bracket(inStr,pos);228arraystr=['[' inStr(pos:endpos)];229arraystr=regexprep(arraystr,'"_NaN_"','NaN');230arraystr=regexprep(arraystr,'"([-+]*)_Inf_"','$1Inf');231arraystr(arraystr==sprintf('\n'))=[];232arraystr(arraystr==sprintf('\r'))=[];233%arraystr=regexprep(arraystr,'\s*,',','); % this is slow,sometimes needed234if(~isempty(e1l) && ~isempty(e1r)) % the array is in 2D or higher D235astr=inStr((e1l+1):(e1r-1));236astr=regexprep(astr,'"_NaN_"','NaN');237astr=regexprep(astr,'"([-+]*)_Inf_"','$1Inf');238astr(astr==sprintf('\n'))=[];239astr(astr==sprintf('\r'))=[];240astr(astr==' ')='';241if(isempty(find(astr=='[', 1))) % array is 2D242dim2=length(sscanf(astr,'%f,',[1 inf]));243end244else % array is 1D245astr=arraystr(2:end-1);246astr(astr==' ')='';247[obj, count, errmsg, nextidx]=sscanf(astr,'%f,',[1,inf]);248if(nextidx>=length(astr)-1)249object=obj;250pos=endpos;251parse_char(']');252return;253end254end255if(~isempty(dim2))256astr=arraystr;257astr(astr=='[')='';258astr(astr==']')='';259astr(astr==' ')='';260[obj, count, errmsg, nextidx]=sscanf(astr,'%f,',inf);261if(nextidx>=length(astr)-1)262object=reshape(obj,dim2,numel(obj)/dim2)';263pos=endpos;264parse_char(']');265if(pbar>0)266waitbar(pos/length(inStr),pbar,'loading ...');267end268return;269end270end271arraystr=regexprep(arraystr,'\]\s*,','];');272else273arraystr='[';274end275try276if(isoct && regexp(arraystr,'"','once'))277error('Octave eval can produce empty cells for JSON-like input');278end279object=eval(arraystr);280pos=endpos;281catch282while 1283newopt=varargin2struct(varargin{:},'JSONLAB_ArrayDepth_',arraydepth+1);284val = parse_value(newopt);285object{end+1} = val;286if next_char == ']'287break;288end289parse_char(',');290end291end292end293if(jsonopt('SimplifyCell',0,varargin{:})==1)294try295oldobj=object;296object=cell2mat(object')';297if(iscell(oldobj) && isstruct(object) && numel(object)>1 && jsonopt('SimplifyCellArray',1,varargin{:})==0)298object=oldobj;299elseif(size(object,1)>1 && ndims(object)==2)300object=object';301end302catch303end304end305parse_char(']');306307if(pbar>0)308waitbar(pos/length(inStr),pbar,'loading ...');309end310%%-------------------------------------------------------------------------311312function parse_char(c)313global pos inStr len314skip_whitespace;315if pos > len || inStr(pos) ~= c316error_pos(sprintf('Expected %c at position %%d', c));317else318pos = pos + 1;319skip_whitespace;320end321322%%-------------------------------------------------------------------------323324function c = next_char325global pos inStr len326skip_whitespace;327if pos > len328c = [];329else330c = inStr(pos);331end332333%%-------------------------------------------------------------------------334335function skip_whitespace336global pos inStr len337while pos <= len && isspace(inStr(pos))338pos = pos + 1;339end340341%%-------------------------------------------------------------------------342function str = parseStr(varargin)343global pos inStr len esc index_esc len_esc344% len, ns = length(inStr), keyboard345if inStr(pos) ~= '"'346error_pos('String starting with " expected at position %d');347else348pos = pos + 1;349end350str = '';351while pos <= len352while index_esc <= len_esc && esc(index_esc) < pos353index_esc = index_esc + 1;354end355if index_esc > len_esc356str = [str inStr(pos:len)];357pos = len + 1;358break;359else360str = [str inStr(pos:esc(index_esc)-1)];361pos = esc(index_esc);362end363nstr = length(str); switch inStr(pos)364case '"'365pos = pos + 1;366if(~isempty(str))367if(strcmp(str,'_Inf_'))368str=Inf;369elseif(strcmp(str,'-_Inf_'))370str=-Inf;371elseif(strcmp(str,'_NaN_'))372str=NaN;373end374end375return;376case '\'377if pos+1 > len378error_pos('End of file reached right after escape character');379end380pos = pos + 1;381switch inStr(pos)382case {'"' '\' '/'}383str(nstr+1) = inStr(pos);384pos = pos + 1;385case {'b' 'f' 'n' 'r' 't'}386str(nstr+1) = sprintf(['\' inStr(pos)]);387pos = pos + 1;388case 'u'389if pos+4 > len390error_pos('End of file reached in escaped unicode character');391end392str(nstr+(1:6)) = inStr(pos-1:pos+4);393pos = pos + 5;394end395otherwise % should never happen396str(nstr+1) = inStr(pos), keyboard397pos = pos + 1;398end399end400error_pos('End of file while expecting end of inStr');401402%%-------------------------------------------------------------------------403404function num = parse_number(varargin)405global pos inStr len isoct406currstr=inStr(pos:end);407numstr=0;408if(isoct~=0)409numstr=regexp(currstr,'^\s*-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?','end');410[num, one] = sscanf(currstr, '%f', 1);411delta=numstr+1;412else413[num, one, err, delta] = sscanf(currstr, '%f', 1);414if ~isempty(err)415error_pos('Error reading number at position %d');416end417end418pos = pos + delta-1;419420%%-------------------------------------------------------------------------421422function val = parse_value(varargin)423global pos inStr len424true = 1; false = 0;425426pbar=jsonopt('progressbar_',-1,varargin{:});427if(pbar>0)428waitbar(pos/len,pbar,'loading ...');429end430431switch(inStr(pos))432case '"'433val = parseStr(varargin{:});434return;435case '['436val = parse_array(varargin{:});437return;438case '{'439val = parse_object(varargin{:});440if isstruct(val)441if(~isempty(strmatch('x0x5F_ArrayType_',fieldnames(val), 'exact')))442val=jstruct2array(val);443end444elseif isempty(val)445val = struct;446end447return;448case {'-','0','1','2','3','4','5','6','7','8','9'}449val = parse_number(varargin{:});450return;451case 't'452if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'true')453val = true;454pos = pos + 4;455return;456end457case 'f'458if pos+4 <= len && strcmpi(inStr(pos:pos+4), 'false')459val = false;460pos = pos + 5;461return;462end463case 'n'464if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'null')465val = [];466pos = pos + 4;467return;468end469end470error_pos('Value expected at position %d');471%%-------------------------------------------------------------------------472473function error_pos(msg)474global pos inStr len475poShow = max(min([pos-15 pos-1 pos pos+20],len),1);476if poShow(3) == poShow(2)477poShow(3:4) = poShow(2)+[0 -1]; % display nothing after478end479msg = [sprintf(msg, pos) ': ' ...480inStr(poShow(1):poShow(2)) '<error>' inStr(poShow(3):poShow(4)) ];481error( ['JSONparser:invalidFormat: ' msg] );482483%%-------------------------------------------------------------------------484485function str = valid_field(str)486global isoct487% From MATLAB doc: field names must begin with a letter, which may be488% followed by any combination of letters, digits, and underscores.489% Invalid characters will be converted to underscores, and the prefix490% "x0x[Hex code]_" will be added if the first character is not a letter.491pos=regexp(str,'^[^A-Za-z]','once');492if(~isempty(pos))493if(~isoct)494str=regexprep(str,'^([^A-Za-z])','x0x${sprintf(''%X'',unicode2native($1))}_','once');495else496str=sprintf('x0x%X_%s',char(str(1)),str(2:end));497end498end499if(isempty(regexp(str,'[^0-9A-Za-z_]', 'once' ))) return; end500if(~isoct)501str=regexprep(str,'([^0-9A-Za-z_])','_0x${sprintf(''%X'',unicode2native($1))}_');502else503pos=regexp(str,'[^0-9A-Za-z_]');504if(isempty(pos)) return; end505str0=str;506pos0=[0 pos(:)' length(str)];507str='';508for i=1:length(pos)509str=[str str0(pos0(i)+1:pos(i)-1) sprintf('_0x%X_',str0(pos(i)))];510end511if(pos(end)~=length(str))512str=[str str0(pos0(end-1)+1:pos0(end))];513end514end515%str(~isletter(str) & ~('0' <= str & str <= '9')) = '_';516517%%-------------------------------------------------------------------------518function endpos = matching_quote(str,pos)519len=length(str);520while(pos<len)521if(str(pos)=='"')522if(~(pos>1 && str(pos-1)=='\'))523endpos=pos;524return;525end526end527pos=pos+1;528end529error('unmatched quotation mark');530%%-------------------------------------------------------------------------531function [endpos, e1l, e1r, maxlevel] = matching_bracket(str,pos)532global arraytoken533level=1;534maxlevel=level;535endpos=0;536bpos=arraytoken(arraytoken>=pos);537tokens=str(bpos);538len=length(tokens);539pos=1;540e1l=[];541e1r=[];542while(pos<=len)543c=tokens(pos);544if(c==']')545level=level-1;546if(isempty(e1r)) e1r=bpos(pos); end547if(level==0)548endpos=bpos(pos);549return550end551end552if(c=='[')553if(isempty(e1l)) e1l=bpos(pos); end554level=level+1;555maxlevel=max(maxlevel,level);556end557if(c=='"')558pos=matching_quote(tokens,pos+1);559end560pos=pos+1;561end562if(endpos==0)563error('unmatched "]"');564end565566567568