Objectively 1.0.0
Ultra-lightweight object oriented framework for GNU C.
JSONSerialization.c
Go to the documentation of this file.
1/*
2 * Objectively: Ultra-lightweight object oriented framework for GNU C.
3 * Copyright (C) 2014 Jay Dolan <jay@jaydolan.com>
4 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any damages
7 * arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software
15 * in a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source distribution.
22 */
23
24#include <assert.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "Boole.h"
29#include "JSONSerialization.h"
30#include "MutableData.h"
31#include "MutableDictionary.h"
32#include "MutableArray.h"
33#include "Null.h"
34#include "Number.h"
35#include "String.h"
36
37#define _Class _JSONSerialization
38
39#pragma mark - JSONSerialization
40
44typedef struct {
47 size_t depth;
49
50static void writeElement(JSONWriter *writer, const ident obj);
51
57static void writeNull(JSONWriter *writer, const Null *null) {
58
59 $(writer->data, appendBytes, (uint8_t *) "null", 4);
60}
61
67static void writeBoole(JSONWriter *writer, const Boole *boolean) {
68
69 if (boolean->value) {
70 $(writer->data, appendBytes, (uint8_t *) "true", 4);
71 } else {
72 $(writer->data, appendBytes, (uint8_t *) "false", 5);
73 }
74}
75
81static void writeString(JSONWriter *writer, const String *string) {
82
83 $(writer->data, appendBytes, (uint8_t *) "\"", 1);
84 $(writer->data, appendBytes, (uint8_t *) string->chars, string->length);
85 $(writer->data, appendBytes, (uint8_t *) "\"", 1);
86}
87
93static void writeNumber(JSONWriter *writer, const Number *number) {
94
95 String *string = $(alloc(String), initWithFormat, "%g", number->value);
96
97 $(writer->data, appendBytes, (uint8_t *) string->chars, string->length);
98
99 release(string);
100}
101
107static void writeLabel(JSONWriter *writer, const String *label) {
108
109 writeString(writer, label);
110 $(writer->data, appendBytes, (uint8_t *) ": ", 2);
111}
112
117static void writePretty(JSONWriter *writer) {
118
119 if (writer->options & JSON_WRITE_PRETTY) {
120 $(writer->data, appendBytes, (uint8_t *) "\n", 1);
121 for (size_t i = 0; i < writer->depth; i++) {
122 $(writer->data, appendBytes, (uint8_t *) " ", 2);
123 }
124 }
125}
126
132static void writeObject(JSONWriter *writer, const Dictionary *object) {
133
134 $(writer->data, appendBytes, (uint8_t * ) "{", 1);
135 writer->depth++;
136
137 Array *keys = $(object, allKeys);
138 if (writer->options & JSON_WRITE_SORTED) {
139 Array *sorted = $(keys, sortedArray, StringCompare);
140 release(keys);
141 keys = sorted;
142 }
143
144 for (size_t i = 0; i < keys->count; i++) {
145
146 writePretty(writer);
147
148 const ident key = $(keys, objectAtIndex, i);
149 writeLabel(writer, (String *) key);
150
151 const ident obj = $(object, objectForKey, key);
152 writeElement(writer, obj);
153
154 if (i < keys->count - 1) {
155 $(writer->data, appendBytes, (uint8_t *) ",", 1);
156 }
157 }
158
159 release(keys);
160
161 writer->depth--;
162 writePretty(writer);
163
164 $(writer->data, appendBytes, (uint8_t * ) "}", 1);
165}
166
172static void writeArray(JSONWriter *writer, const Array *array) {
173
174 $(writer->data, appendBytes, (uint8_t * ) "[", 1);
175 writer->depth++;
176
177 for (size_t i = 0; i < array->count; i++) {
178
179 writePretty(writer);
180
181 writeElement(writer, $(array, objectAtIndex, i));
182
183 if (i < array->count - 1) {
184 $(writer->data, appendBytes, (uint8_t *) ",", 1);
185 }
186 }
187
188 writer->depth--;
189 writePretty(writer);
190
191 $(writer->data, appendBytes, (uint8_t * ) "]", 1);
192}
193
199static void writeElement(JSONWriter *writer, const ident obj) {
200
201 const Object *object = cast(Object, obj);
202 if (object) {
203 if ($(object, isKindOfClass, _Dictionary())) {
204 writeObject(writer, (const Dictionary *) object);
205 } else if ($(object, isKindOfClass, _Array())) {
206 writeArray(writer, (const Array *) object);
207 } else if ($(object, isKindOfClass, _String())) {
208 writeString(writer, (const String *) object);
209 } else if ($(object, isKindOfClass, _Number())) {
210 writeNumber(writer, (const Number *) object);
211 } else if ($(object, isKindOfClass, _Boole())) {
212 writeBoole(writer, (const Boole *) object);
213 } else if ($(object, isKindOfClass, _Null())) {
214 writeNull(writer, (const Null *) object);
215 }
216 }
217}
218
223static Data *dataFromObject(const ident obj, int options) {
224
225 if (obj) {
226 JSONWriter writer = {
227 .data = $(alloc(MutableData), init),
228 .options = options
229 };
230
231 writeObject(&writer, obj);
232
233 return (Data *) writer.data;
234 }
235
236 return NULL;
237}
238
242typedef struct {
243 const Data *data;
245 uint8_t *b;
246} JSONReader;
247
248static ident readElement(JSONReader *reader);
249
254static int readByte(JSONReader *reader) {
255
256 if (reader->b) {
257 if (reader->b - reader->data->bytes < reader->data->length) {
258 return (int) *(++(reader->b));
259 }
260 } else {
261 if (reader->data->bytes) {
262 return (int) *(reader->b = reader->data->bytes);
263 }
264 }
265
266 return -1;
267}
268
275static int readByteUntil(JSONReader *reader, const char *stop) {
276
277 int b;
278
279 while (true) {
280 b = readByte(reader);
281 if (b == -1 || strchr(stop, b)) {
282 break;
283 }
284 }
285
286 return b;
287}
288
294static void consumeBytes(JSONReader *reader, const char *bytes) {
295
296 for (size_t i = 1; i < strlen(bytes); i++) {
297 int b = readByte(reader);
298 assert(b == bytes[i]);
299 }
300}
301
307static String *readString(JSONReader *reader) {
308
309 uint8_t *bytes = reader->b;
310
311 const int b = readByteUntil(reader, "\"");
312 assert(b == '"');
313
314 const size_t length = reader->b - bytes - 1;
315 return $$(String, stringWithBytes, bytes + 1, length, STRING_ENCODING_UTF8);
316}
317
323static Number *readNumber(JSONReader *reader) {
324
325 uint8_t *bytes = reader->b;
326
327 uint8_t *end;
328 double d = strtod((char *) bytes, (char **) &end);
329
330 assert(end > bytes);
331 reader->b = end - 1;
332
333 return $$(Number, numberWithValue, d);
334}
335
341static _Bool *readBoole(JSONReader *reader) {
342
343 Boole *boolean = NULL;
344
345 switch (*reader->b) {
346 case 't':
347 consumeBytes(reader, "true");
348 boolean = $$(Boole, True);
349 break;
350 case 'f':
351 consumeBytes(reader, "false");
352 boolean = $$(Boole, False);
353 break;
354 default:
355 assert(false);
356 }
357
358 return retain(boolean);
359}
360
366static Null *readNull(JSONReader *reader) {
367
368 consumeBytes(reader, "null");
369
370 Null *null = $$(Null, null);
371
372 return retain(null);
373}
374
380static String *readLabel(JSONReader *reader) {
381
382 const int b = readByteUntil(reader, "\"}");
383 if (b == '"') {
384 return readString(reader);
385 } if (b == '}') {
386 reader->b--;
387 }
388
389 return NULL;
390}
391
398
400
401 while (true) {
402
403 String *key = readLabel(reader);
404 if (key == NULL) {
405 const int b = readByteUntil(reader, "}");
406 assert(b == '}');
407 break;
408 }
409
410 const int b = readByteUntil(reader, ":");
411 assert(b == ':');
412
413 ident obj = readElement(reader);
414 assert(obj);
415
416 $(object, setObjectForKey, obj, key);
417
418 release(key);
419 release(obj);
420 }
421
422 return (Dictionary *) object;
423}
424
430static Array *readArray(JSONReader *reader) {
431
433
434 while (true) {
435
436 Object *obj = readElement(reader);
437 if (obj == NULL) {
438 const int b = readByteUntil(reader, "]");
439 assert(b == ']');
440 break;
441 }
442
443 $(array, addObject, obj);
444
445 release(obj);
446 }
447
448 return (Array *) array;
449}
450
456static ident readElement(JSONReader *reader) {
457
458 const int b = readByteUntil(reader, "{[\"tfn0123456789.-]}");
459 if (b == '{') {
460 return readObject(reader);
461 } else if (b == '[') {
462 return readArray(reader);
463 } else if (b == '\"') {
464 return readString(reader);
465 } else if (b == 't' || b == 'f') {
466 return readBoole(reader);
467 } else if (b == 'n') {
468 return readNull(reader);
469 } else if (b == '.' || b == '-' || isdigit(b)) {
470 return readNumber(reader);
471 } else if (b == ']' || b == '}') {
472 reader->b--;
473 }
474
475 return NULL;
476}
477
482static ident objectFromData(const Data *data, int options) {
483
484 if (data && data->length) {
485 JSONReader reader = {
486 .data = data,
487 .options = options
488 };
489
490 return readElement(&reader);
491 }
492
493 return NULL;
494}
495
496#pragma mark - Class lifecycle
497
501static void initialize(Class *clazz) {
502
503 ((JSONSerializationInterface *) clazz->interface)->dataFromObject = dataFromObject;
504 ((JSONSerializationInterface *) clazz->interface)->objectFromData = objectFromData;
505}
506
512 static Class *clazz;
513 static Once once;
514
515 do_once(&once, {
516 clazz = _initialize(&(const ClassDef) {
517 .name = "JSONSerialization",
518 .superclass = _Object(),
519 .instanceSize = sizeof(JSONSerialization),
520 .interfaceOffset = offsetof(JSONSerialization, interface),
521 .interfaceSize = sizeof(JSONSerializationInterface),
523 });
524 });
525
526 return clazz;
527}
528
529#undef _Class
A wrapper for placing boolean primitives into collections, etc.
ident release(ident obj)
Atomically decrement the given Object's reference count. If the resulting reference count is 0,...
Definition: Class.c:196
Class * _initialize(const ClassDef *def)
Initializes the given Class.
Definition: Class.c:91
ident retain(ident obj)
Atomically increment the given Object's reference count.
Definition: Class.c:211
#define obj
#define alloc(type)
Allocate and initialize and instance of type.
Definition: Class.h:159
#define cast(type, obj)
Safely cast obj to type.
Definition: Class.h:165
static ident readElement(JSONReader *reader)
Reads an element from reader. An element is any valid JSON type.
static void writeLabel(JSONWriter *writer, const String *label)
Writes the label (field name) label to writer.
static int readByte(JSONReader *reader)
static void writeNull(JSONWriter *writer, const Null *null)
Writes null to writer.
static _Bool * readBoole(JSONReader *reader)
Reads a Boole from reader.
static void writeString(JSONWriter *writer, const String *string)
Writes string to writer.
static void writePretty(JSONWriter *writer)
Writes pretty formatting, if applicable, to writer.
static void writeNumber(JSONWriter *writer, const Number *number)
Writes number to writer.
static void writeBoole(JSONWriter *writer, const Boole *boolean)
Writes boolean to writer.
static int readByteUntil(JSONReader *reader, const char *stop)
Consume bytes from reader until a byte from stop is found.
static void writeArray(JSONWriter *writer, const Array *array)
Writes array to writer.
static Dictionary * readObject(JSONReader *reader)
Reads an object from reader. An object is a valid JSON structure.
static void writeObject(JSONWriter *writer, const Dictionary *object)
Writes object to writer.
static String * readString(JSONReader *reader)
Reads a String from reader.
static Array * readArray(JSONReader *reader)
Reads an array from `reader.
static void consumeBytes(JSONReader *reader, const char *bytes)
Consumes and verifies bytes from `reader.
static void initialize(Class *clazz)
static String * readLabel(JSONReader *reader)
Reads a label from reader.
static Number * readNumber(JSONReader *reader)
Reads a Number from reader.
static Null * readNull(JSONReader *reader)
Reads Null from Reader.
static void writeElement(JSONWriter *writer, const ident obj)
Writes the specified JSON element to writer.
JSON serialization and introspection.
@ JSON_WRITE_PRETTY
Enables pretty (indented) formatting of JSON output.
@ JSON_WRITE_SORTED
Enables lexicographic sorting of JSON output.
Mutable arrays.
Mutable data buffers.
Mutable key-value stores.
The Null sentinel.
A wrapper for placing numeric primitives into collections, etc.
Immutable UTF-8 strings.
@ STRING_ENCODING_UTF8
Definition: String.h:53
void * ident
The identity type, similar to Objective-C id.
Definition: Types.h:49
long Once
The Once type.
Definition: Once.h:37
#define do_once(once, block)
Executes the given block at most one time.
Definition: Once.h:43
Immutable arrays.
Definition: Array.h:56
Class * _Array(void)
The Array archetype.
Definition: Array.c:470
Array * sortedArray(const Array *self, Comparator comparator)
Definition: Array.c:420
ident objectAtIndex(const Array *self, int index)
size_t count
The count of elements.
Definition: Array.h:72
A wrapper for placing boolean primitives into collections, etc.
Definition: Boole.h:41
_Bool value
The backing _Bool.
Definition: Boole.h:57
Class * _Boole(void)
The Boole archetype.
Definition: Boole.c:125
Boole * False(void)
Definition: Boole.c:59
Boole * True(void)
Definition: Boole.c:77
ClassDefs are passed to _initialize via an archetype to initialize a Class.
Definition: Class.h:41
The runtime representation of a Class.
Definition: Class.h:95
ident interface
The interface of the Class.
Definition: Class.h:105
Condition * init(Condition *self)
Initializes this Condition.
Definition: Condition.c:67
Immutable data buffers.
Definition: Data.h:50
size_t length
The length of bytes.
Definition: Data.h:76
uint8_t * bytes
The bytes.
Definition: Data.h:66
DateFormatter * initWithFormat(DateFormatter *self, const char *fmt)
Initializes a DateFormatter with the specified format string.
Definition: DateFormatter.c:76
Immutable key-value stores.
Definition: Dictionary.h:60
Class * _Dictionary(void)
The Dictionary archetype.
Definition: Dictionary.c:451
Array * allKeys(const Dictionary *self)
Definition: Dictionary.c:169
ident objectForKey(const Dictionary *self, const ident key)
Definition: Dictionary.c:382
A reader for parsing JSON Data.
const Data * data
JSON serialization and introspection.
Class * _JSONSerialization(void)
The JSONSerialization archetype.
Data * dataFromObject(const ident obj, int options)
Serializes the given Object to JSON Data.
ident objectFromData(const Data *data, int options)
Parses an Object from the specified Data.
A writer for generating JSON Data.
MutableData * data
Mutable arrays.
Definition: MutableArray.h:40
void addObject(MutableArray *self, const ident obj)
Adds the specified Object to this MutableArray.
Definition: MutableArray.c:99
MutableArray * array(void)
Returns a new MutableArray.
Definition: MutableArray.c:153
Mutable data buffers.
Definition: MutableData.h:40
MutableData * data(void)
Returns a new MutableData.
Definition: MutableData.c:75
void appendBytes(MutableData *self, const uint8_t *bytes, size_t length)
Appends the given bytes to this Data.
Definition: MutableData.c:53
Mutable key-value stores.
void setObjectForKey(MutableDictionary *self, const ident obj, const ident key)
Sets a pair in this MutableDictionary.
MutableString * string(void)
Returns a new MutableString.
The Null sentinel.
Definition: Null.h:42
Null * null(void)
Definition: Null.c:48
Class * _Null(void)
The Null archetype.
Definition: Null.c:83
A wrapper for placing numeric primitives into collections, etc.
Definition: Number.h:41
Number * numberWithValue(double value)
Returns a new Number with the given value.
Definition: Number.c:159
double value
The backing value.
Definition: Number.h:57
Class * _Number(void)
The Number archetype.
Definition: Number.c:198
Object is the root Class of The Objectively Class hierarchy.
Definition: Object.h:46
Class * _Object(void)
The Object archetype.
Definition: Object.c:136
_Bool isKindOfClass(const Object *self, const Class *clazz)
Tests for Class hierarchy membership.
Definition: Object.c:101
Immutable UTF-8 strings.
Definition: String.h:69
char * chars
The backing null-terminated UTF-8 encoded character array.
Definition: String.h:85
size_t length
The length of the String in bytes.
Definition: String.h:90
String * stringWithBytes(const uint8_t *bytes, size_t length, StringEncoding encoding)
Returns a new String by decoding length of bytes to UTF-8.
Definition: String.c:478
Class * _String(void)
The String archetype.
Definition: String.c:654
OBJECTIVELY_EXPORT Order StringCompare(const ident a, const ident b)
A Comparator for sorting Strings.
Definition: String.c:721