]>
Commit | Line | Data |
---|---|---|
887d5eed A |
1 | /* |
2 | * Copyright (c) 2017 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | ||
23 | /* | |
24 | * Provides a stream-based API for generating JSON output | |
25 | * | |
26 | * Handles tedious tasks like worrying about comma placement (and avoiding trailing commas). | |
27 | * Assumes strings are already escaped (if necessary) and does no error checking (thus it | |
28 | * may produce invalid JSON when used improperly). | |
29 | * | |
30 | * As a convenience, when the provided `json` stream is NULL (i.e. it was never initialized | |
31 | * by `JSON_OPEN`) these APIs will do nothing. | |
32 | * | |
33 | * Example usage: | |
34 | * | |
35 | * JSON_t json = JSON_OPEN("/path/to/output.json") | |
36 | * JSON_OBJECT_BEGIN(json); // root object | |
37 | * | |
38 | * JSON_OBJECT_SET(json, version, %.1f, 1.0); | |
39 | * JSON_OBJECT_SET_BOOL(json, has_fruit, 1); | |
40 | * | |
41 | * // Note the required quotes for strings (formatted or not) | |
42 | * char *mystr = "hello"; | |
43 | * JSON_OBJECT_SET(json, formatted_string, "%s", mystr); | |
44 | * JSON_OBJECT_SET(json, literal_string, "my literal string"); | |
45 | * | |
46 | * JSON_KEY(json, fruit_array); | |
47 | * JSON_ARRAY_BEGIN(json); // fruit_array | |
48 | * JSON_ARRAY_APPEND(json, "my literal string"); | |
49 | * JSON_ARRAY_APPEND(json, "<0x%08llx>", 0xface); | |
50 | * JSON_ARRAY_APPEND(json, %d, 3); | |
51 | * JSON_ARRAY_END(json); // fruit_array | |
52 | * | |
53 | * JSON_OBJECT_END(json); // root object | |
54 | * JSON_CLOSE(json); | |
55 | */ | |
56 | ||
57 | #ifndef _JSON_H_ | |
58 | #define _JSON_H_ | |
59 | ||
60 | #include <stdio.h> | |
61 | #include <stdlib.h> | |
62 | #include <stdbool.h> | |
63 | ||
64 | #define _JSON_IF(json, code) \ | |
65 | if (json != NULL) { \ | |
66 | code; \ | |
67 | } | |
68 | #define _JSON_COMMA(json) \ | |
69 | if (json->require_comma) { \ | |
70 | fprintf(json->stream, ","); \ | |
71 | } | |
72 | ||
73 | struct _JSON { | |
74 | FILE* stream; | |
75 | bool require_comma; | |
76 | }; | |
77 | typedef struct _JSON * JSON_t; | |
78 | ||
79 | #pragma mark Open/Close | |
80 | /* Return a new JSON_t stream */ | |
81 | static inline JSON_t JSON_OPEN(const char *path) { | |
82 | JSON_t p = malloc(sizeof(struct _JSON)); | |
83 | p->stream = fopen(path, "w+"); | |
84 | p->require_comma = false; | |
85 | return p; | |
86 | } | |
87 | ||
88 | /* Close an existing JSON stream, removing trailing commas */ | |
89 | #define JSON_CLOSE(json) _JSON_IF(json, fclose(json->stream); free(json)) | |
90 | ||
91 | #pragma mark Keys/Values | |
92 | /* Output the `key` half of a key/value pair */ | |
93 | #define JSON_KEY(json, key) _JSON_IF(json, _JSON_COMMA(json); fprintf(json->stream, "\"" #key "\":"); json->require_comma = false) | |
94 | /* Output the `value` half of a key/value pair */ | |
95 | #define JSON_VALUE(json, format, ...) _JSON_IF(json, fprintf(json->stream, #format, ##__VA_ARGS__); json->require_comma = true) | |
96 | ||
97 | #define _JSON_BEGIN(json, character) _JSON_COMMA(json); fprintf(json->stream, #character); json->require_comma = false; | |
98 | #define _JSON_END(json, character) fprintf(json->stream, #character); json->require_comma = true; | |
99 | #define _JSON_BOOL(val) ( val ? "true" : "false" ) | |
100 | ||
101 | #pragma mark Objects | |
102 | /* Start a new JSON object */ | |
103 | #define JSON_OBJECT_BEGIN(json) _JSON_IF(json, _JSON_BEGIN(json, {)) | |
104 | /* Set a value in the current JSON object */ | |
105 | #define JSON_OBJECT_SET(json, key, format, ...) _JSON_IF(json, JSON_KEY(json, key); JSON_VALUE(json, format, ##__VA_ARGS__)) | |
106 | /* Set a boolean in the current JSON object */ | |
107 | #define JSON_OBJECT_SET_BOOL(json, key, value) JSON_OBJECT_SET(json, key, %s, _JSON_BOOL(value)) | |
108 | /* End the current JSON object */ | |
109 | #define JSON_OBJECT_END(json) _JSON_IF(json, _JSON_END(json, })) | |
110 | ||
111 | #pragma mark Arrays | |
112 | /* Start a new JSON array */ | |
113 | #define JSON_ARRAY_BEGIN(json) _JSON_IF(json, _JSON_BEGIN(json, [)) | |
114 | /* Append a value to the current JSON array */ | |
115 | #define JSON_ARRAY_APPEND(json, format, ...) _JSON_IF(json, _JSON_COMMA(json); JSON_VALUE(json, format, ##__VA_ARGS__)) | |
116 | /* End the current JSON array */ | |
117 | #define JSON_ARRAY_END(json) _JSON_IF(json, _JSON_END(json, ])) | |
118 | ||
119 | #endif /* _JSON_H_ */ |