SAVEJSON JSON encoder and emitter. Syntax: JSON = SAVEJSON(OBJECT) SAVEJSON(OBJECT, FILENAME) Description: JSON = SAVEJSON(OBJECT) serializes the value of OBJECT in string JSON encoded in Javascript Object Notation format (JSON). SAVEJSON(OBJECT, FILENAME) performs the same conversion but prints it to the file named by string FILENAME. Notes: This function is inspired by a previous function by Tomeu Garau with the same name. He is the true glider man. Main changes are: - Added support for string output. - Added support for automatic creation of target directory if needed. - Changed NaN mapping from string 'NaN' to value null. - Changed character array mapping: only row char vectors map to strings and control characters are escaped properly. There is no one-to-one map between MATLAB/Octave values and JSON values. The conversion is done according to this rules: - Boolean scalars map to corresponding boolean literals (true or false). - Numeric scalars map to corresponding numeric literals, except from NaN which maps to null (because it is not a valid JSON value). - Structs map to objects with field names as keys and field values as values mapped according to this same set of rules. - Strings (character row vectors) are converted to string literals escaping characters according to section 2.5 of RFC in references. - Other arrays (either character arrays, numeric arrays, struct arrays or cell arrays) map to flat arrays in column major order. Dimensionality and shape information is lost. References: Crockford, D.; 2006: The application/json Media Type for JavaScript Object Notation (JSON). <http://www.ietf.org/rfc/rfc4627.txt> Examples: % Encode to string: json = savejson([]) json = savejson({}) json = savejson(struct()) json = savejson(25) json = savejson(NaN) json = savejson(true) json = savejson(false) json = savejson(rand(1,10)) json = savejson(... {sprintf(['escape this:' ... ' \b (backspace) \f (form feed) \n (line feed) \t (tab)' ... ' \r (carriage return) \x001A (escaped unicode character)' ... ' " (quotation mark) \\ (backslash)'])}) json = savejson(struct('field1', {'a', 'b', 'c'}, ... 'field2', {[1 2], [3 5], [4 pi]})) % Encode to file: json = savejson(rand(1,10), 'json/example.json') See also: LOADJSON FOPEN FWRITE FCLOSE Authors: Joan Pau Beltran <joanpau.beltran@socib.cat>
0001 function json = savejson(object, varargin) 0002 %SAVEJSON JSON encoder and emitter. 0003 % 0004 % Syntax: 0005 % JSON = SAVEJSON(OBJECT) 0006 % SAVEJSON(OBJECT, FILENAME) 0007 % 0008 % Description: 0009 % JSON = SAVEJSON(OBJECT) serializes the value of OBJECT in string JSON 0010 % encoded in Javascript Object Notation format (JSON). 0011 % 0012 % SAVEJSON(OBJECT, FILENAME) performs the same conversion but prints 0013 % it to the file named by string FILENAME. 0014 % 0015 % Notes: 0016 % This function is inspired by a previous function by Tomeu Garau with the 0017 % same name. He is the true glider man. Main changes are: 0018 % - Added support for string output. 0019 % - Added support for automatic creation of target directory if needed. 0020 % - Changed NaN mapping from string 'NaN' to value null. 0021 % - Changed character array mapping: only row char vectors map to strings 0022 % and control characters are escaped properly. 0023 % 0024 % There is no one-to-one map between MATLAB/Octave values and JSON values. 0025 % The conversion is done according to this rules: 0026 % - Boolean scalars map to corresponding boolean literals (true or false). 0027 % - Numeric scalars map to corresponding numeric literals, except from NaN 0028 % which maps to null (because it is not a valid JSON value). 0029 % - Structs map to objects with field names as keys and field values as 0030 % values mapped according to this same set of rules. 0031 % - Strings (character row vectors) are converted to string literals 0032 % escaping characters according to section 2.5 of RFC in references. 0033 % - Other arrays (either character arrays, numeric arrays, struct arrays 0034 % or cell arrays) map to flat arrays in column major order. 0035 % Dimensionality and shape information is lost. 0036 % 0037 % References: 0038 % Crockford, D.; 2006: 0039 % The application/json Media Type for JavaScript Object Notation (JSON). 0040 % <http://www.ietf.org/rfc/rfc4627.txt> 0041 % 0042 % Examples: 0043 % % Encode to string: 0044 % json = savejson([]) 0045 % json = savejson({}) 0046 % json = savejson(struct()) 0047 % json = savejson(25) 0048 % json = savejson(NaN) 0049 % json = savejson(true) 0050 % json = savejson(false) 0051 % json = savejson(rand(1,10)) 0052 % json = savejson(... 0053 % {sprintf(['escape this:' ... 0054 % ' \b (backspace) \f (form feed) \n (line feed) \t (tab)' ... 0055 % ' \r (carriage return) \x001A (escaped unicode character)' ... 0056 % ' " (quotation mark) \\ (backslash)'])}) 0057 % json = savejson(struct('field1', {'a', 'b', 'c'}, ... 0058 % 'field2', {[1 2], [3 5], [4 pi]})) 0059 % % Encode to file: 0060 % json = savejson(rand(1,10), 'json/example.json') 0061 % 0062 % See also: 0063 % LOADJSON 0064 % FOPEN 0065 % FWRITE 0066 % FCLOSE 0067 % 0068 % Authors: 0069 % Joan Pau Beltran <joanpau.beltran@socib.cat> 0070 0071 % Copyright (C) 2013-2016 0072 % ICTS SOCIB - Servei d'observacio i prediccio costaner de les Illes Balears 0073 % <http://www.socib.es> 0074 % 0075 % This program is free software: you can redistribute it and/or modify 0076 % it under the terms of the GNU General Public License as published by 0077 % the Free Software Foundation, either version 3 of the License, or 0078 % (at your option) any later version. 0079 % 0080 % This program is distributed in the hope that it will be useful, 0081 % but WITHOUT ANY WARRANTY; without even the implied warranty of 0082 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0083 % GNU General Public License for more details. 0084 % 0085 % You should have received a copy of the GNU General Public License 0086 % along with this program. If not, see <http://www.gnu.org/licenses/>. 0087 0088 error(nargchk(1, 2, nargin, 'struct')); 0089 0090 if isscalar(object) && isstruct(object) 0091 json = savejsonObject(object); 0092 else 0093 json = savejsonArray(object); 0094 end 0095 0096 if nargin > 1 0097 filename = varargin{1}; 0098 [filepath, ~, ~] = fileparts(filename); 0099 % MATLAB does not provide a proper way to check if a relative path points to 0100 % an existing directory (EXIST checks for existance in the whole load path). 0101 [status, attrout] = fileattrib(filepath); 0102 if ~status 0103 [success, message] = mkdir(filepath); 0104 if ~success 0105 error('glider_toolbox:savejson:DirectoryError', ... 0106 'Could not create directory %s: %s.', filepath, message); 0107 end 0108 elseif ~attrout.directory 0109 error('glider_toolbox:savejson:DirectoryError', ... 0110 'Not a directory %s.', filepath); 0111 end 0112 [fid, message] = fopen(filename, 'w'); 0113 if fid < 0 0114 error('glider_toolbox:savejson:FileError', ... 0115 'Could not open file for writing %s: %s.', filename, message); 0116 end 0117 count = fwrite(fid, json, 'char'); 0118 status = fclose(fid); 0119 if status < 0 0120 error('glider_toolbox:savejson:FileError', ... 0121 'Could not close file %s: %d characters written.', filename, count); 0122 end 0123 end 0124 0125 end 0126 0127 function json = savejsonObject(object) 0128 %SAVEJSONOBJECT Encode a scalar struct as a JSON object. 0129 keys = fieldnames(object); 0130 vals = struct2cell(structfun(@savejsonValue, object, 'UniformOutput', false)); 0131 pairs = [keys(:) vals(:)]'; 0132 switch size(pairs, 2) 0133 case 0 0134 json = '{}'; 0135 case 1 0136 json = ['{' sprintf('"%s":%s', pairs{:}) '}']; 0137 otherwise 0138 json = ['{' ... 0139 sprintf('"%s":%s,', pairs{:, 1:end-1}) ... 0140 sprintf('"%s":%s', pairs{:, end}) ... 0141 '}']; 0142 end 0143 end 0144 0145 function json = savejsonArray(object) 0146 %SAVEJSONARRAY Encode a cell array or array as a JSON array. 0147 if iscell(object) 0148 vals = cellfun(@savejsonValue, object, 'UniformOutput', false); 0149 else 0150 vals = arrayfun(@savejsonValue, object, 'UniformOutput', false); 0151 end 0152 switch numel(vals) 0153 case 0 0154 json = '[]'; 0155 case 1 0156 json = ['[' sprintf('%s', vals{:}) ']']; 0157 otherwise 0158 json = ['[' sprintf('%s,', vals{1:end-1}) sprintf('%s', vals{end}) ']']; 0159 end 0160 end 0161 0162 function json = savejsonValue(object) 0163 %SAVEJSONVALUE Encode an arbitrary value as a JSON value. 0164 if ischar(object) && isrow(object) 0165 json = savejsonString(object); 0166 elseif ~isscalar(object) || iscell(object) 0167 json = savejsonArray(object); 0168 elseif islogical(object) 0169 json = savejsonBoolean(object); 0170 elseif isnumeric(object) 0171 json = savejsonNumber(object); 0172 elseif isstruct(object) 0173 json = savejsonObject(object); 0174 end 0175 end 0176 0177 function json = savejsonBoolean(object) 0178 %SAVEJSONBOOLEAN Encode a scalar boolean value as a JSON boolean value. 0179 if object 0180 json = 'true'; 0181 else 0182 json = 'false'; 0183 end 0184 end 0185 0186 function json = savejsonNumber(object) 0187 %WRTIEJSONNUMBER Encode a scalar numeric value as a JSON number value. 0188 if isnan(object) 0189 json = 'null'; 0190 else 0191 json = sprintf('%G', object); 0192 end 0193 end 0194 0195 function json = savejsonString(object) 0196 %SAVEJSONSTRING Encode a row char vector value as a JSON string value. 0197 control_characters = arrayfun(@(b)(native2unicode(b, 'UTF-8')), ... 0198 uint8(0:31), 'UniformOutput', false); 0199 control_escapeseqs = arrayfun(@(b)(sprintf('\\u%04x', b)), ... 0200 uint8(0:31), 'UniformOutput', false); 0201 control_escapeseqs(1+[8 9 10 12 13]) = {'\b' '\t' '\n' '\f' '\r'}; 0202 json = [ ... 0203 '"' ... 0204 regexprep(object, ... 0205 regexptranslate('escape', [{'\' '"' } control_characters]), ... 0206 regexptranslate('escape', [{'\\' '\"'} control_escapeseqs])) ... 0207 '"' ]; 0208 end