1 /******************************************************************************** 2 * * 3 * J S O N R e a d e r & W r i t e r * 4 * * 5 ********************************************************************************* 6 * Copyright (C) 2013,2020 by Jeroen van der Zijp. All Rights Reserved. * 7 ********************************************************************************* 8 * This library is free software; you can redistribute it and/or modify * 9 * it under the terms of the GNU Lesser General Public License as published by * 10 * the Free Software Foundation; either version 3 of the License, or * 11 * (at your option) any later version. * 12 * * 13 * This library is distributed in the hope that it will be useful, * 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 * GNU Lesser General Public License for more details. * 17 * * 18 * You should have received a copy of the GNU Lesser General Public License * 19 * along with this program. If not, see <http://www.gnu.org/licenses/> * 20 ********************************************************************************/ 21 #ifndef FXJSON_H 22 #define FXJSON_H 23 24 namespace FX { 25 26 27 /** 28 * The FXJSON serializer loads or saves an FXVariant to a JSON text file. 29 * Since FXVariant can contain an arbitrarily complex data structure, this 30 * provides applications with a convenient way to load and save state information 31 * in a well-defined and human-readable file format. 32 * The base class implements serialization/deserialization to/from an external 33 * buffer. 34 * Subclasses FXJSONFile and FXJSONString serialize from/to strings and disk- 35 * based files, respectively. 36 * The new JSON5 standard may also be parsed, allowing for single- and multi-line 37 * nested comments to be embedded in the input. 38 * Syntax errors in the input cause the parser to return an error, and allow 39 * diagnosis of the problem and its location in the file by line number, column 40 * number, and byte-offset from the start of the file. 41 * When writing a json stream, the generated output may be formatter in different 42 * ways. The flow-mode controls the overall layout of the resulting text output; 43 * when flow is set to Stream, all output is generated with no formatting to 44 * improve human legibility. This is the most space-friendly format possible. 45 * If flow is set to Compact, a human readable, compact format, aiming to 46 * maximize the amount of information on each line is generated. 47 * When flow is set to Pretty, a nicely indented, but extremely airy output 48 * results, and the resulting document will contain many, many lines with 49 * little data. 50 * Numeric values are printed with configurable precision; (default=15 digits 51 * which results in minimal information loss for real numbers). 52 * For Pretty flow format, output may be indented in multiples of the indent 53 * level (default=2). Depending on flow setting, lines may be wrapped at a 54 * maximum number of columns (default=80). 55 * Output strings containing reserved characters may be escaped; for UTF8 56 * characters, there are 3 options for escaping. When escape mode is 0, UTF8 57 * characters are passed unescaped. In escape mode 1, UTF8 characters are 58 * escaped as \xXX, and in escape mode 2, UTF8 will be escaed using Unicode 59 * escape sequences \uXXXX or \uXXXX\uXXXX (two surrogate-pairs escape codes 60 * for code points exceeding 16 bits). 61 * The default setting is to allow UTF8 characters in the output, but be aware 62 * that such outputs need UTF8-capable viewer software to be rendered properly. 63 */ 64 class FXAPI FXJSON { 65 public: 66 enum Error { 67 ErrOK, /// No errors 68 ErrSave, /// Unable to save 69 ErrLoad, /// Unable to load 70 ErrToken, /// Illegal token 71 ErrColon, /// Expected colon ':' 72 ErrComma, /// Expected comma ',' 73 ErrBracket, /// Expected closing bracket 74 ErrBrace, /// Expected closing brace 75 ErrQuotes, /// Expected closing quotes 76 ErrQuote, /// Expected closing quote 77 ErrNumber, /// Numeric conversion 78 ErrEnd /// Unexpected end of file 79 }; 80 enum Flow { 81 Stream, /// Stream-of-consciousness output 82 Compact, /// Compact, human readable output (default) 83 Pretty /// Pretty printed, indented output 84 }; 85 enum Direction { 86 Stop = 0, /// Not active 87 Save = 1, /// Save to device 88 Load = 2 /// Load from device 89 }; 90 protected: 91 FXchar *begptr; // Text buffer begin ptr 92 FXchar *endptr; // Text buffer end ptr 93 FXchar *wptr; // Text buffer write ptr 94 FXchar *rptr; // Text buffer read ptr 95 FXchar *sptr; // Text buffer scan ptr 96 FXlong offset; // Position from start 97 FXint column; // Column number 98 FXint indent; // Indent level 99 FXint line; // Line number 100 Direction dir; // Direction 101 FXint token; // Token 102 FXint wrap; // Line wrap column 103 FXuchar flow; // Output flow 104 FXuchar prec; // Float precision 105 FXuchar fmt; // Float format 106 FXuchar esc; // Escape mode 107 FXuchar dent; // Indentation amount 108 private: 109 FXint next(); 110 FXbool need(FXival count); 111 FXbool emit(const FXchar* str,FXival count); 112 FXbool emit(FXchar ch,FXival count); 113 Error loadString(FXString& str); 114 Error loadIdent(FXString& str); 115 Error loadMap(FXVariant& var); 116 Error loadArray(FXVariant& var); 117 Error loadVariant(FXVariant& var); 118 Error saveString(const FXString& str); 119 Error saveIdent(const FXString& str); 120 Error saveMap(const FXVariant& var); 121 Error saveArray(const FXVariant& var); 122 Error saveVariant(const FXVariant& var); 123 private: 124 static const FXchar *const errors[]; 125 private: 126 FXJSON(const FXJSON&); 127 FXJSON &operator=(const FXJSON&); 128 public: 129 130 /** 131 * Initialize JSON serializer with no buffer. 132 */ 133 FXJSON(); 134 135 /** 136 * Construct JSON serializer with given buffer of size, and open it for 137 * direction d. 138 */ 139 FXJSON(FXchar* buffer,FXuval sz=8192,Direction d=Load); 140 141 /** 142 * Open JSON parse buffer with size and direction. 143 */ 144 FXbool open(FXchar* buffer=NULL,FXuval sz=8192,Direction d=Load); 145 146 /** 147 * Return direction in effect. 148 */ direction()149 Direction direction() const { return dir; } 150 151 /** 152 * Return size of parse buffer 153 */ size()154 FXuval size() const { return endptr-begptr; } 155 156 /** 157 * Return current line number. 158 */ getLine()159 FXint getLine() const { return line; } 160 161 /** 162 * Return current column number. 163 */ getColumn()164 FXint getColumn() const { return column; } 165 166 /** 167 * Return offset from begin of file. 168 */ getOffset()169 FXlong getOffset() const { return offset; } 170 171 /** 172 * Load a variant from JSON stream. 173 * Return false if stream wasn't opened for loading, or syntax error. 174 */ 175 Error load(FXVariant& variant); 176 177 /** 178 * Save a variant to JSON stream. 179 * Return false if stream wasn't opened for saving, or disk was full. 180 */ 181 Error save(const FXVariant& variant); 182 183 /// Returns error code for given error getError(Error err)184 static const FXchar* getError(Error err){ return errors[err]; } 185 186 /** 187 * Floating point output precision control. 188 * This controls the number of significant digits written to 189 * the output. The default is 15. 190 */ setNumericPrecision(FXint p)191 void setNumericPrecision(FXint p){ prec=p; } getNumericPrecision()192 FXint getNumericPrecision() const { return prec; } 193 194 /** 195 * Floating point output format control. 196 * The format mode is interpreted as follows: 197 * 198 * 0 No exponent. 199 * 1 Exponent. 200 * 2 Output exponent when required. 201 * 202 * The default mode is 2. 203 */ setNumericFormat(FXint f)204 void setNumericFormat(FXint f){ fmt=f; } getNumericFormat()205 FXint getNumericFormat() const { return fmt; } 206 207 /** 208 * Change output flow format (Stream, Compact, Pretty). 209 * Stream is the most compact, but pretty much unreadable by humans; it 210 * aims to be compact. 211 * Compact is very human-readable while at the same time using minimum number 212 * of lines to represent the output. 213 * Pretty will print one item on each line, with indentation. It is very easily 214 * readable but produces large numbers of text lines. 215 */ setOutputFlow(FXint f)216 void setOutputFlow(FXint f){ flow=f; } getOutputFlow()217 FXint getOutputFlow() const { return flow; } 218 219 /** 220 * Change indentation level for pretty print flow, the amount of 221 * indentation applied for each level. 222 */ setIndentation(FXint d)223 void setIndentation(FXint d){ dent=d; } getIndentation()224 FXint getIndentation() const { return dent; } 225 226 /** 227 * Change column at which lines are wrapped. 228 */ setLineWrap(FXint w)229 void setLineWrap(FXint w){ wrap=w; } getLineWrap()230 FXint getLineWrap() const { return wrap; } 231 232 /** 233 * Change string escape mode. 234 * The escape mode is interpreted as follows: 235 * 236 * 0 Don't escape unicode in strings; 237 * 1 Escape unicode as \xXX 238 * 2 Escape unicode as \uXXXX or \uXXXX\uXXXX. 239 * 240 * Default is to escape control characters only. 241 */ setEscapeMode(FXint e)242 void setEscapeMode(FXint e){ esc=e; } getEscapeMode()243 FXint getEscapeMode() const { return esc; } 244 245 /** 246 * Read at least count bytes into buffer; return bytes available, or -1 for error. 247 */ 248 virtual FXival fill(FXival count); 249 250 /** 251 * Write at least count bytes from buffer; return space available, or -1 for error. 252 */ 253 virtual FXival flush(FXival count); 254 255 /** 256 * Close stream and delete buffer, if owned. 257 */ 258 FXbool close(); 259 260 /** 261 * Close JSON stream and clean up. 262 */ 263 virtual ~FXJSON(); 264 }; 265 266 } 267 268 #endif 269