123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- function varargout = spm_jsonwrite(varargin)
- % Serialize a JSON (JavaScript Object Notation) structure
- % FORMAT spm_jsonwrite(filename,json)
- % filename - JSON filename
- % json - JSON structure
- %
- % FORMAT S = spm_jsonwrite(json)
- % json - JSON structure
- % S - serialized JSON structure (string)
- %
- % FORMAT [...] = spm_jsonwrite(...,opts)
- % opts - structure of optional parameters:
- % indent: string to use for indentation [Default: '']
- % replacementStyle: string to control how non-alphanumeric
- % characters are replaced [Default: 'underscore']
- % convertinfandnan: encode NaN, Inf and -Inf as "null"
- % [Default: true]
- %
- % References:
- % JSON Standard: http://www.json.org/
- %__________________________________________________________________________
- % Copyright (C) 2015-2018 Wellcome Trust Centre for Neuroimaging
- % Guillaume Flandin
- % $Id: spm_jsonwrite.m 7478 2018-11-08 14:51:54Z guillaume $
- %-Input parameters
- %--------------------------------------------------------------------------
- opts = struct(...
- 'indent','',...
- 'replacementstyle','underscore',...
- 'convertinfandnan',true);
- opt = struct([]);
- if nargin > 1
- if ischar(varargin{1})
- filename = varargin{1};
- json = varargin{2};
- else
- filename = '';
- json = varargin{1};
- opt = varargin{2};
- end
- if nargin > 2
- opt = varargin{3};
- end
- else
- filename = '';
- json = varargin{1};
- end
- fn = fieldnames(opt);
- for i=1:numel(fn)
- if ~isfield(opts,lower(fn{i})), warning('Unknown option "%s".',fn{i}); end
- opts.(lower(fn{i})) = opt.(fn{i});
- end
- optregistry(opts);
- %-JSON serialization
- %--------------------------------------------------------------------------
- fmt('init',sprintf(opts.indent));
- S = jsonwrite_var(json,~isempty(opts.indent));
- %-Output
- %--------------------------------------------------------------------------
- if isempty(filename)
- varargout = { S };
- else
- fid = fopen(filename,'wt');
- if fid == -1
- error('Unable to open file "%s" for writing.',filename);
- end
- fprintf(fid,'%s',S);
- fclose(fid);
- end
- %==========================================================================
- function S = jsonwrite_var(json,tab)
- if nargin < 2, tab = ''; end
- if isstruct(json) || isa(json,'containers.Map')
- S = jsonwrite_struct(json,tab);
- elseif iscell(json)
- S = jsonwrite_cell(json,tab);
- elseif ischar(json)
- if size(json,1) <= 1
- S = jsonwrite_char(json);
- else
- S = jsonwrite_cell(cellstr(json),tab);
- end
- elseif isnumeric(json) || islogical(json)
- S = jsonwrite_numeric(json);
- elseif isa(json,'string')
- if numel(json) == 1
- if ismissing(json)
- S = 'null';
- else
- S = jsonwrite_char(char(json));
- end
- else
- json = arrayfun(@(x)x,json,'UniformOutput',false);
- json(cellfun(@(x) ismissing(x),json)) = {'null'};
- idx = find(size(json)~=1);
- if numel(idx) == 1 % vector
- S = jsonwrite_cell(json,tab);
- else % array
- S = jsonwrite_cell(num2cell(json,setdiff(1:ndims(json),idx(1))),tab);
- end
- end
- elseif isa(json,'datetime') || isa(json,'categorical')
- S = jsonwrite_var(string(json));
- elseif isa(json,'table')
- S = struct;
- s = size(json);
- vn = json.Properties.VariableNames;
- for i=1:s(1)
- for j=1:s(2)
- if iscell(json{i,j})
- S(i).(vn{j}) = json{i,j}{1};
- else
- S(i).(vn{j}) = json{i,j};
- end
- end
- end
- S = jsonwrite_struct(S,tab);
- else
- if numel(json) ~= 1
- json = arrayfun(@(x)x,json,'UniformOutput',false);
- S = jsonwrite_cell(json,tab);
- else
- p = properties(json);
- if isempty(p), p = fieldnames(json); end % for pre-classdef
- s = struct;
- for i=1:numel(p)
- s.(p{i}) = json.(p{i});
- end
- S = jsonwrite_struct(s,tab);
- %error('Class "%s" is not supported.',class(json));
- end
- end
- %==========================================================================
- function S = jsonwrite_struct(json,tab)
- if numel(json) == 1
- if isstruct(json), fn = fieldnames(json); else fn = keys(json); end
- S = ['{' fmt('\n',tab)];
- for i=1:numel(fn)
- key = fn{i};
- if strcmp(optregistry('replacementStyle'),'hex')
- key = regexprep(key,...
- '^x0x([0-9a-fA-F]{2})', '${native2unicode(hex2dec($1))}');
- key = regexprep(key,...
- '0x([0-9a-fA-F]{2})', '${native2unicode(hex2dec($1))}');
- end
- if isstruct(json), val = json.(fn{i}); else val = json(fn{i}); end
- S = [S fmt(tab) jsonwrite_char(key) ':' fmt(' ',tab) ...
- jsonwrite_var(val,tab+1)];
- if i ~= numel(fn), S = [S ',']; end
- S = [S fmt('\n',tab)];
- end
- S = [S fmt(tab-1) '}'];
- else
- S = jsonwrite_cell(arrayfun(@(x) {x},json),tab);
- end
- %==========================================================================
- function S = jsonwrite_cell(json,tab)
- if numel(json) == 0 ...
- || (numel(json) == 1 && iscellstr(json)) ...
- || all(all(cellfun(@isnumeric,json))) ...
- || all(all(cellfun(@islogical,json)))
- tab = '';
- end
- S = ['[' fmt('\n',tab)];
- for i=1:numel(json)
- S = [S fmt(tab) jsonwrite_var(json{i},tab+1)];
- if i ~= numel(json), S = [S ',']; end
- S = [S fmt('\n',tab)];
- end
- S = [S fmt(tab-1) ']'];
- %==========================================================================
- function S = jsonwrite_char(json)
- % any-Unicode-character-except-"-or-\-or-control-character
- % \" \\ \/ \b \f \n \r \t \u four-hex-digits
- json = strrep(json,'\','\\');
- json = strrep(json,'"','\"');
- %json = strrep(json,'/','\/');
- json = strrep(json,sprintf('\b'),'\b');
- json = strrep(json,sprintf('\f'),'\f');
- json = strrep(json,sprintf('\n'),'\n');
- json = strrep(json,sprintf('\r'),'\r');
- json = strrep(json,sprintf('\t'),'\t');
- S = ['"' json '"'];
- %==========================================================================
- function S = jsonwrite_numeric(json)
- if any(imag(json(:)))
- error('Complex numbers not supported.');
- end
- if numel(json) == 0
- S = jsonwrite_cell({});
- return;
- elseif numel(json) > 1
- idx = find(size(json)~=1);
- if numel(idx) == 1 % vector
- if any(islogical(json)) || any(~isfinite(json))
- S = jsonwrite_cell(num2cell(json),'');
- else
- S = ['[' sprintf('%23.16g,',json) ']']; % eq to num2str(json,16)
- S(end-1) = ''; % remove last ","
- S(S==' ') = [];
- end
- else % array
- S = jsonwrite_cell(num2cell(json,setdiff(1:ndims(json),idx(1))),'');
- end
- return;
- end
- if islogical(json)
- if json, S = 'true'; else S = 'false'; end
- elseif ~isfinite(json)
- if optregistry('convertinfandnan')
- S = 'null';
- else
- if isnan(json)
- S = 'NaN';
- elseif json > 0
- S = 'Infinity';
- else
- S = '-Infinity';
- end
- end
- else
- S = num2str(json,16);
- end
- %==========================================================================
- function b = fmt(varargin)
- persistent tab;
- if nargin == 2 && isequal(varargin{1},'init')
- tab = varargin{2};
- end
- b = '';
- if nargin == 1
- if varargin{1} > 0, b = repmat(tab,1,varargin{1}); end
- elseif nargin == 2
- if ~isempty(tab) && ~isempty(varargin{2}), b = sprintf(varargin{1}); end
- end
- %==========================================================================
- function val = optregistry(opts)
- persistent options
- if isstruct(opts)
- options = opts;
- else
- val = options.(lower(opts));
- end
|