1% Licensed to the Apache Software Foundation (ASF) under one 2% or more contributor license agreements. See the NOTICE file 3% distributed with this work for additional information 4% regarding copyright ownership. The ASF licenses this file 5% to you under the Apache License, Version 2.0 (the 6% "License"); you may not use this file except in compliance 7% with the License. You may obtain a copy of the License at 8% 9% http://www.apache.org/licenses/LICENSE-2.0 10% 11% Unless required by applicable law or agreed to in writing, 12% software distributed under the License is distributed on an 13% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14% KIND, either express or implied. See the License for the 15% specific language governing permissions and limitations 16% under the License. 17 18function data = parse_json(fname,varargin) 19%PARSE_JSON parse a JSON (JavaScript Object Notation) file or string 20% 21% Based on jsonlab (https://github.com/fangq/jsonlab) created by Qianqian Fang. Jsonlab is lisonced under BSD or GPL v3. 22 23global pos inStr len esc index_esc len_esc isoct arraytoken 24 25if(regexp(fname,'^\s*(?:\[.+\])|(?:\{.+\})\s*$','once')) 26 string=fname; 27elseif(exist(fname,'file')) 28 try 29 string = fileread(fname); 30 catch 31 try 32 string = urlread(['file://',fname]); 33 catch 34 string = urlread(['file://',fullfile(pwd,fname)]); 35 end 36 end 37else 38 error('input file does not exist'); 39end 40 41pos = 1; len = length(string); inStr = string; 42isoct=exist('OCTAVE_VERSION','builtin'); 43arraytoken=find(inStr=='[' | inStr==']' | inStr=='"'); 44jstr=regexprep(inStr,'\\\\',' '); 45escquote=regexp(jstr,'\\"'); 46arraytoken=sort([arraytoken escquote]); 47 48% String delimiters and escape chars identified to improve speed: 49esc = find(inStr=='"' | inStr=='\' ); % comparable to: regexp(inStr, '["\\]'); 50index_esc = 1; len_esc = length(esc); 51 52opt=varargin2struct(varargin{:}); 53 54if(jsonopt('ShowProgress',0,opt)==1) 55 opt.progressbar_=waitbar(0,'loading ...'); 56end 57jsoncount=1; 58while pos <= len 59 switch(next_char) 60 case '{' 61 data{jsoncount} = parse_object(opt); 62 case '[' 63 data{jsoncount} = parse_array(opt); 64 otherwise 65 error_pos('Outer level structure must be an object or an array'); 66 end 67 jsoncount=jsoncount+1; 68end % while 69 70jsoncount=length(data); 71if(jsoncount==1 && iscell(data)) 72 data=data{1}; 73end 74 75if(isfield(opt,'progressbar_')) 76 close(opt.progressbar_); 77end 78 79%%------------------------------------------------------------------------- 80function object = parse_object(varargin) 81 parse_char('{'); 82 object = []; 83 if next_char ~= '}' 84 while 1 85 str = parseStr(varargin{:}); 86 if isempty(str) 87 error_pos('Name of value at position %d cannot be empty'); 88 end 89 parse_char(':'); 90 val = parse_value(varargin{:}); 91 object.(valid_field(str))=val; 92 if next_char == '}' 93 break; 94 end 95 parse_char(','); 96 end 97 end 98 parse_char('}'); 99 if(isstruct(object)) 100 object=struct2jdata(object); 101 end 102 103%%------------------------------------------------------------------------- 104 105function object = parse_array(varargin) % JSON array is written in row-major order 106global pos inStr isoct 107 parse_char('['); 108 object = cell(0, 1); 109 dim2=[]; 110 arraydepth=jsonopt('JSONLAB_ArrayDepth_',1,varargin{:}); 111 pbar=-1; 112 if(isfield(varargin{1},'progressbar_')) 113 pbar=varargin{1}.progressbar_; 114 end 115 116 if next_char ~= ']' 117 if(jsonopt('FastArrayParser',1,varargin{:})>=1 && arraydepth>=jsonopt('FastArrayParser',1,varargin{:})) 118 [endpos, e1l, e1r]=matching_bracket(inStr,pos); 119 arraystr=['[' inStr(pos:endpos)]; 120 arraystr=regexprep(arraystr,'"_NaN_"','NaN'); 121 arraystr=regexprep(arraystr,'"([-+]*)_Inf_"','$1Inf'); 122 arraystr(arraystr==sprintf('\n'))=[]; 123 arraystr(arraystr==sprintf('\r'))=[]; 124 %arraystr=regexprep(arraystr,'\s*,',','); % this is slow,sometimes needed 125 if(~isempty(e1l) && ~isempty(e1r)) % the array is in 2D or higher D 126 astr=inStr((e1l+1):(e1r-1)); 127 astr=regexprep(astr,'"_NaN_"','NaN'); 128 astr=regexprep(astr,'"([-+]*)_Inf_"','$1Inf'); 129 astr(astr==sprintf('\n'))=[]; 130 astr(astr==sprintf('\r'))=[]; 131 astr(astr==' ')=''; 132 if(isempty(find(astr=='[', 1))) % array is 2D 133 dim2=length(sscanf(astr,'%f,',[1 inf])); 134 end 135 else % array is 1D 136 astr=arraystr(2:end-1); 137 astr(astr==' ')=''; 138 [obj, count, errmsg, nextidx]=sscanf(astr,'%f,',[1,inf]); 139 if(nextidx>=length(astr)-1) 140 object=obj; 141 pos=endpos; 142 parse_char(']'); 143 return; 144 end 145 end 146 if(~isempty(dim2)) 147 astr=arraystr; 148 astr(astr=='[')=''; 149 astr(astr==']')=''; 150 astr(astr==' ')=''; 151 [obj, count, errmsg, nextidx]=sscanf(astr,'%f,',inf); 152 if(nextidx>=length(astr)-1) 153 object=reshape(obj,dim2,numel(obj)/dim2)'; 154 pos=endpos; 155 parse_char(']'); 156 if(pbar>0) 157 waitbar(pos/length(inStr),pbar,'loading ...'); 158 end 159 return; 160 end 161 end 162 arraystr=regexprep(arraystr,'\]\s*,','];'); 163 else 164 arraystr='['; 165 end 166 try 167 if(isoct && regexp(arraystr,'"','once')) 168 error('Octave eval can produce empty cells for JSON-like input'); 169 end 170 object=eval(arraystr); 171 pos=endpos; 172 catch 173 while 1 174 newopt=varargin2struct(varargin{:},'JSONLAB_ArrayDepth_',arraydepth+1); 175 val = parse_value(newopt); 176 object{end+1} = val; 177 if next_char == ']' 178 break; 179 end 180 parse_char(','); 181 end 182 end 183 end 184 if(jsonopt('SimplifyCell',0,varargin{:})==1) 185 try 186 oldobj=object; 187 object=cell2mat(object')'; 188 if(iscell(oldobj) && isstruct(object) && numel(object)>1 && jsonopt('SimplifyCellArray',1,varargin{:})==0) 189 object=oldobj; 190 elseif(size(object,1)>1 && ismatrix(object)) 191 object=object'; 192 end 193 catch 194 end 195 end 196 parse_char(']'); 197 198 if(pbar>0) 199 waitbar(pos/length(inStr),pbar,'loading ...'); 200 end 201%%------------------------------------------------------------------------- 202 203function parse_char(c) 204 global pos inStr len 205 pos=skip_whitespace(pos,inStr,len); 206 if pos > len || inStr(pos) ~= c 207 error_pos(sprintf('Expected %c at position %%d', c)); 208 else 209 pos = pos + 1; 210 pos=skip_whitespace(pos,inStr,len); 211 end 212 213%%------------------------------------------------------------------------- 214 215function c = next_char 216 global pos inStr len 217 pos=skip_whitespace(pos,inStr,len); 218 if pos > len 219 c = []; 220 else 221 c = inStr(pos); 222 end 223 224%%------------------------------------------------------------------------- 225 226function newpos=skip_whitespace(pos,inStr,len) 227 newpos=pos; 228 while newpos <= len && isspace(inStr(newpos)) 229 newpos = newpos + 1; 230 end 231 232%%------------------------------------------------------------------------- 233function str = parseStr(varargin) 234 global pos inStr len esc index_esc len_esc 235 % len, ns = length(inStr), keyboard 236 if inStr(pos) ~= '"' 237 error_pos('String starting with " expected at position %d'); 238 else 239 pos = pos + 1; 240 end 241 str = ''; 242 while pos <= len 243 while index_esc <= len_esc && esc(index_esc) < pos 244 index_esc = index_esc + 1; 245 end 246 if index_esc > len_esc 247 str = [str inStr(pos:len)]; 248 pos = len + 1; 249 break; 250 else 251 str = [str inStr(pos:esc(index_esc)-1)]; 252 pos = esc(index_esc); 253 end 254 nstr = length(str); 255 switch inStr(pos) 256 case '"' 257 pos = pos + 1; 258 if(~isempty(str)) 259 if(strcmp(str,'_Inf_')) 260 str=Inf; 261 elseif(strcmp(str,'-_Inf_')) 262 str=-Inf; 263 elseif(strcmp(str,'_NaN_')) 264 str=NaN; 265 end 266 end 267 return; 268 case '\' 269 if pos+1 > len 270 error_pos('End of file reached right after escape character'); 271 end 272 pos = pos + 1; 273 switch inStr(pos) 274 case {'"' '\' '/'} 275 str(nstr+1) = inStr(pos); 276 pos = pos + 1; 277 case {'b' 'f' 'n' 'r' 't'} 278 str(nstr+1) = sprintf(['\' inStr(pos)]); 279 pos = pos + 1; 280 case 'u' 281 if pos+4 > len 282 error_pos('End of file reached in escaped unicode character'); 283 end 284 str(nstr+(1:6)) = inStr(pos-1:pos+4); 285 pos = pos + 5; 286 end 287 otherwise % should never happen 288 str(nstr+1) = inStr(pos); 289 keyboard; 290 pos = pos + 1; 291 end 292 end 293 error_pos('End of file while expecting end of inStr'); 294 295%%------------------------------------------------------------------------- 296 297function num = parse_number(varargin) 298 global pos inStr isoct 299 currstr=inStr(pos:min(pos+30,end)); 300 if(isoct~=0) 301 numstr=regexp(currstr,'^\s*-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?','end'); 302 [num] = sscanf(currstr, '%f', 1); 303 delta=numstr+1; 304 else 305 [num, one, err, delta] = sscanf(currstr, '%f', 1); 306 if ~isempty(err) 307 error_pos('Error reading number at position %d'); 308 end 309 end 310 pos = pos + delta-1; 311 312%%------------------------------------------------------------------------- 313 314function val = parse_value(varargin) 315 global pos inStr len 316 317 if(isfield(varargin{1},'progressbar_')) 318 waitbar(pos/len,varargin{1}.progressbar_,'loading ...'); 319 end 320 321 switch(inStr(pos)) 322 case '"' 323 val = parseStr(varargin{:}); 324 return; 325 case '[' 326 val = parse_array(varargin{:}); 327 return; 328 case '{' 329 val = parse_object(varargin{:}); 330 return; 331 case {'-','0','1','2','3','4','5','6','7','8','9'} 332 val = parse_number(varargin{:}); 333 return; 334 case 't' 335 if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'true') 336 val = true; 337 pos = pos + 4; 338 return; 339 end 340 case 'f' 341 if pos+4 <= len && strcmpi(inStr(pos:pos+4), 'false') 342 val = false; 343 pos = pos + 5; 344 return; 345 end 346 case 'n' 347 if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'null') 348 val = []; 349 pos = pos + 4; 350 return; 351 end 352 end 353 error_pos('Value expected at position %d'); 354%%------------------------------------------------------------------------- 355 356function error_pos(msg) 357 global pos inStr len 358 poShow = max(min([pos-15 pos-1 pos pos+20],len),1); 359 if poShow(3) == poShow(2) 360 poShow(3:4) = poShow(2)+[0 -1]; % display nothing after 361 end 362 msg = [sprintf(msg, pos) ': ' ... 363 inStr(poShow(1):poShow(2)) '<error>' inStr(poShow(3):poShow(4)) ]; 364 error( ['JSONparser:invalidFormat: ' msg] ); 365 366%%------------------------------------------------------------------------- 367 368function str = valid_field(str) 369global isoct 370% From MATLAB doc: field names must begin with a letter, which may be 371% followed by any combination of letters, digits, and underscores. 372% Invalid characters will be converted to underscores, and the prefix 373% "x0x[Hex code]_" will be added if the first character is not a letter. 374 pos=regexp(str,'^[^A-Za-z]','once'); 375 if(~isempty(pos)) 376 if(~isoct) 377 str=regexprep(str,'^([^A-Za-z])','x0x${sprintf(''%X'',unicode2native($1))}_','once'); 378 else 379 str=sprintf('x0x%X_%s',char(str(1)),str(2:end)); 380 end 381 end 382 if(isempty(regexp(str,'[^0-9A-Za-z_]', 'once' ))) 383 return; 384 end 385 if(~isoct) 386 str=regexprep(str,'([^0-9A-Za-z_])','_0x${sprintf(''%X'',unicode2native($1))}_'); 387 else 388 pos=regexp(str,'[^0-9A-Za-z_]'); 389 if(isempty(pos)) 390 return; 391 end 392 str0=str; 393 pos0=[0 pos(:)' length(str)]; 394 str=''; 395 for i=1:length(pos) 396 str=[str str0(pos0(i)+1:pos(i)-1) sprintf('_0x%X_',str0(pos(i)))]; 397 end 398 if(pos(end)~=length(str)) 399 str=[str str0(pos0(end-1)+1:pos0(end))]; 400 end 401 end 402 %str(~isletter(str) & ~('0' <= str & str <= '9')) = '_'; 403 404%%------------------------------------------------------------------------- 405function endpos = matching_quote(str,pos) 406len=length(str); 407while(pos<len) 408 if(str(pos)=='"') 409 if(~(pos>1 && str(pos-1)=='\')) 410 endpos=pos; 411 return; 412 end 413 end 414 pos=pos+1; 415end 416error('unmatched quotation mark'); 417%%------------------------------------------------------------------------- 418function [endpos, e1l, e1r, maxlevel] = matching_bracket(str,pos) 419global arraytoken 420level=1; 421maxlevel=level; 422endpos=0; 423bpos=arraytoken(arraytoken>=pos); 424tokens=str(bpos); 425len=length(tokens); 426pos=1; 427e1l=[]; 428e1r=[]; 429while(pos<=len) 430 c=tokens(pos); 431 if(c==']') 432 level=level-1; 433 if(isempty(e1r)) 434 e1r=bpos(pos); 435 end 436 if(level==0) 437 endpos=bpos(pos); 438 return 439 end 440 end 441 if(c=='[') 442 if(isempty(e1l)) 443 e1l=bpos(pos); 444 end 445 level=level+1; 446 maxlevel=max(maxlevel,level); 447 end 448 if(c=='"') 449 pos=matching_quote(tokens,pos+1); 450 end 451 pos=pos+1; 452end 453if(endpos==0) 454 error('unmatched "]"'); 455end 456 457function opt=varargin2struct(varargin) 458% 459% opt=varargin2struct('param1',value1,'param2',value2,...) 460% or 461% opt=varargin2struct(...,optstruct,...) 462% 463% convert a series of input parameters into a structure 464% 465% input: 466% 'param', value: the input parameters should be pairs of a string and a value 467% optstruct: if a parameter is a struct, the fields will be merged to the output struct 468% 469% output: 470% opt: a struct where opt.param1=value1, opt.param2=value2 ... 471% 472 473len=length(varargin); 474opt=struct; 475if(len==0) return; end 476i=1; 477while(i<=len) 478 if(isstruct(varargin{i})) 479 opt=mergestruct(opt,varargin{i}); 480 elseif(ischar(varargin{i}) && i<len) 481 opt=setfield(opt,lower(varargin{i}),varargin{i+1}); 482 i=i+1; 483 else 484 error('input must be in the form of ...,''name'',value,... pairs or structs'); 485 end 486 i=i+1; 487end 488function val=jsonopt(key,default,varargin) 489% 490% val=jsonopt(key,default,optstruct) 491% 492% setting options based on a struct. The struct can be produced 493% by varargin2struct from a list of 'param','value' pairs 494% 495% authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu) 496% 497% $Id: loadjson.m 371 2012-06-20 12:43:06Z fangq $ 498% 499% input: 500% key: a string with which one look up a value from a struct 501% default: if the key does not exist, return default 502% optstruct: a struct where each sub-field is a key 503% 504% output: 505% val: if key exists, val=optstruct.key; otherwise val=default 506% 507 508val=default; 509if(nargin<=2) return; end 510opt=varargin{1}; 511if(isstruct(opt)) 512 if(isfield(opt,key)) 513 val=getfield(opt,key); 514 elseif(isfield(opt,lower(key))) 515 val=getfield(opt,lower(key)); 516 end 517end 518 519function s=mergestruct(s1,s2) 520% 521% s=mergestruct(s1,s2) 522% 523% merge two struct objects into one 524% 525% authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu) 526% date: 2012/12/22 527% 528% input: 529% s1,s2: a struct object, s1 and s2 can not be arrays 530% 531% output: 532% s: the merged struct object. fields in s1 and s2 will be combined in s. 533 534if(~isstruct(s1) || ~isstruct(s2)) 535 error('input parameters contain non-struct'); 536end 537if(length(s1)>1 || length(s2)>1) 538 error('can not merge struct arrays'); 539end 540fn=fieldnames(s2); 541s=s1; 542for i=1:length(fn) 543 s=setfield(s,fn{i},getfield(s2,fn{i})); 544end 545function newdata=struct2jdata(data,varargin) 546% 547% newdata=struct2jdata(data,opt,...) 548% 549% convert a JData object (in the form of a struct array) into an array 550% 551% authors:Qianqian Fang (fangq<at> nmr.mgh.harvard.edu) 552% 553% input: 554% data: a struct array. If data contains JData keywords in the first 555% level children, these fields are parsed and regrouped into a 556% data object (arrays, trees, graphs etc) based on JData 557% specification. The JData keywords are 558% "_ArrayType_", "_ArraySize_", "_ArrayData_" 559% "_ArrayIsSparse_", "_ArrayIsComplex_" 560% opt: (optional) a list of 'Param',value pairs for additional options 561% The supported options include 562% 'Recursive', if set to 1, will apply the conversion to 563% every child; 0 to disable 564% 565% output: 566% newdata: the covnerted data if the input data does contain a JData 567% structure; otherwise, the same as the input. 568% 569% examples: 570% obj=struct('_ArrayType_','double','_ArraySize_',[2 3], 571% '_ArrayIsSparse_',1 ,'_ArrayData_',null); 572% ubjdata=struct2jdata(obj); 573 574fn=fieldnames(data); 575newdata=data; 576len=length(data); 577if(jsonopt('Recursive',0,varargin{:})==1) 578 for i=1:length(fn) % depth-first 579 for j=1:len 580 if(isstruct(getfield(data(j),fn{i}))) 581 newdata(j)=setfield(newdata(j),fn{i},jstruct2array(getfield(data(j),fn{i}))); 582 end 583 end 584 end 585end 586if(~isempty(strmatch('x0x5F_ArrayType_',fn)) && ~isempty(strmatch('x0x5F_ArrayData_',fn))) 587 newdata=cell(len,1); 588 for j=1:len 589 ndata=cast(data(j).x0x5F_ArrayData_,data(j).x0x5F_ArrayType_); 590 iscpx=0; 591 if(~isempty(strmatch('x0x5F_ArrayIsComplex_',fn))) 592 if(data(j).x0x5F_ArrayIsComplex_) 593 iscpx=1; 594 end 595 end 596 if(~isempty(strmatch('x0x5F_ArrayIsSparse_',fn))) 597 if(data(j).x0x5F_ArrayIsSparse_) 598 if(~isempty(strmatch('x0x5F_ArraySize_',fn))) 599 dim=double(data(j).x0x5F_ArraySize_); 600 if(iscpx && size(ndata,2)==4-any(dim==1)) 601 ndata(:,end-1)=complex(ndata(:,end-1),ndata(:,end)); 602 end 603 if isempty(ndata) 604 % All-zeros sparse 605 ndata=sparse(dim(1),prod(dim(2:end))); 606 elseif dim(1)==1 607 % Sparse row vector 608 ndata=sparse(1,ndata(:,1),ndata(:,2),dim(1),prod(dim(2:end))); 609 elseif dim(2)==1 610 % Sparse column vector 611 ndata=sparse(ndata(:,1),1,ndata(:,2),dim(1),prod(dim(2:end))); 612 else 613 % Generic sparse array. 614 ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3),dim(1),prod(dim(2:end))); 615 end 616 else 617 if(iscpx && size(ndata,2)==4) 618 ndata(:,3)=complex(ndata(:,3),ndata(:,4)); 619 end 620 ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3)); 621 end 622 end 623 elseif(~isempty(strmatch('x0x5F_ArraySize_',fn))) 624 if(iscpx && size(ndata,2)==2) 625 ndata=complex(ndata(:,1),ndata(:,2)); 626 end 627 ndata=reshape(ndata(:),data(j).x0x5F_ArraySize_); 628 end 629 newdata{j}=ndata; 630 end 631 if(len==1) 632 newdata=newdata{1}; 633 end 634end 635