├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTORS.md ├── LICENSE ├── Makefile ├── README.md ├── cJSON.c ├── cJSON.h ├── cJSONConfig.cmake.in ├── cJSONConfigVersion.cmake.in ├── cJSON_Utils.c ├── cJSON_Utils.h ├── libcjson.pc.in ├── libcjson_utils.pc.in ├── test.c ├── test_utils.c └── tests ├── test1 ├── test2 ├── test3 ├── test4 ├── test5 └── test6 /.gitignore: -------------------------------------------------------------------------------- 1 | .svn 2 | test 3 | *.o 4 | *.a 5 | *.so 6 | *.swp 7 | *.patch 8 | tags 9 | *.dylib 10 | build/ 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_LEGACY_CYGWIN_WIN32 0) 2 | cmake_minimum_required(VERSION 2.8) 3 | 4 | include(GNUInstallDirs) 5 | 6 | project(cJSON C) 7 | 8 | set(PROJECT_VERSION_MAJOR 1) 9 | set(PROJECT_VERSION_MINOR 0) 10 | set(PROJECT_VERSION_PATCH 1) 11 | set(CJSON_VERSION_SO 1) 12 | set(CJSON_UTILS_VERSION_SO 1) 13 | set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") 14 | 15 | option(ENABLE_CUSTOM_COMPILER_FLAGS ON) 16 | if (ENABLE_CUSTOM_COMPILER_FLAGS) 17 | if(("${CMAKE_C_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_C_COMPILER_ID}" MATCHES "Clang")) 18 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c89 -pedantic -Wall -Wextra -Werror -Wstrict-prototypes -Wwrite-strings") 19 | endif() 20 | endif() 21 | 22 | #variables for pkg-config 23 | set(prefix "${CMAKE_INSTALL_PREFIX}") 24 | set(libdir "${CMAKE_INSTALL_LIBDIR}") 25 | set(version "${PROJECT_VERSION}") 26 | set(includedir "${CMAKE_INSTALL_INCLUDEDIR}") 27 | 28 | option(BUILD_SHARED_LIBS "Build shared libraries" ON) 29 | option(ENABLE_TARGET_EXPORT "Enable exporting of CMake targets. Disable when it causes problems!" ON) 30 | 31 | #cJSON 32 | set(CJSON_LIB cjson) 33 | 34 | file(GLOB HEADERS cJSON.h) 35 | set(SOURCES cJSON.c) 36 | 37 | add_library("${CJSON_LIB}" "${HEADERS}" "${SOURCES}") 38 | if (NOT WIN32) 39 | target_link_libraries("${CJSON_LIB}" m) 40 | endif() 41 | 42 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libcjson.pc.in" 43 | "${CMAKE_CURRENT_BINARY_DIR}/libcjson.pc" @ONLY) 44 | 45 | install(FILES cJSON.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/cjson") 46 | install (FILES "${CMAKE_CURRENT_BINARY_DIR}/libcjson.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 47 | install(TARGETS "${CJSON_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}" EXPORT "${CJSON_LIB}") 48 | if(ENABLE_TARGET_EXPORT) 49 | # export library information for CMake projects 50 | install(EXPORT "${CJSON_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/cJSON") 51 | endif() 52 | 53 | set_target_properties("${CJSON_LIB}" 54 | PROPERTIES 55 | SOVERSION "${CJSON_VERSION_SO}" 56 | VERSION "${PROJECT_VERSION}") 57 | 58 | #cJSON_Utils 59 | option(ENABLE_CJSON_UTILS "Enable building the cJSON_Utils library." OFF) 60 | if(ENABLE_CJSON_UTILS) 61 | set(CJSON_UTILS_LIB cjson_utils) 62 | 63 | file(GLOB HEADERS_UTILS cJSON_Utils.h) 64 | set(SOURCES_UTILS cJSON_Utils.c) 65 | 66 | add_library("${CJSON_UTILS_LIB}" "${HEADERS_UTILS}" "${SOURCES_UTILS}") 67 | target_link_libraries("${CJSON_UTILS_LIB}" "${CJSON_LIB}") 68 | 69 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libcjson_utils.pc.in" 70 | "${CMAKE_CURRENT_BINARY_DIR}/libcjson_utils.pc" @ONLY) 71 | 72 | install(TARGETS "${CJSON_UTILS_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}" EXPORT "${CJSON_UTILS_LIB}") 73 | install(FILES cJSON_Utils.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/cjson") 74 | install (FILES "${CMAKE_CURRENT_BINARY_DIR}/libcjson_utils.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 75 | if(ENABLE_TARGET_EXPORT) 76 | # export library information for CMake projects 77 | install(EXPORT "${CJSON_UTILS_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/cJSON") 78 | endif() 79 | 80 | set_target_properties("${CJSON_UTILS_LIB}" 81 | PROPERTIES 82 | SOVERSION "${CJSON_UTILS_VERSION_SO}" 83 | VERSION "${PROJECT_VERSION}") 84 | endif() 85 | 86 | # create the other package config files 87 | configure_file( 88 | cJSONConfig.cmake.in 89 | ${PROJECT_BINARY_DIR}/cJSONConfig.cmake @ONLY) 90 | configure_file( 91 | cJSONConfigVersion.cmake.in 92 | ${PROJECT_BINARY_DIR}/cJSONConfigVersion.cmake @ONLY) 93 | 94 | # Install package config files 95 | install(FILES ${PROJECT_BINARY_DIR}/cJSONConfig.cmake 96 | ${PROJECT_BINARY_DIR}/cJSONConfigVersion.cmake 97 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/cJSON") 98 | 99 | option(ENABLE_CJSON_TEST "Enable building cJSON test" ON) 100 | if(ENABLE_CJSON_TEST) 101 | set(TEST_CJSON cJSON_test) 102 | add_executable("${TEST_CJSON}" test.c) 103 | target_link_libraries("${TEST_CJSON}" "${CJSON_LIB}") 104 | 105 | if(ENABLE_CJSON_UTILS) 106 | set(TEST_CJSON_UTILS cJSON_test_utils) 107 | add_executable("${TEST_CJSON_UTILS}" test_utils.c) 108 | target_link_libraries("${TEST_CJSON_UTILS}" "${CJSON_UTILS_LIB}") 109 | endif() 110 | endif() 111 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | Contributors 2 | ============ 3 | 4 | * [Ajay Bhargav](https://github.com/ajaybhargav) 5 | * [Anton Sergeev](https://github.com/anton-sergeev) 6 | * [Christian Schulze](https://github.com/ChristianSch) 7 | * [Dave Gamble](https://github.com/DaveGamble) 8 | * [dieyushi](https://github.com/dieyushi) 9 | * [Dongwen Huang (黄东文)](https://github.com/DongwenHuang) 10 | * Eswar Yaganti 11 | * [Evan Todd](https://github.com/etodd) 12 | * [Fabrice Fontaine](https://github.com/ffontaine) 13 | * Ian Mobley 14 | * Irwan Djadjadi 15 | * [IvanVoid](https://github.com/npi3pak) 16 | * [Jonathan Fether](https://github.com/jfether) 17 | * [Kevin Branigan](https://github.com/kbranigan) 18 | * [Linus Wallgren](https://github.com/ecksun) 19 | * [Max Bruckner](https://github.com/FSMaxB) 20 | * Mike Pontillo 21 | * Paulo Antonio Alvarez 22 | * [Rafael Leal Dias](https://github.com/rafaeldias) 23 | * [Rod Vagg](https://github.com/rvagg) 24 | * [Roland Meertens](https://github.com/rmeertens) 25 | * [Weston Schmidt](https://github.com/schmidtw) 26 | 27 | And probably more people on [SourceForge](https://sourceforge.net/p/cjson/bugs/search/?q=status%3Aclosed-rejected+or+status%3Aclosed-out-of-date+or+status%3Awont-fix+or+status%3Aclosed-fixed+or+status%3Aclosed&page=0) 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Dave Gamble 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CJSON_OBJ = cJSON.o 2 | UTILS_OBJ = cJSON_Utils.o 3 | CJSON_LIBNAME = libcjson 4 | UTILS_LIBNAME = libcjson_utils 5 | CJSON_TEST = cJSON_test 6 | UTILS_TEST = cJSON_test_utils 7 | 8 | LDLIBS = -lm 9 | 10 | LIBVERSION = 1.0.1 11 | CJSON_SOVERSION = 1 12 | UTILS_SOVERSION = 1 13 | 14 | PREFIX ?= /usr/local 15 | INCLUDE_PATH ?= include/cjson 16 | LIBRARY_PATH ?= lib 17 | 18 | INSTALL_INCLUDE_PATH = $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH) 19 | INSTALL_LIBRARY_PATH = $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH) 20 | 21 | INSTALL ?= cp -a 22 | 23 | R_CFLAGS = -fPIC -std=c89 -pedantic -Wall -Werror -Wstrict-prototypes -Wwrite-strings $(CFLAGS) 24 | 25 | uname := $(shell sh -c 'uname -s 2>/dev/null || echo false') 26 | 27 | #library file extensions 28 | SHARED = so 29 | STATIC = a 30 | 31 | ## create dynamic (shared) library on Darwin (base OS for MacOSX and IOS) 32 | ifeq (Darwin, $(uname)) 33 | SHARED = dylib 34 | endif 35 | 36 | #cJSON library names 37 | CJSON_SHARED = $(CJSON_LIBNAME).$(SHARED) 38 | CJSON_SHARED_VERSION = $(CJSON_LIBNAME).$(SHARED).$(LIBVERSION) 39 | CJSON_SHARED_SO = $(CJSON_LIBNAME).$(SHARED).$(CJSON_SOVERSION) 40 | CJSON_STATIC = $(CJSON_LIBNAME).$(STATIC) 41 | 42 | #cJSON_Utils library names 43 | UTILS_SHARED = $(UTILS_LIBNAME).$(SHARED) 44 | UTILS_SHARED_VERSION = $(UTILS_LIBNAME).$(SHARED).$(LIBVERSION) 45 | UTILS_SHARED_SO = $(UTILS_LIBNAME).$(SHARED).$(UTILS_SOVERSION) 46 | UTILS_STATIC = $(UTILS_LIBNAME).$(STATIC) 47 | 48 | SHARED_CMD = $(CC) -shared -o 49 | 50 | .PHONY: all shared static tests clean install 51 | 52 | all: shared static tests 53 | 54 | shared: $(CJSON_SHARED) $(UTILS_SHARED) 55 | 56 | static: $(CJSON_STATIC) $(UTILS_STATIC) 57 | 58 | tests: $(CJSON_TEST) $(UTILS_TEST) 59 | 60 | test: tests 61 | ./$(CJSON_TEST) 62 | ./$(UTILS_TEST) 63 | 64 | .c.o: 65 | $(CC) -ansi -pedantic -c $(R_CFLAGS) $< 66 | 67 | #tests 68 | #cJSON 69 | $(CJSON_TEST): cJSON.c cJSON.h test.c 70 | $(CC) $^ -o $@ $(LDLIBS) -I. 71 | #cJSON_Utils 72 | $(UTILS_TEST): cJSON.c cJSON.h test.c 73 | $(CC) $^ -o $@ $(LDLIBS) -I. 74 | 75 | #static libraries 76 | #cJSON 77 | $(CJSON_STATIC): $(CJSON_OBJ) 78 | $(AR) rcs $@ $< 79 | #cJSON_Utils 80 | $(UTILS_STATIC): $(UTILS_OBJ) 81 | $(AR) rcs $@ $< 82 | 83 | #shared libraries .so.1.0.0 84 | #cJSON 85 | $(CJSON_SHARED_VERSION): $(CJSON_OBJ) 86 | $(CC) -shared -o $@ $< $(LDFLAGS) 87 | #cJSON_Utils 88 | $(UTILS_SHARED_VERSION): $(UTILS_OBJ) 89 | $(CC) -shared -o $@ $< $(LDFLAGS) 90 | 91 | #objects 92 | #cJSON 93 | $(CJSON_OBJ): cJSON.c cJSON.h 94 | #cJSON_Utils 95 | $(UTILS_OBJ): cJSON_Utils.c cJSON_Utils.h 96 | 97 | 98 | #links .so -> .so.1 -> .so.1.0.0 99 | #cJSON 100 | $(CJSON_SHARED_SO): $(CJSON_SHARED_VERSION) 101 | ln -s $(CJSON_SHARED_VERSION) $(CJSON_SHARED_SO) 102 | $(CJSON_SHARED): $(CJSON_SHARED_SO) 103 | ln -s $(CJSON_SHARED_SO) $(CJSON_SHARED) 104 | #cJSON_Utils 105 | $(UTILS_SHARED_SO): $(UTILS_SHARED_VERSION) 106 | ln -s $(UTILS_SHARED_VERSION) $(UTILS_SHARED_SO) 107 | $(UTILS_SHARED): $(UTILS_SHARED_SO) 108 | ln -s $(UTILS_SHARED_SO) $(UTILS_SHARED) 109 | 110 | #install 111 | #cJSON 112 | install-cjson: 113 | mkdir -p $(INSTALL_LIBRARY_PATH) $(INSTALL_INCLUDE_PATH) 114 | $(INSTALL) cJSON.h $(INSTALL_INCLUDE_PATH) 115 | $(INSTALL) $(CJSON_SHARED) $(CJSON_SHARED_SO) $(CJSON_SHARED_VERSION) $(INSTALL_LIBRARY_PATH) 116 | #cJSON_Utils 117 | install-utils: install-cjson 118 | $(INSTALL) cJSON_Utils.h $(INSTALL_INCLUDE_PATH) 119 | $(INSTALL) $(UTILS_SHARED) $(UTILS_SHARED_SO) $(UTILS_SHARED_VERSION) $(INSTALL_LIBRARY_PATH) 120 | 121 | install: install-cjson install-utils 122 | 123 | #uninstall 124 | #cJSON 125 | uninstall-cjson: uninstall-utils 126 | $(RM) $(INSTALL_LIBRARY_PATH)/$(CJSON_SHARED) 127 | $(RM) $(INSTALL_LIBRARY_PATH)/$(CJSON_SHARED_VERSION) 128 | $(RM) $(INSTALL_LIBRARY_PATH)/$(CJSON_SHARED_SO) 129 | rmdir $(INSTALL_LIBRARY_PATH) 130 | $(RM) $(INSTALL_INCLUDE_PATH)/cJSON.h 131 | rmdir $(INSTALL_INCLUDE_PATH) 132 | #cJSON_Utils 133 | uninstall-utils: 134 | $(RM) $(INSTALL_LIBRARY_PATH)/$(UTILS_SHARED) 135 | $(RM) $(INSTALL_LIBRARY_PATH)/$(UTILS_SHARED_VERSION) 136 | $(RM) $(INSTALL_LIBRARY_PATH)/$(UTILS_SHARED_SO) 137 | $(RM) $(INSTALL_INCLUDE_PATH)/cJSON_Utils.h 138 | 139 | uninstall: uninstall-utils uninstall-cjson 140 | 141 | clean: 142 | $(RM) $(CJSON_OBJ) $(UTILS_OBJ) #delete object files 143 | $(RM) $(CJSON_SHARED) $(CJSON_SHARED_VERSION) $(CJSON_SHARED_SO) $(CJSON_STATIC) #delete cJSON 144 | $(RM) $(UTILS_SHARED) $(UTILS_SHARED_VERSION) $(UTILS_SHARED_SO) $(UTILS_STATIC) #delete cJSON_Utils 145 | $(RM) $(CJSON_TEST) $(UTILS_TEST) #delete tests 146 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [cJSON has moved](https://github.com/DaveGamble/cJSON) 2 | -------------------------------------------------------------------------------- /cJSON.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009 Dave Gamble 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* cJSON */ 24 | /* JSON parser in C. */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "cJSON.h" 34 | 35 | /* Determine the number of bits that an integer has using the preprocessor */ 36 | #if INT_MAX == 32767 37 | /* 16 bits */ 38 | #define INTEGER_SIZE 0x0010 39 | #elif INT_MAX == 2147483647 40 | /* 32 bits */ 41 | #define INTEGER_SIZE 0x0100 42 | #elif INT_MAX == 9223372036854775807 43 | /* 64 bits */ 44 | #define INTEGER_SIZE 0x1000 45 | #else 46 | #error "Failed to determine the size of an integer" 47 | #endif 48 | 49 | /* define our own boolean type */ 50 | typedef int bool; 51 | #define true ((bool)1) 52 | #define false ((bool)0) 53 | 54 | static const char *global_ep = NULL; 55 | 56 | const char *cJSON_GetErrorPtr(void) 57 | { 58 | return global_ep; 59 | } 60 | 61 | /* case insensitive strcmp */ 62 | static int cJSON_strcasecmp(const char *s1, const char *s2) 63 | { 64 | if (!s1) 65 | { 66 | return (s1 == s2) ? 0 : 1; /* both NULL? */ 67 | } 68 | if (!s2) 69 | { 70 | return 1; 71 | } 72 | for(; tolower(*(const unsigned char *)s1) == tolower(*(const unsigned char *)s2); ++s1, ++s2) 73 | { 74 | if (*s1 == '\0') 75 | { 76 | return 0; 77 | } 78 | } 79 | 80 | return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); 81 | } 82 | 83 | static void *(*cJSON_malloc)(size_t sz) = malloc; 84 | static void (*cJSON_free)(void *ptr) = free; 85 | 86 | static char* cJSON_strdup(const char* str) 87 | { 88 | size_t len = 0; 89 | char *copy = NULL; 90 | 91 | len = strlen(str) + 1; 92 | if (!(copy = (char*)cJSON_malloc(len))) 93 | { 94 | return NULL; 95 | } 96 | memcpy(copy, str, len); 97 | 98 | return copy; 99 | } 100 | 101 | void cJSON_InitHooks(cJSON_Hooks* hooks) 102 | { 103 | if (!hooks) 104 | { 105 | /* Reset hooks */ 106 | cJSON_malloc = malloc; 107 | cJSON_free = free; 108 | return; 109 | } 110 | 111 | cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc; 112 | cJSON_free = (hooks->free_fn) ? hooks->free_fn : free; 113 | } 114 | 115 | /* Internal constructor. */ 116 | static cJSON *cJSON_New_Item(void) 117 | { 118 | cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); 119 | if (node) 120 | { 121 | memset(node, '\0', sizeof(cJSON)); 122 | } 123 | 124 | return node; 125 | } 126 | 127 | /* Delete a cJSON structure. */ 128 | void cJSON_Delete(cJSON *c) 129 | { 130 | cJSON *next = NULL; 131 | while (c) 132 | { 133 | next = c->next; 134 | if (!(c->type & cJSON_IsReference) && c->child) 135 | { 136 | cJSON_Delete(c->child); 137 | } 138 | if (!(c->type & cJSON_IsReference) && c->valuestring) 139 | { 140 | cJSON_free(c->valuestring); 141 | } 142 | if (!(c->type & cJSON_StringIsConst) && c->string) 143 | { 144 | cJSON_free(c->string); 145 | } 146 | cJSON_free(c); 147 | c = next; 148 | } 149 | } 150 | 151 | /* Parse the input text to generate a number, and populate the result into item. */ 152 | static const char *parse_number(cJSON *item, const char *num) 153 | { 154 | double n = 0; 155 | double sign = 1; 156 | double scale = 0; 157 | int subscale = 0; 158 | int signsubscale = 1; 159 | 160 | /* Has sign? */ 161 | if (*num == '-') 162 | { 163 | sign = -1; 164 | num++; 165 | } 166 | /* is zero */ 167 | if (*num == '0') 168 | { 169 | num++; 170 | } 171 | /* Number? */ 172 | if ((*num >= '1') && (*num <= '9')) 173 | { 174 | do 175 | { 176 | n = (n * 10.0) + (*num++ - '0'); 177 | } 178 | while ((*num >= '0') && (*num<='9')); 179 | } 180 | /* Fractional part? */ 181 | if ((*num == '.') && (num[1] >= '0') && (num[1] <= '9')) 182 | { 183 | num++; 184 | do 185 | { 186 | n = (n *10.0) + (*num++ - '0'); 187 | scale--; 188 | } while ((*num >= '0') && (*num <= '9')); 189 | } 190 | /* Exponent? */ 191 | if ((*num == 'e') || (*num == 'E')) 192 | { 193 | num++; 194 | /* With sign? */ 195 | if (*num == '+') 196 | { 197 | num++; 198 | } 199 | else if (*num == '-') 200 | { 201 | signsubscale = -1; 202 | num++; 203 | } 204 | /* Number? */ 205 | while ((*num>='0') && (*num<='9')) 206 | { 207 | subscale = (subscale * 10) + (*num++ - '0'); 208 | } 209 | } 210 | 211 | /* number = +/- number.fraction * 10^+/- exponent */ 212 | n = sign * n * pow(10.0, (scale + subscale * signsubscale)); 213 | 214 | item->valuedouble = n; 215 | item->valueint = (int)n; 216 | item->type = cJSON_Number; 217 | 218 | return num; 219 | } 220 | 221 | /* calculate the next largest power of 2 */ 222 | static int pow2gt (int x) 223 | { 224 | --x; 225 | 226 | x |= x >> 1; 227 | x |= x >> 2; 228 | x |= x >> 4; 229 | #if INTEGER_SIZE & 0x1110 /* at least 16 bit */ 230 | x |= x >> 8; 231 | #endif 232 | #if INTEGER_SIZE & 0x1100 /* at least 32 bit */ 233 | x |= x >> 16; 234 | #endif 235 | #if INT_SIZE & 0x1000 /* 64 bit */ 236 | x |= x >> 32; 237 | #endif 238 | 239 | return x + 1; 240 | } 241 | 242 | typedef struct 243 | { 244 | char *buffer; 245 | int length; 246 | int offset; 247 | } printbuffer; 248 | 249 | /* realloc printbuffer if necessary to have at least "needed" bytes more */ 250 | static char* ensure(printbuffer *p, int needed) 251 | { 252 | char *newbuffer = NULL; 253 | int newsize = 0; 254 | if (!p || !p->buffer) 255 | { 256 | return NULL; 257 | } 258 | needed += p->offset; 259 | if (needed <= p->length) 260 | { 261 | return p->buffer + p->offset; 262 | } 263 | 264 | newsize = pow2gt(needed); 265 | newbuffer = (char*)cJSON_malloc(newsize); 266 | if (!newbuffer) 267 | { 268 | cJSON_free(p->buffer); 269 | p->length = 0; 270 | p->buffer = NULL; 271 | 272 | return NULL; 273 | } 274 | if (newbuffer) 275 | { 276 | memcpy(newbuffer, p->buffer, p->length); 277 | } 278 | cJSON_free(p->buffer); 279 | p->length = newsize; 280 | p->buffer = newbuffer; 281 | 282 | return newbuffer + p->offset; 283 | } 284 | 285 | /* calculate the new length of the string in a printbuffer */ 286 | static int update(const printbuffer *p) 287 | { 288 | char *str = NULL; 289 | if (!p || !p->buffer) 290 | { 291 | return 0; 292 | } 293 | str = p->buffer + p->offset; 294 | 295 | return p->offset + strlen(str); 296 | } 297 | 298 | /* Render the number nicely from the given item into a string. */ 299 | static char *print_number(const cJSON *item, printbuffer *p) 300 | { 301 | char *str = NULL; 302 | double d = item->valuedouble; 303 | /* special case for 0. */ 304 | if (d == 0) 305 | { 306 | if (p) 307 | { 308 | str = ensure(p, 2); 309 | } 310 | else 311 | { 312 | str = (char*)cJSON_malloc(2); 313 | } 314 | if (str) 315 | { 316 | strcpy(str,"0"); 317 | } 318 | } 319 | /* value is an int */ 320 | else if ((fabs(((double)item->valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN)) 321 | { 322 | if (p) 323 | { 324 | str = ensure(p, 21); 325 | } 326 | else 327 | { 328 | /* 2^64+1 can be represented in 21 chars. */ 329 | str = (char*)cJSON_malloc(21); 330 | } 331 | if (str) 332 | { 333 | sprintf(str, "%d", item->valueint); 334 | } 335 | } 336 | /* value is a floating point number */ 337 | else 338 | { 339 | if (p) 340 | { 341 | /* This is a nice tradeoff. */ 342 | str = ensure(p, 64); 343 | } 344 | else 345 | { 346 | /* This is a nice tradeoff. */ 347 | str=(char*)cJSON_malloc(64); 348 | } 349 | if (str) 350 | { 351 | /* This checks for NaN and Infinity */ 352 | if ((d * 0) != 0) 353 | { 354 | sprintf(str, "null"); 355 | } 356 | else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60)) 357 | { 358 | sprintf(str, "%.0f", d); 359 | } 360 | else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9)) 361 | { 362 | sprintf(str, "%e", d); 363 | } 364 | else 365 | { 366 | sprintf(str, "%f", d); 367 | } 368 | } 369 | } 370 | return str; 371 | } 372 | 373 | /* parse 4 digit hexadecimal number */ 374 | static unsigned parse_hex4(const char *str) 375 | { 376 | unsigned h = 0; 377 | /* first digit */ 378 | if ((*str >= '0') && (*str <= '9')) 379 | { 380 | h += (*str) - '0'; 381 | } 382 | else if ((*str >= 'A') && (*str <= 'F')) 383 | { 384 | h += 10 + (*str) - 'A'; 385 | } 386 | else if ((*str >= 'a') && (*str <= 'f')) 387 | { 388 | h += 10 + (*str) - 'a'; 389 | } 390 | else /* invalid */ 391 | { 392 | return 0; 393 | } 394 | 395 | 396 | /* second digit */ 397 | h = h << 4; 398 | str++; 399 | if ((*str >= '0') && (*str <= '9')) 400 | { 401 | h += (*str) - '0'; 402 | } 403 | else if ((*str >= 'A') && (*str <= 'F')) 404 | { 405 | h += 10 + (*str) - 'A'; 406 | } 407 | else if ((*str >= 'a') && (*str <= 'f')) 408 | { 409 | h += 10 + (*str) - 'a'; 410 | } 411 | else /* invalid */ 412 | { 413 | return 0; 414 | } 415 | 416 | /* third digit */ 417 | h = h << 4; 418 | str++; 419 | if ((*str >= '0') && (*str <= '9')) 420 | { 421 | h += (*str) - '0'; 422 | } 423 | else if ((*str >= 'A') && (*str <= 'F')) 424 | { 425 | h += 10 + (*str) - 'A'; 426 | } 427 | else if ((*str >= 'a') && (*str <= 'f')) 428 | { 429 | h += 10 + (*str) - 'a'; 430 | } 431 | else /* invalid */ 432 | { 433 | return 0; 434 | } 435 | 436 | /* fourth digit */ 437 | h = h << 4; 438 | str++; 439 | if ((*str >= '0') && (*str <= '9')) 440 | { 441 | h += (*str) - '0'; 442 | } 443 | else if ((*str >= 'A') && (*str <= 'F')) 444 | { 445 | h += 10 + (*str) - 'A'; 446 | } 447 | else if ((*str >= 'a') && (*str <= 'f')) 448 | { 449 | h += 10 + (*str) - 'a'; 450 | } 451 | else /* invalid */ 452 | { 453 | return 0; 454 | } 455 | 456 | return h; 457 | } 458 | 459 | /* first bytes of UTF8 encoding for a given length in bytes */ 460 | static const unsigned char firstByteMark[7] = 461 | { 462 | 0x00, /* should never happen */ 463 | 0x00, /* 0xxxxxxx */ 464 | 0xC0, /* 110xxxxx */ 465 | 0xE0, /* 1110xxxx */ 466 | 0xF0, /* 11110xxx */ 467 | 0xF8, 468 | 0xFC 469 | }; 470 | 471 | /* Parse the input text into an unescaped cstring, and populate item. */ 472 | static const char *parse_string(cJSON *item, const char *str, const char **ep) 473 | { 474 | const char *ptr = str + 1; 475 | const char *end_ptr =str + 1; 476 | char *ptr2 = NULL; 477 | char *out = NULL; 478 | int len = 0; 479 | unsigned uc = 0; 480 | unsigned uc2 = 0; 481 | 482 | /* not a string! */ 483 | if (*str != '\"') 484 | { 485 | *ep = str; 486 | return NULL; 487 | } 488 | 489 | while ((*end_ptr != '\"') && *end_ptr && ++len) 490 | { 491 | if (*end_ptr++ == '\\') 492 | { 493 | if (*end_ptr == '\0') 494 | { 495 | /* prevent buffer overflow when last input character is a backslash */ 496 | return NULL; 497 | } 498 | /* Skip escaped quotes. */ 499 | end_ptr++; 500 | } 501 | } 502 | 503 | /* This is at most how long we need for the string, roughly. */ 504 | out = (char*)cJSON_malloc(len + 1); 505 | if (!out) 506 | { 507 | return NULL; 508 | } 509 | item->valuestring = out; /* assign here so out will be deleted during cJSON_Delete() later */ 510 | item->type = cJSON_String; 511 | 512 | ptr = str + 1; 513 | ptr2 = out; 514 | /* loop through the string literal */ 515 | while (ptr < end_ptr) 516 | { 517 | if (*ptr != '\\') 518 | { 519 | *ptr2++ = *ptr++; 520 | } 521 | /* escape sequence */ 522 | else 523 | { 524 | ptr++; 525 | switch (*ptr) 526 | { 527 | case 'b': 528 | *ptr2++ = '\b'; 529 | break; 530 | case 'f': 531 | *ptr2++ = '\f'; 532 | break; 533 | case 'n': 534 | *ptr2++ = '\n'; 535 | break; 536 | case 'r': 537 | *ptr2++ = '\r'; 538 | break; 539 | case 't': 540 | *ptr2++ = '\t'; 541 | break; 542 | case '\"': 543 | case '\\': 544 | case '/': 545 | *ptr2++ = *ptr; 546 | break; 547 | case 'u': 548 | /* transcode utf16 to utf8. See RFC2781 and RFC3629. */ 549 | uc = parse_hex4(ptr + 1); /* get the unicode char. */ 550 | ptr += 4; 551 | if (ptr >= end_ptr) 552 | { 553 | /* invalid */ 554 | *ep = str; 555 | return NULL; 556 | } 557 | /* check for invalid. */ 558 | if (((uc >= 0xDC00) && (uc <= 0xDFFF)) || (uc == 0)) 559 | { 560 | *ep = str; 561 | return NULL; 562 | } 563 | 564 | /* UTF16 surrogate pairs. */ 565 | if ((uc >= 0xD800) && (uc<=0xDBFF)) 566 | { 567 | if ((ptr + 6) > end_ptr) 568 | { 569 | /* invalid */ 570 | *ep = str; 571 | return NULL; 572 | } 573 | if ((ptr[1] != '\\') || (ptr[2] != 'u')) 574 | { 575 | /* missing second-half of surrogate. */ 576 | *ep = str; 577 | return NULL; 578 | } 579 | uc2 = parse_hex4(ptr + 3); 580 | ptr += 6; /* \uXXXX */ 581 | if ((uc2 < 0xDC00) || (uc2 > 0xDFFF)) 582 | { 583 | /* invalid second-half of surrogate. */ 584 | *ep = str; 585 | return NULL; 586 | } 587 | /* calculate unicode codepoint from the surrogate pair */ 588 | uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF)); 589 | } 590 | 591 | /* encode as UTF8 592 | * takes at maximum 4 bytes to encode: 593 | * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ 594 | len = 4; 595 | if (uc < 0x80) 596 | { 597 | /* normal ascii, encoding 0xxxxxxx */ 598 | len = 1; 599 | } 600 | else if (uc < 0x800) 601 | { 602 | /* two bytes, encoding 110xxxxx 10xxxxxx */ 603 | len = 2; 604 | } 605 | else if (uc < 0x10000) 606 | { 607 | /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ 608 | len = 3; 609 | } 610 | ptr2 += len; 611 | 612 | switch (len) { 613 | case 4: 614 | /* 10xxxxxx */ 615 | *--ptr2 = ((uc | 0x80) & 0xBF); 616 | uc >>= 6; 617 | case 3: 618 | /* 10xxxxxx */ 619 | *--ptr2 = ((uc | 0x80) & 0xBF); 620 | uc >>= 6; 621 | case 2: 622 | /* 10xxxxxx */ 623 | *--ptr2 = ((uc | 0x80) & 0xBF); 624 | uc >>= 6; 625 | case 1: 626 | /* depending on the length in bytes this determines the 627 | * encoding ofthe first UTF8 byte */ 628 | *--ptr2 = (uc | firstByteMark[len]); 629 | } 630 | ptr2 += len; 631 | break; 632 | default: 633 | *ep = str; 634 | return NULL; 635 | } 636 | ptr++; 637 | } 638 | } 639 | *ptr2 = '\0'; 640 | if (*ptr == '\"') 641 | { 642 | ptr++; 643 | } 644 | 645 | return ptr; 646 | } 647 | 648 | /* Render the cstring provided to an escaped version that can be printed. */ 649 | static char *print_string_ptr(const char *str, printbuffer *p) 650 | { 651 | const char *ptr = NULL; 652 | char *ptr2 = NULL; 653 | char *out = NULL; 654 | int len = 0; 655 | bool flag = false; 656 | unsigned char token = '\0'; 657 | 658 | /* empty string */ 659 | if (!str) 660 | { 661 | if (p) 662 | { 663 | out = ensure(p, 3); 664 | } 665 | else 666 | { 667 | out = (char*)cJSON_malloc(3); 668 | } 669 | if (!out) 670 | { 671 | return NULL; 672 | } 673 | strcpy(out, "\"\""); 674 | 675 | return out; 676 | } 677 | 678 | /* set "flag" to 1 if something needs to be escaped */ 679 | for (ptr = str; *ptr; ptr++) 680 | { 681 | flag |= (((*ptr > 0) && (*ptr < 32)) /* unprintable characters */ 682 | || (*ptr == '\"') /* double quote */ 683 | || (*ptr == '\\')) /* backslash */ 684 | ? 1 685 | : 0; 686 | } 687 | /* no characters have to be escaped */ 688 | if (!flag) 689 | { 690 | len = ptr - str; 691 | if (p) 692 | { 693 | out = ensure(p, len + 3); 694 | } 695 | else 696 | { 697 | out = (char*)cJSON_malloc(len + 3); 698 | } 699 | if (!out) 700 | { 701 | return NULL; 702 | } 703 | 704 | ptr2 = out; 705 | *ptr2++ = '\"'; 706 | strcpy(ptr2, str); 707 | ptr2[len] = '\"'; 708 | ptr2[len + 1] = '\0'; 709 | 710 | return out; 711 | } 712 | 713 | ptr = str; 714 | /* calculate additional space that is needed for escaping */ 715 | while ((token = *ptr) && ++len) 716 | { 717 | if (strchr("\"\\\b\f\n\r\t", token)) 718 | { 719 | len++; /* +1 for the backslash */ 720 | } 721 | else if (token < 32) 722 | { 723 | len += 5; /* +5 for \uXXXX */ 724 | } 725 | ptr++; 726 | } 727 | 728 | if (p) 729 | { 730 | out = ensure(p, len + 3); 731 | } 732 | else 733 | { 734 | out = (char*)cJSON_malloc(len + 3); 735 | } 736 | if (!out) 737 | { 738 | return NULL; 739 | } 740 | 741 | ptr2 = out; 742 | ptr = str; 743 | *ptr2++ = '\"'; 744 | /* copy the string */ 745 | while (*ptr) 746 | { 747 | if (((unsigned char)*ptr > 31) && (*ptr != '\"') && (*ptr != '\\')) 748 | { 749 | /* normal character, copy */ 750 | *ptr2++ = *ptr++; 751 | } 752 | else 753 | { 754 | /* character needs to be escaped */ 755 | *ptr2++ = '\\'; 756 | switch (token = *ptr++) 757 | { 758 | case '\\': 759 | *ptr2++ = '\\'; 760 | break; 761 | case '\"': 762 | *ptr2++ = '\"'; 763 | break; 764 | case '\b': 765 | *ptr2++ = 'b'; 766 | break; 767 | case '\f': 768 | *ptr2++ = 'f'; 769 | break; 770 | case '\n': 771 | *ptr2++ = 'n'; 772 | break; 773 | case '\r': 774 | *ptr2++ = 'r'; 775 | break; 776 | case '\t': 777 | *ptr2++ = 't'; 778 | break; 779 | default: 780 | /* escape and print as unicode codepoint */ 781 | sprintf(ptr2, "u%04x", token); 782 | ptr2 += 5; 783 | break; 784 | } 785 | } 786 | } 787 | *ptr2++ = '\"'; 788 | *ptr2++ = '\0'; 789 | 790 | return out; 791 | } 792 | 793 | /* Invoke print_string_ptr (which is useful) on an item. */ 794 | static char *print_string(const cJSON *item, printbuffer *p) 795 | { 796 | return print_string_ptr(item->valuestring, p); 797 | } 798 | 799 | /* Predeclare these prototypes. */ 800 | static const char *parse_value(cJSON *item, const char *value, const char **ep); 801 | static char *print_value(const cJSON *item, int depth, bool fmt, printbuffer *p); 802 | static const char *parse_array(cJSON *item, const char *value, const char **ep); 803 | static char *print_array(const cJSON *item, int depth, bool fmt, printbuffer *p); 804 | static const char *parse_object(cJSON *item, const char *value, const char **ep); 805 | static char *print_object(const cJSON *item, int depth, bool fmt, printbuffer *p); 806 | 807 | /* Utility to jump whitespace and cr/lf */ 808 | static const char *skip(const char *in) 809 | { 810 | while (in && *in && ((unsigned char)*in<=32)) 811 | { 812 | in++; 813 | } 814 | 815 | return in; 816 | } 817 | 818 | /* Parse an object - create a new root, and populate. */ 819 | cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, bool require_null_terminated) 820 | { 821 | const char *end = NULL; 822 | /* use global error pointer if no specific one was given */ 823 | const char **ep = return_parse_end ? return_parse_end : &global_ep; 824 | cJSON *c = cJSON_New_Item(); 825 | *ep = NULL; 826 | if (!c) /* memory fail */ 827 | { 828 | return NULL; 829 | } 830 | 831 | end = parse_value(c, skip(value), ep); 832 | if (!end) 833 | { 834 | /* parse failure. ep is set. */ 835 | cJSON_Delete(c); 836 | return NULL; 837 | } 838 | 839 | /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ 840 | if (require_null_terminated) 841 | { 842 | end = skip(end); 843 | if (*end) 844 | { 845 | cJSON_Delete(c); 846 | *ep = end; 847 | return NULL; 848 | } 849 | } 850 | if (return_parse_end) 851 | { 852 | *return_parse_end = end; 853 | } 854 | 855 | return c; 856 | } 857 | 858 | /* Default options for cJSON_Parse */ 859 | cJSON *cJSON_Parse(const char *value) 860 | { 861 | return cJSON_ParseWithOpts(value, 0, 0); 862 | } 863 | 864 | /* Render a cJSON item/entity/structure to text. */ 865 | char *cJSON_Print(const cJSON *item) 866 | { 867 | return print_value(item, 0, 1, 0); 868 | } 869 | 870 | char *cJSON_PrintUnformatted(const cJSON *item) 871 | { 872 | return print_value(item, 0, 0, 0); 873 | } 874 | 875 | char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, bool fmt) 876 | { 877 | printbuffer p; 878 | p.buffer = (char*)cJSON_malloc(prebuffer); 879 | if (!p.buffer) 880 | { 881 | return NULL; 882 | } 883 | p.length = prebuffer; 884 | p.offset = 0; 885 | 886 | return print_value(item, 0, fmt, &p); 887 | } 888 | 889 | 890 | /* Parser core - when encountering text, process appropriately. */ 891 | static const char *parse_value(cJSON *item, const char *value, const char **ep) 892 | { 893 | if (!value) 894 | { 895 | /* Fail on null. */ 896 | return NULL; 897 | } 898 | 899 | /* parse the different types of values */ 900 | if (!strncmp(value, "null", 4)) 901 | { 902 | item->type = cJSON_NULL; 903 | return value + 4; 904 | } 905 | if (!strncmp(value, "false", 5)) 906 | { 907 | item->type = cJSON_False; 908 | return value + 5; 909 | } 910 | if (!strncmp(value, "true", 4)) 911 | { 912 | item->type = cJSON_True; 913 | item->valueint = 1; 914 | return value + 4; 915 | } 916 | if (*value == '\"') 917 | { 918 | return parse_string(item, value, ep); 919 | } 920 | if ((*value == '-') || ((*value >= '0') && (*value <= '9'))) 921 | { 922 | return parse_number(item, value); 923 | } 924 | if (*value == '[') 925 | { 926 | return parse_array(item, value, ep); 927 | } 928 | if (*value == '{') 929 | { 930 | return parse_object(item, value, ep); 931 | } 932 | 933 | /* failure. */ 934 | *ep = value; 935 | return NULL; 936 | } 937 | 938 | /* Render a value to text. */ 939 | static char *print_value(const cJSON *item, int depth, bool fmt, printbuffer *p) 940 | { 941 | char *out = NULL; 942 | 943 | if (!item) 944 | { 945 | return NULL; 946 | } 947 | if (p) 948 | { 949 | switch ((item->type) & 0xFF) 950 | { 951 | case cJSON_NULL: 952 | out = ensure(p, 5); 953 | if (out) 954 | { 955 | strcpy(out, "null"); 956 | } 957 | break; 958 | case cJSON_False: 959 | out = ensure(p, 6); 960 | if (out) 961 | { 962 | strcpy(out, "false"); 963 | } 964 | break; 965 | case cJSON_True: 966 | out = ensure(p, 5); 967 | if (out) 968 | { 969 | strcpy(out, "true"); 970 | } 971 | break; 972 | case cJSON_Number: 973 | out = print_number(item, p); 974 | break; 975 | case cJSON_String: 976 | out = print_string(item, p); 977 | break; 978 | case cJSON_Array: 979 | out = print_array(item, depth, fmt, p); 980 | break; 981 | case cJSON_Object: 982 | out = print_object(item, depth, fmt, p); 983 | break; 984 | } 985 | } 986 | else 987 | { 988 | switch ((item->type) & 0xFF) 989 | { 990 | case cJSON_NULL: 991 | out = cJSON_strdup("null"); 992 | break; 993 | case cJSON_False: 994 | out = cJSON_strdup("false"); 995 | break; 996 | case cJSON_True: 997 | out = cJSON_strdup("true"); 998 | break; 999 | case cJSON_Number: 1000 | out = print_number(item, 0); 1001 | break; 1002 | case cJSON_String: 1003 | out = print_string(item, 0); 1004 | break; 1005 | case cJSON_Array: 1006 | out = print_array(item, depth, fmt, 0); 1007 | break; 1008 | case cJSON_Object: 1009 | out = print_object(item, depth, fmt, 0); 1010 | break; 1011 | } 1012 | } 1013 | 1014 | return out; 1015 | } 1016 | 1017 | /* Build an array from input text. */ 1018 | static const char *parse_array(cJSON *item,const char *value,const char **ep) 1019 | { 1020 | cJSON *child = NULL; 1021 | if (*value != '[') 1022 | { 1023 | /* not an array! */ 1024 | *ep = value; 1025 | return NULL; 1026 | } 1027 | 1028 | item->type = cJSON_Array; 1029 | value = skip(value + 1); 1030 | if (*value == ']') 1031 | { 1032 | /* empty array. */ 1033 | return value + 1; 1034 | } 1035 | 1036 | item->child = child = cJSON_New_Item(); 1037 | if (!item->child) 1038 | { 1039 | /* memory fail */ 1040 | return NULL; 1041 | } 1042 | /* skip any spacing, get the value. */ 1043 | value = skip(parse_value(child, skip(value), ep)); 1044 | if (!value) 1045 | { 1046 | return NULL; 1047 | } 1048 | 1049 | /* loop through the comma separated array elements */ 1050 | while (*value == ',') 1051 | { 1052 | cJSON *new_item = NULL; 1053 | if (!(new_item = cJSON_New_Item())) 1054 | { 1055 | /* memory fail */ 1056 | return NULL; 1057 | } 1058 | /* add new item to end of the linked list */ 1059 | child->next = new_item; 1060 | new_item->prev = child; 1061 | child = new_item; 1062 | 1063 | /* go to the next comma */ 1064 | value = skip(parse_value(child, skip(value + 1), ep)); 1065 | if (!value) 1066 | { 1067 | /* memory fail */ 1068 | return NULL; 1069 | } 1070 | } 1071 | 1072 | if (*value == ']') 1073 | { 1074 | /* end of array */ 1075 | return value + 1; 1076 | } 1077 | 1078 | /* malformed. */ 1079 | *ep = value; 1080 | 1081 | return NULL; 1082 | } 1083 | 1084 | /* Render an array to text */ 1085 | static char *print_array(const cJSON *item, int depth, bool fmt, printbuffer *p) 1086 | { 1087 | char **entries; 1088 | char *out = NULL; 1089 | char *ptr = NULL; 1090 | char *ret = NULL; 1091 | int len = 5; 1092 | cJSON *child = item->child; 1093 | int numentries = 0; 1094 | int i = 0; 1095 | bool fail = false; 1096 | size_t tmplen = 0; 1097 | 1098 | /* How many entries in the array? */ 1099 | while (child) 1100 | { 1101 | numentries++; 1102 | child = child->next; 1103 | } 1104 | 1105 | /* Explicitly handle numentries == 0 */ 1106 | if (!numentries) 1107 | { 1108 | if (p) 1109 | { 1110 | out = ensure(p, 3); 1111 | } 1112 | else 1113 | { 1114 | out = (char*)cJSON_malloc(3); 1115 | } 1116 | if (out) 1117 | { 1118 | strcpy(out,"[]"); 1119 | } 1120 | 1121 | return out; 1122 | } 1123 | 1124 | if (p) 1125 | { 1126 | /* Compose the output array. */ 1127 | /* opening square bracket */ 1128 | i = p->offset; 1129 | ptr = ensure(p, 1); 1130 | if (!ptr) 1131 | { 1132 | return NULL; 1133 | } 1134 | *ptr = '['; 1135 | p->offset++; 1136 | 1137 | child = item->child; 1138 | while (child && !fail) 1139 | { 1140 | print_value(child, depth + 1, fmt, p); 1141 | p->offset = update(p); 1142 | if (child->next) 1143 | { 1144 | len = fmt ? 2 : 1; 1145 | ptr = ensure(p, len + 1); 1146 | if (!ptr) 1147 | { 1148 | return NULL; 1149 | } 1150 | *ptr++ = ','; 1151 | if(fmt) 1152 | { 1153 | *ptr++ = ' '; 1154 | } 1155 | *ptr = '\0'; 1156 | p->offset += len; 1157 | } 1158 | child = child->next; 1159 | } 1160 | ptr = ensure(p, 2); 1161 | if (!ptr) 1162 | { 1163 | return NULL; 1164 | } 1165 | *ptr++ = ']'; 1166 | *ptr = '\0'; 1167 | out = (p->buffer) + i; 1168 | } 1169 | else 1170 | { 1171 | /* Allocate an array to hold the pointers to all printed values */ 1172 | entries = (char**)cJSON_malloc(numentries * sizeof(char*)); 1173 | if (!entries) 1174 | { 1175 | return NULL; 1176 | } 1177 | memset(entries, '\0', numentries * sizeof(char*)); 1178 | 1179 | /* Retrieve all the results: */ 1180 | child = item->child; 1181 | while (child && !fail) 1182 | { 1183 | ret = print_value(child, depth + 1, fmt, 0); 1184 | entries[i++] = ret; 1185 | if (ret) 1186 | { 1187 | len += strlen(ret) + 2 + (fmt ? 1 : 0); 1188 | } 1189 | else 1190 | { 1191 | fail = true; 1192 | } 1193 | child = child->next; 1194 | } 1195 | 1196 | /* If we didn't fail, try to malloc the output string */ 1197 | if (!fail) 1198 | { 1199 | out = (char*)cJSON_malloc(len); 1200 | } 1201 | /* If that fails, we fail. */ 1202 | if (!out) 1203 | { 1204 | fail = true; 1205 | } 1206 | 1207 | /* Handle failure. */ 1208 | if (fail) 1209 | { 1210 | /* free all the entries in the array */ 1211 | for (i = 0; i < numentries; i++) 1212 | { 1213 | if (entries[i]) 1214 | { 1215 | cJSON_free(entries[i]); 1216 | } 1217 | } 1218 | cJSON_free(entries); 1219 | return NULL; 1220 | } 1221 | 1222 | /* Compose the output array. */ 1223 | *out='['; 1224 | ptr = out + 1; 1225 | *ptr = '\0'; 1226 | for (i = 0; i < numentries; i++) 1227 | { 1228 | tmplen = strlen(entries[i]); 1229 | memcpy(ptr, entries[i], tmplen); 1230 | ptr += tmplen; 1231 | if (i != (numentries - 1)) 1232 | { 1233 | *ptr++ = ','; 1234 | if(fmt) 1235 | { 1236 | *ptr++ = ' '; 1237 | } 1238 | *ptr = '\0'; 1239 | } 1240 | cJSON_free(entries[i]); 1241 | } 1242 | cJSON_free(entries); 1243 | *ptr++ = ']'; 1244 | *ptr++ = '\0'; 1245 | } 1246 | 1247 | return out; 1248 | } 1249 | 1250 | /* Build an object from the text. */ 1251 | static const char *parse_object(cJSON *item, const char *value, const char **ep) 1252 | { 1253 | cJSON *child = NULL; 1254 | if (*value != '{') 1255 | { 1256 | /* not an object! */ 1257 | *ep = value; 1258 | return NULL; 1259 | } 1260 | 1261 | item->type = cJSON_Object; 1262 | value = skip(value + 1); 1263 | if (*value == '}') 1264 | { 1265 | /* empty object. */ 1266 | return value + 1; 1267 | } 1268 | 1269 | child = cJSON_New_Item(); 1270 | item->child = child; 1271 | if (!item->child) 1272 | { 1273 | return NULL; 1274 | } 1275 | /* parse first key */ 1276 | value = skip(parse_string(child, skip(value), ep)); 1277 | if (!value) 1278 | { 1279 | return NULL; 1280 | } 1281 | /* use string as key, not value */ 1282 | child->string = child->valuestring; 1283 | child->valuestring = NULL; 1284 | 1285 | if (*value != ':') 1286 | { 1287 | /* invalid object. */ 1288 | *ep = value; 1289 | return NULL; 1290 | } 1291 | /* skip any spacing, get the value. */ 1292 | value = skip(parse_value(child, skip(value + 1), ep)); 1293 | if (!value) 1294 | { 1295 | return NULL; 1296 | } 1297 | 1298 | while (*value == ',') 1299 | { 1300 | cJSON *new_item = NULL; 1301 | if (!(new_item = cJSON_New_Item())) 1302 | { 1303 | /* memory fail */ 1304 | return NULL; 1305 | } 1306 | /* add to linked list */ 1307 | child->next = new_item; 1308 | new_item->prev = child; 1309 | 1310 | child = new_item; 1311 | value = skip(parse_string(child, skip(value + 1), ep)); 1312 | if (!value) 1313 | { 1314 | return NULL; 1315 | } 1316 | 1317 | /* use string as key, not value */ 1318 | child->string = child->valuestring; 1319 | child->valuestring = NULL; 1320 | 1321 | if (*value != ':') 1322 | { 1323 | /* invalid object. */ 1324 | *ep = value; 1325 | return NULL; 1326 | } 1327 | /* skip any spacing, get the value. */ 1328 | value = skip(parse_value(child, skip(value + 1), ep)); 1329 | if (!value) 1330 | { 1331 | return NULL; 1332 | } 1333 | } 1334 | /* end of object */ 1335 | if (*value == '}') 1336 | { 1337 | return value + 1; 1338 | } 1339 | 1340 | /* malformed */ 1341 | *ep = value; 1342 | return NULL; 1343 | } 1344 | 1345 | /* Render an object to text. */ 1346 | static char *print_object(const cJSON *item, int depth, bool fmt, printbuffer *p) 1347 | { 1348 | char **entries = NULL; 1349 | char **names = NULL; 1350 | char *out = NULL; 1351 | char *ptr = NULL; 1352 | char *ret = NULL; 1353 | char *str = NULL; 1354 | int len = 7; 1355 | int i = 0; 1356 | int j = 0; 1357 | cJSON *child = item->child; 1358 | int numentries = 0; 1359 | bool fail = false; 1360 | size_t tmplen = 0; 1361 | 1362 | /* Count the number of entries. */ 1363 | while (child) 1364 | { 1365 | numentries++; 1366 | child = child->next; 1367 | } 1368 | 1369 | /* Explicitly handle empty object case */ 1370 | if (!numentries) 1371 | { 1372 | if (p) 1373 | { 1374 | out = ensure(p, fmt ? depth + 4 : 3); 1375 | } 1376 | else 1377 | { 1378 | out = (char*)cJSON_malloc(fmt ? depth + 4 : 3); 1379 | } 1380 | if (!out) 1381 | { 1382 | return NULL; 1383 | } 1384 | ptr = out; 1385 | *ptr++ = '{'; 1386 | if (fmt) { 1387 | *ptr++ = '\n'; 1388 | for (i = 0; i < depth; i++) 1389 | { 1390 | *ptr++ = '\t'; 1391 | } 1392 | } 1393 | *ptr++ = '}'; 1394 | *ptr++ = '\0'; 1395 | 1396 | return out; 1397 | } 1398 | 1399 | if (p) 1400 | { 1401 | /* Compose the output: */ 1402 | i = p->offset; 1403 | len = fmt ? 2 : 1; /* fmt: {\n */ 1404 | ptr = ensure(p, len + 1); 1405 | if (!ptr) 1406 | { 1407 | return NULL; 1408 | } 1409 | 1410 | *ptr++ = '{'; 1411 | if (fmt) 1412 | { 1413 | *ptr++ = '\n'; 1414 | } 1415 | *ptr = '\0'; 1416 | p->offset += len; 1417 | 1418 | child = item->child; 1419 | depth++; 1420 | while (child) 1421 | { 1422 | if (fmt) 1423 | { 1424 | ptr = ensure(p, depth); 1425 | if (!ptr) 1426 | { 1427 | return NULL; 1428 | } 1429 | for (j = 0; j < depth; j++) 1430 | { 1431 | *ptr++ = '\t'; 1432 | } 1433 | p->offset += depth; 1434 | } 1435 | 1436 | /* print key */ 1437 | print_string_ptr(child->string, p); 1438 | p->offset = update(p); 1439 | 1440 | len = fmt ? 2 : 1; 1441 | ptr = ensure(p, len); 1442 | if (!ptr) 1443 | { 1444 | return NULL; 1445 | } 1446 | *ptr++ = ':'; 1447 | if (fmt) 1448 | { 1449 | *ptr++ = '\t'; 1450 | } 1451 | p->offset+=len; 1452 | 1453 | /* print value */ 1454 | print_value(child, depth, fmt, p); 1455 | p->offset = update(p); 1456 | 1457 | /* print comma if not last */ 1458 | len = (fmt ? 1 : 0) + (child->next ? 1 : 0); 1459 | ptr = ensure(p, len + 1); 1460 | if (!ptr) 1461 | { 1462 | return NULL; 1463 | } 1464 | if (child->next) 1465 | { 1466 | *ptr++ = ','; 1467 | } 1468 | 1469 | if (fmt) 1470 | { 1471 | *ptr++ = '\n'; 1472 | } 1473 | *ptr = '\0'; 1474 | p->offset += len; 1475 | 1476 | child = child->next; 1477 | } 1478 | 1479 | ptr = ensure(p, fmt ? (depth + 1) : 2); 1480 | if (!ptr) 1481 | { 1482 | return NULL; 1483 | } 1484 | if (fmt) 1485 | { 1486 | for (i = 0; i < (depth - 1); i++) 1487 | { 1488 | *ptr++ = '\t'; 1489 | } 1490 | } 1491 | *ptr++ = '}'; 1492 | *ptr = '\0'; 1493 | out = (p->buffer) + i; 1494 | } 1495 | else 1496 | { 1497 | /* Allocate space for the names and the objects */ 1498 | entries = (char**)cJSON_malloc(numentries * sizeof(char*)); 1499 | if (!entries) 1500 | { 1501 | return NULL; 1502 | } 1503 | names = (char**)cJSON_malloc(numentries * sizeof(char*)); 1504 | if (!names) 1505 | { 1506 | cJSON_free(entries); 1507 | return NULL; 1508 | } 1509 | memset(entries, '\0', sizeof(char*) * numentries); 1510 | memset(names, '\0', sizeof(char*) * numentries); 1511 | 1512 | /* Collect all the results into our arrays: */ 1513 | child = item->child; 1514 | depth++; 1515 | if (fmt) 1516 | { 1517 | len += depth; 1518 | } 1519 | while (child && !fail) 1520 | { 1521 | names[i] = str = print_string_ptr(child->string, 0); /* print key */ 1522 | entries[i++] = ret = print_value(child, depth, fmt, 0); 1523 | if (str && ret) 1524 | { 1525 | len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0); 1526 | } 1527 | else 1528 | { 1529 | fail = true; 1530 | } 1531 | child = child->next; 1532 | } 1533 | 1534 | /* Try to allocate the output string */ 1535 | if (!fail) 1536 | { 1537 | out = (char*)cJSON_malloc(len); 1538 | } 1539 | if (!out) 1540 | { 1541 | fail = true; 1542 | } 1543 | 1544 | /* Handle failure */ 1545 | if (fail) 1546 | { 1547 | /* free all the printed keys and values */ 1548 | for (i = 0; i < numentries; i++) 1549 | { 1550 | if (names[i]) 1551 | { 1552 | cJSON_free(names[i]); 1553 | } 1554 | if (entries[i]) 1555 | { 1556 | cJSON_free(entries[i]); 1557 | } 1558 | } 1559 | cJSON_free(names); 1560 | cJSON_free(entries); 1561 | return NULL; 1562 | } 1563 | 1564 | /* Compose the output: */ 1565 | *out = '{'; 1566 | ptr = out + 1; 1567 | if (fmt) 1568 | { 1569 | *ptr++ = '\n'; 1570 | } 1571 | *ptr = '\0'; 1572 | for (i = 0; i < numentries; i++) 1573 | { 1574 | if (fmt) 1575 | { 1576 | for (j = 0; j < depth; j++) 1577 | { 1578 | *ptr++='\t'; 1579 | } 1580 | } 1581 | tmplen = strlen(names[i]); 1582 | memcpy(ptr, names[i], tmplen); 1583 | ptr += tmplen; 1584 | *ptr++ = ':'; 1585 | if (fmt) 1586 | { 1587 | *ptr++ = '\t'; 1588 | } 1589 | strcpy(ptr, entries[i]); 1590 | ptr += strlen(entries[i]); 1591 | if (i != (numentries - 1)) 1592 | { 1593 | *ptr++ = ','; 1594 | } 1595 | if (fmt) 1596 | { 1597 | *ptr++ = '\n'; 1598 | } 1599 | *ptr = '\0'; 1600 | cJSON_free(names[i]); 1601 | cJSON_free(entries[i]); 1602 | } 1603 | 1604 | cJSON_free(names); 1605 | cJSON_free(entries); 1606 | if (fmt) 1607 | { 1608 | for (i = 0; i < (depth - 1); i++) 1609 | { 1610 | *ptr++ = '\t'; 1611 | } 1612 | } 1613 | *ptr++ = '}'; 1614 | *ptr++ = '\0'; 1615 | } 1616 | 1617 | return out; 1618 | } 1619 | 1620 | /* Get Array size/item / object item. */ 1621 | int cJSON_GetArraySize(const cJSON *array) 1622 | { 1623 | cJSON *c = array->child; 1624 | int i = 0; 1625 | while(c) 1626 | { 1627 | i++; 1628 | c = c->next; 1629 | } 1630 | return i; 1631 | } 1632 | 1633 | cJSON *cJSON_GetArrayItem(const cJSON *array, int item) 1634 | { 1635 | cJSON *c = array ? array->child : NULL; 1636 | while (c && item > 0) 1637 | { 1638 | item--; 1639 | c = c->next; 1640 | } 1641 | 1642 | return c; 1643 | } 1644 | 1645 | cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string) 1646 | { 1647 | cJSON *c = object ? object->child : NULL; 1648 | while (c && cJSON_strcasecmp(c->string, string)) 1649 | { 1650 | c = c->next; 1651 | } 1652 | return c; 1653 | } 1654 | 1655 | bool cJSON_HasObjectItem(const cJSON *object,const char *string) 1656 | { 1657 | return cJSON_GetObjectItem(object, string) ? 1 : 0; 1658 | } 1659 | 1660 | /* Utility for array list handling. */ 1661 | static void suffix_object(cJSON *prev, cJSON *item) 1662 | { 1663 | prev->next = item; 1664 | item->prev = prev; 1665 | } 1666 | 1667 | /* Utility for handling references. */ 1668 | static cJSON *create_reference(const cJSON *item) 1669 | { 1670 | cJSON *ref = cJSON_New_Item(); 1671 | if (!ref) 1672 | { 1673 | return NULL; 1674 | } 1675 | memcpy(ref, item, sizeof(cJSON)); 1676 | ref->string = NULL; 1677 | ref->type |= cJSON_IsReference; 1678 | ref->next = ref->prev = NULL; 1679 | return ref; 1680 | } 1681 | 1682 | /* Add item to array/object. */ 1683 | void cJSON_AddItemToArray(cJSON *array, cJSON *item) 1684 | { 1685 | cJSON *c = array->child; 1686 | if (!item) 1687 | { 1688 | return; 1689 | } 1690 | if (!c) 1691 | { 1692 | /* list is empty, start new one */ 1693 | array->child = item; 1694 | } 1695 | else 1696 | { 1697 | /* append to the end */ 1698 | while (c->next) 1699 | { 1700 | c = c->next; 1701 | } 1702 | suffix_object(c, item); 1703 | } 1704 | } 1705 | 1706 | void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) 1707 | { 1708 | if (!item) 1709 | { 1710 | return; 1711 | } 1712 | 1713 | /* free old key and set new one */ 1714 | if (item->string) 1715 | { 1716 | cJSON_free(item->string); 1717 | } 1718 | item->string = cJSON_strdup(string); 1719 | 1720 | cJSON_AddItemToArray(object,item); 1721 | } 1722 | 1723 | /* Add an item to an object with constant string as key */ 1724 | void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) 1725 | { 1726 | if (!item) 1727 | { 1728 | return; 1729 | } 1730 | if (!(item->type & cJSON_StringIsConst) && item->string) 1731 | { 1732 | cJSON_free(item->string); 1733 | } 1734 | item->string = (char*)string; 1735 | item->type |= cJSON_StringIsConst; 1736 | cJSON_AddItemToArray(object, item); 1737 | } 1738 | 1739 | void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) 1740 | { 1741 | cJSON_AddItemToArray(array, create_reference(item)); 1742 | } 1743 | 1744 | void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) 1745 | { 1746 | cJSON_AddItemToObject(object, string, create_reference(item)); 1747 | } 1748 | 1749 | cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) 1750 | { 1751 | cJSON *c = array->child; 1752 | while (c && (which > 0)) 1753 | { 1754 | c = c->next; 1755 | which--; 1756 | } 1757 | if (!c) 1758 | { 1759 | /* item doesn't exist */ 1760 | return NULL; 1761 | } 1762 | if (c->prev) 1763 | { 1764 | /* not the first element */ 1765 | c->prev->next = c->next; 1766 | } 1767 | if (c->next) 1768 | { 1769 | c->next->prev = c->prev; 1770 | } 1771 | if (c==array->child) 1772 | { 1773 | array->child = c->next; 1774 | } 1775 | /* make sure the detached item doesn't point anywhere anymore */ 1776 | c->prev = c->next = NULL; 1777 | 1778 | return c; 1779 | } 1780 | 1781 | void cJSON_DeleteItemFromArray(cJSON *array, int which) 1782 | { 1783 | cJSON_Delete(cJSON_DetachItemFromArray(array, which)); 1784 | } 1785 | 1786 | cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string) 1787 | { 1788 | int i = 0; 1789 | cJSON *c = object->child; 1790 | while (c && cJSON_strcasecmp(c->string,string)) 1791 | { 1792 | i++; 1793 | c = c->next; 1794 | } 1795 | if (c) 1796 | { 1797 | return cJSON_DetachItemFromArray(object, i); 1798 | } 1799 | 1800 | return NULL; 1801 | } 1802 | 1803 | void cJSON_DeleteItemFromObject(cJSON *object, const char *string) 1804 | { 1805 | cJSON_Delete(cJSON_DetachItemFromObject(object, string)); 1806 | } 1807 | 1808 | /* Replace array/object items with new ones. */ 1809 | void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) 1810 | { 1811 | cJSON *c = array->child; 1812 | while (c && (which > 0)) 1813 | { 1814 | c = c->next; 1815 | which--; 1816 | } 1817 | if (!c) 1818 | { 1819 | cJSON_AddItemToArray(array, newitem); 1820 | return; 1821 | } 1822 | newitem->next = c; 1823 | newitem->prev = c->prev; 1824 | c->prev = newitem; 1825 | if (c == array->child) 1826 | { 1827 | array->child = newitem; 1828 | } 1829 | else 1830 | { 1831 | newitem->prev->next = newitem; 1832 | } 1833 | } 1834 | 1835 | void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) 1836 | { 1837 | cJSON *c = array->child; 1838 | while (c && (which > 0)) 1839 | { 1840 | c = c->next; 1841 | which--; 1842 | } 1843 | if (!c) 1844 | { 1845 | return; 1846 | } 1847 | newitem->next = c->next; 1848 | newitem->prev = c->prev; 1849 | if (newitem->next) 1850 | { 1851 | newitem->next->prev = newitem; 1852 | } 1853 | if (c == array->child) 1854 | { 1855 | array->child = newitem; 1856 | } 1857 | else 1858 | { 1859 | newitem->prev->next = newitem; 1860 | } 1861 | c->next = c->prev = NULL; 1862 | cJSON_Delete(c); 1863 | } 1864 | 1865 | void cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) 1866 | { 1867 | int i = 0; 1868 | cJSON *c = object->child; 1869 | while(c && cJSON_strcasecmp(c->string, string)) 1870 | { 1871 | i++; 1872 | c = c->next; 1873 | } 1874 | if(c) 1875 | { 1876 | /* free the old string if not const */ 1877 | if (!(newitem->type & cJSON_StringIsConst) && newitem->string) 1878 | { 1879 | cJSON_free(newitem->string); 1880 | } 1881 | 1882 | newitem->string = cJSON_strdup(string); 1883 | cJSON_ReplaceItemInArray(object, i, newitem); 1884 | } 1885 | } 1886 | 1887 | /* Create basic types: */ 1888 | cJSON *cJSON_CreateNull(void) 1889 | { 1890 | cJSON *item = cJSON_New_Item(); 1891 | if(item) 1892 | { 1893 | item->type = cJSON_NULL; 1894 | } 1895 | 1896 | return item; 1897 | } 1898 | 1899 | cJSON *cJSON_CreateTrue(void) 1900 | { 1901 | cJSON *item = cJSON_New_Item(); 1902 | if(item) 1903 | { 1904 | item->type = cJSON_True; 1905 | } 1906 | 1907 | return item; 1908 | } 1909 | 1910 | cJSON *cJSON_CreateFalse(void) 1911 | { 1912 | cJSON *item = cJSON_New_Item(); 1913 | if(item) 1914 | { 1915 | item->type = cJSON_False; 1916 | } 1917 | 1918 | return item; 1919 | } 1920 | 1921 | cJSON *cJSON_CreateBool(bool b) 1922 | { 1923 | cJSON *item = cJSON_New_Item(); 1924 | if(item) 1925 | { 1926 | item->type = b ? cJSON_True : cJSON_False; 1927 | } 1928 | 1929 | return item; 1930 | } 1931 | 1932 | cJSON *cJSON_CreateNumber(double num) 1933 | { 1934 | cJSON *item = cJSON_New_Item(); 1935 | if(item) 1936 | { 1937 | item->type = cJSON_Number; 1938 | item->valuedouble = num; 1939 | item->valueint = (int)num; 1940 | } 1941 | 1942 | return item; 1943 | } 1944 | 1945 | cJSON *cJSON_CreateString(const char *string) 1946 | { 1947 | cJSON *item = cJSON_New_Item(); 1948 | if(item) 1949 | { 1950 | item->type = cJSON_String; 1951 | item->valuestring = cJSON_strdup(string); 1952 | if(!item->valuestring) 1953 | { 1954 | cJSON_Delete(item); 1955 | return NULL; 1956 | } 1957 | } 1958 | 1959 | return item; 1960 | } 1961 | 1962 | cJSON *cJSON_CreateArray(void) 1963 | { 1964 | cJSON *item = cJSON_New_Item(); 1965 | if(item) 1966 | { 1967 | item->type=cJSON_Array; 1968 | } 1969 | 1970 | return item; 1971 | } 1972 | 1973 | cJSON *cJSON_CreateObject(void) 1974 | { 1975 | cJSON *item = cJSON_New_Item(); 1976 | if (item) 1977 | { 1978 | item->type = cJSON_Object; 1979 | } 1980 | 1981 | return item; 1982 | } 1983 | 1984 | /* Create Arrays: */ 1985 | cJSON *cJSON_CreateIntArray(const int *numbers, int count) 1986 | { 1987 | int i = 0; 1988 | cJSON *n = NULL; 1989 | cJSON *p = NULL; 1990 | cJSON *a = cJSON_CreateArray(); 1991 | for(i = 0; a && (i < count); i++) 1992 | { 1993 | n = cJSON_CreateNumber(numbers[i]); 1994 | if (!n) 1995 | { 1996 | cJSON_Delete(a); 1997 | return NULL; 1998 | } 1999 | if(!i) 2000 | { 2001 | a->child = n; 2002 | } 2003 | else 2004 | { 2005 | suffix_object(p, n); 2006 | } 2007 | p = n; 2008 | } 2009 | 2010 | return a; 2011 | } 2012 | 2013 | cJSON *cJSON_CreateFloatArray(const float *numbers, int count) 2014 | { 2015 | int i = 0; 2016 | cJSON *n = NULL; 2017 | cJSON *p = NULL; 2018 | cJSON *a = cJSON_CreateArray(); 2019 | for(i = 0; a && (i < count); i++) 2020 | { 2021 | n = cJSON_CreateNumber(numbers[i]); 2022 | if(!n) 2023 | { 2024 | cJSON_Delete(a); 2025 | return NULL; 2026 | } 2027 | if(!i) 2028 | { 2029 | a->child = n; 2030 | } 2031 | else 2032 | { 2033 | suffix_object(p, n); 2034 | } 2035 | p = n; 2036 | } 2037 | 2038 | return a; 2039 | } 2040 | 2041 | cJSON *cJSON_CreateDoubleArray(const double *numbers, int count) 2042 | { 2043 | int i = 0; 2044 | cJSON *n = NULL; 2045 | cJSON *p = NULL; 2046 | cJSON *a = cJSON_CreateArray(); 2047 | for(i = 0;a && (i < count); i++) 2048 | { 2049 | n = cJSON_CreateNumber(numbers[i]); 2050 | if(!n) 2051 | { 2052 | cJSON_Delete(a); 2053 | return NULL; 2054 | } 2055 | if(!i) 2056 | { 2057 | a->child = n; 2058 | } 2059 | else 2060 | { 2061 | suffix_object(p, n); 2062 | } 2063 | p = n; 2064 | } 2065 | 2066 | return a; 2067 | } 2068 | 2069 | cJSON *cJSON_CreateStringArray(const char **strings, int count) 2070 | { 2071 | int i = 0; 2072 | cJSON *n = NULL; 2073 | cJSON *p = NULL; 2074 | cJSON *a = cJSON_CreateArray(); 2075 | for (i = 0; a && (i < count); i++) 2076 | { 2077 | n = cJSON_CreateString(strings[i]); 2078 | if(!n) 2079 | { 2080 | cJSON_Delete(a); 2081 | return NULL; 2082 | } 2083 | if(!i) 2084 | { 2085 | a->child = n; 2086 | } 2087 | else 2088 | { 2089 | suffix_object(p,n); 2090 | } 2091 | p = n; 2092 | } 2093 | 2094 | return a; 2095 | } 2096 | 2097 | /* Duplication */ 2098 | cJSON *cJSON_Duplicate(const cJSON *item, bool recurse) 2099 | { 2100 | cJSON *newitem = NULL; 2101 | cJSON *cptr = NULL; 2102 | cJSON *nptr = NULL; 2103 | cJSON *newchild = NULL; 2104 | 2105 | /* Bail on bad ptr */ 2106 | if (!item) 2107 | { 2108 | return NULL; 2109 | } 2110 | /* Create new item */ 2111 | newitem = cJSON_New_Item(); 2112 | if (!newitem) 2113 | { 2114 | return NULL; 2115 | } 2116 | /* Copy over all vars */ 2117 | newitem->type = item->type & (~cJSON_IsReference); 2118 | newitem->valueint = item->valueint; 2119 | newitem->valuedouble = item->valuedouble; 2120 | if (item->valuestring) 2121 | { 2122 | newitem->valuestring = cJSON_strdup(item->valuestring); 2123 | if (!newitem->valuestring) 2124 | { 2125 | cJSON_Delete(newitem); 2126 | return NULL; 2127 | } 2128 | } 2129 | if (item->string) 2130 | { 2131 | newitem->string = cJSON_strdup(item->string); 2132 | if (!newitem->string) 2133 | { 2134 | cJSON_Delete(newitem); 2135 | return NULL; 2136 | } 2137 | } 2138 | /* If non-recursive, then we're done! */ 2139 | if (!recurse) 2140 | { 2141 | return newitem; 2142 | } 2143 | /* Walk the ->next chain for the child. */ 2144 | cptr = item->child; 2145 | while (cptr) 2146 | { 2147 | newchild = cJSON_Duplicate(cptr, 1); /* Duplicate (with recurse) each item in the ->next chain */ 2148 | if (!newchild) 2149 | { 2150 | cJSON_Delete(newitem); 2151 | return NULL; 2152 | } 2153 | if (nptr) 2154 | { 2155 | /* If newitem->child already set, then crosswire ->prev and ->next and move on */ 2156 | nptr->next = newchild; 2157 | newchild->prev = nptr; 2158 | nptr = newchild; 2159 | } 2160 | else 2161 | { 2162 | /* Set newitem->child and move to it */ 2163 | newitem->child = newchild; nptr = newchild; 2164 | } 2165 | cptr = cptr->next; 2166 | } 2167 | 2168 | return newitem; 2169 | } 2170 | 2171 | void cJSON_Minify(char *json) 2172 | { 2173 | char *into = json; 2174 | while (*json) 2175 | { 2176 | if (*json == ' ') 2177 | { 2178 | json++; 2179 | } 2180 | else if (*json == '\t') 2181 | { 2182 | /* Whitespace characters. */ 2183 | json++; 2184 | } 2185 | else if (*json == '\r') 2186 | { 2187 | json++; 2188 | } 2189 | else if (*json=='\n') 2190 | { 2191 | json++; 2192 | } 2193 | else if ((*json == '/') && (json[1] == '/')) 2194 | { 2195 | /* double-slash comments, to end of line. */ 2196 | while (*json && (*json != '\n')) 2197 | { 2198 | json++; 2199 | } 2200 | } 2201 | else if ((*json == '/') && (json[1] == '*')) 2202 | { 2203 | /* multiline comments. */ 2204 | while (*json && !((*json == '*') && (json[1] == '/'))) 2205 | { 2206 | json++; 2207 | } 2208 | json += 2; 2209 | } 2210 | else if (*json == '\"') 2211 | { 2212 | /* string literals, which are \" sensitive. */ 2213 | *into++ = *json++; 2214 | while (*json && (*json != '\"')) 2215 | { 2216 | if (*json == '\\') 2217 | { 2218 | *into++=*json++; 2219 | } 2220 | *into++ = *json++; 2221 | } 2222 | *into++ = *json++; 2223 | } 2224 | else 2225 | { 2226 | /* All other characters. */ 2227 | *into++ = *json++; 2228 | } 2229 | } 2230 | 2231 | /* and null-terminate. */ 2232 | *into = '\0'; 2233 | } 2234 | -------------------------------------------------------------------------------- /cJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009 Dave Gamble 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef cJSON__h 24 | #define cJSON__h 25 | 26 | #ifdef __cplusplus 27 | extern "C" 28 | { 29 | #endif 30 | 31 | #include 32 | 33 | /* cJSON Types: */ 34 | #define cJSON_False (1 << 0) 35 | #define cJSON_True (1 << 1) 36 | #define cJSON_NULL (1 << 2) 37 | #define cJSON_Number (1 << 3) 38 | #define cJSON_String (1 << 4) 39 | #define cJSON_Array (1 << 5) 40 | #define cJSON_Object (1 << 6) 41 | 42 | #define cJSON_IsReference 256 43 | #define cJSON_StringIsConst 512 44 | 45 | /* The cJSON structure: */ 46 | typedef struct cJSON 47 | { 48 | /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ 49 | struct cJSON *next; 50 | struct cJSON *prev; 51 | /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ 52 | struct cJSON *child; 53 | 54 | /* The type of the item, as above. */ 55 | int type; 56 | 57 | /* The item's string, if type==cJSON_String */ 58 | char *valuestring; 59 | /* The item's number, if type==cJSON_Number */ 60 | int valueint; 61 | /* The item's number, if type==cJSON_Number */ 62 | double valuedouble; 63 | 64 | /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ 65 | char *string; 66 | } cJSON; 67 | 68 | typedef struct cJSON_Hooks 69 | { 70 | void *(*malloc_fn)(size_t sz); 71 | void (*free_fn)(void *ptr); 72 | } cJSON_Hooks; 73 | 74 | /* Supply malloc, realloc and free functions to cJSON */ 75 | extern void cJSON_InitHooks(cJSON_Hooks* hooks); 76 | 77 | 78 | /* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ 79 | extern cJSON *cJSON_Parse(const char *value); 80 | /* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ 81 | extern char *cJSON_Print(const cJSON *item); 82 | /* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ 83 | extern char *cJSON_PrintUnformatted(const cJSON *item); 84 | /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ 85 | extern char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, int fmt); 86 | /* Delete a cJSON entity and all subentities. */ 87 | extern void cJSON_Delete(cJSON *c); 88 | 89 | /* Returns the number of items in an array (or object). */ 90 | extern int cJSON_GetArraySize(const cJSON *array); 91 | /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ 92 | extern cJSON *cJSON_GetArrayItem(const cJSON *array, int item); 93 | /* Get item "string" from object. Case insensitive. */ 94 | extern cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string); 95 | extern int cJSON_HasObjectItem(const cJSON *object, const char *string); 96 | /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ 97 | extern const char *cJSON_GetErrorPtr(void); 98 | 99 | /* These calls create a cJSON item of the appropriate type. */ 100 | extern cJSON *cJSON_CreateNull(void); 101 | extern cJSON *cJSON_CreateTrue(void); 102 | extern cJSON *cJSON_CreateFalse(void); 103 | extern cJSON *cJSON_CreateBool(int b); 104 | extern cJSON *cJSON_CreateNumber(double num); 105 | extern cJSON *cJSON_CreateString(const char *string); 106 | extern cJSON *cJSON_CreateArray(void); 107 | extern cJSON *cJSON_CreateObject(void); 108 | 109 | /* These utilities create an Array of count items. */ 110 | extern cJSON *cJSON_CreateIntArray(const int *numbers, int count); 111 | extern cJSON *cJSON_CreateFloatArray(const float *numbers, int count); 112 | extern cJSON *cJSON_CreateDoubleArray(const double *numbers, int count); 113 | extern cJSON *cJSON_CreateStringArray(const char **strings, int count); 114 | 115 | /* Append item to the specified array/object. */ 116 | extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); 117 | extern void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); 118 | extern void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */ 119 | /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ 120 | extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); 121 | extern void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); 122 | 123 | /* Remove/Detatch items from Arrays/Objects. */ 124 | extern cJSON *cJSON_DetachItemFromArray(cJSON *array, int which); 125 | extern void cJSON_DeleteItemFromArray(cJSON *array, int which); 126 | extern cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string); 127 | extern void cJSON_DeleteItemFromObject(cJSON *object, const char *string); 128 | 129 | /* Update array items. */ 130 | extern void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ 131 | extern void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); 132 | extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); 133 | 134 | /* Duplicate a cJSON item */ 135 | extern cJSON *cJSON_Duplicate(const cJSON *item, int recurse); 136 | /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will 137 | need to be released. With recurse!=0, it will duplicate any children connected to the item. 138 | The item->next and ->prev pointers are always zero on return from Duplicate. */ 139 | 140 | /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ 141 | /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */ 142 | extern cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, int require_null_terminated); 143 | 144 | extern void cJSON_Minify(char *json); 145 | 146 | /* Macros for creating things quickly. */ 147 | #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) 148 | #define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) 149 | #define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) 150 | #define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) 151 | #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) 152 | #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) 153 | 154 | /* When assigning an integer value, it needs to be propagated to valuedouble too. */ 155 | #define cJSON_SetIntValue(object,val) ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val)) 156 | #define cJSON_SetNumberValue(object,val) ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val)) 157 | 158 | /* Macro for iterating over an array */ 159 | #define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next) 160 | 161 | #ifdef __cplusplus 162 | } 163 | #endif 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /cJSONConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # Whether the utils lib was build. 2 | set(CJSON_UTILS_FOUND @ENABLE_CJSON_UTILS@) 3 | 4 | # The include directories used by cJSON 5 | set(CJSON_INCLUDE_DIRS "@prefix@/@includedir@") 6 | set(CJSON_INCLUDE_DIR "@prefix@/@includedir@") 7 | 8 | get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" PATH) 9 | 10 | # The cJSON library 11 | set(CJSON_LIBRARY "@CJSON_LIB@") 12 | if(@ENABLE_TARGET_EXPORT@) 13 | # Include the target 14 | include("${_dir}/cjson.cmake") 15 | endif() 16 | 17 | if(CJSON_UTILS_FOUND) 18 | # The cJSON utils library 19 | set(CJSON_UTILS_LIBRARY @CJSON_UTILS_LIB@) 20 | # All cJSON libraries 21 | set(CJSON_LIBRARIES "@CJSON_UTILS_LIB@" "@CJSON_LIB@") 22 | if(@ENABLE_TARGET_EXPORT@) 23 | # Include the target 24 | include("${_dir}/cjson_utils.cmake") 25 | endif() 26 | else() 27 | # All cJSON libraries 28 | set(CJSON_LIBRARIES "@CJSON_LIB@") 29 | endif() 30 | -------------------------------------------------------------------------------- /cJSONConfigVersion.cmake.in: -------------------------------------------------------------------------------- 1 | set(PACKAGE_VERSION "@PROJECT_VERSION@") 2 | 3 | # Check whether the requested PACKAGE_FIND_VERSION is compatible 4 | if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") 5 | set(PACKAGE_VERSION_COMPATIBLE FALSE) 6 | else() 7 | set(PACKAGE_VERSION_COMPATIBLE TRUE) 8 | if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") 9 | set(PACKAGE_VERSION_EXACT TRUE) 10 | endif() 11 | endif() 12 | -------------------------------------------------------------------------------- /cJSON_Utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "cJSON_Utils.h" 6 | 7 | static char* cJSONUtils_strdup(const char* str) 8 | { 9 | size_t len = 0; 10 | char *copy = NULL; 11 | 12 | len = strlen(str) + 1; 13 | if (!(copy = (char*)malloc(len))) 14 | { 15 | return NULL; 16 | } 17 | memcpy(copy, str, len); 18 | 19 | return copy; 20 | } 21 | 22 | static int cJSONUtils_strcasecmp(const char *s1, const char *s2) 23 | { 24 | if (!s1) 25 | { 26 | return (s1 == s2) ? 0 : 1; /* both NULL? */ 27 | } 28 | if (!s2) 29 | { 30 | return 1; 31 | } 32 | for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) 33 | { 34 | if(*s1 == 0) 35 | { 36 | return 0; 37 | } 38 | } 39 | 40 | return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); 41 | } 42 | 43 | /* JSON Pointer implementation: */ 44 | static int cJSONUtils_Pstrcasecmp(const char *a, const char *e) 45 | { 46 | if (!a || !e) 47 | { 48 | return (a == e) ? 0 : 1; /* both NULL? */ 49 | } 50 | for (; *a && *e && (*e != '/'); a++, e++) /* compare until next '/' */ 51 | { 52 | if (*e == '~') 53 | { 54 | /* check for escaped '~' (~0) and '/' (~1) */ 55 | if (!((e[1] == '0') && (*a == '~')) && !((e[1] == '1') && (*a == '/'))) 56 | { 57 | /* invalid escape sequence or wrong character in *a */ 58 | return 1; 59 | } 60 | else 61 | { 62 | e++; 63 | } 64 | } 65 | else if (tolower(*a) != tolower(*e)) 66 | { 67 | return 1; 68 | } 69 | } 70 | if (((*e != 0) && (*e != '/')) != (*a != 0)) 71 | { 72 | /* one string has ended, the other not */ 73 | return 1; 74 | } 75 | 76 | return 0; 77 | } 78 | 79 | static int cJSONUtils_PointerEncodedstrlen(const char *s) 80 | { 81 | int l = 0; 82 | for (; *s; s++, l++) 83 | { 84 | if ((*s == '~') || (*s == '/')) 85 | { 86 | l++; 87 | } 88 | } 89 | 90 | return l; 91 | } 92 | 93 | static void cJSONUtils_PointerEncodedstrcpy(char *d, const char *s) 94 | { 95 | for (; *s; s++) 96 | { 97 | if (*s == '/') 98 | { 99 | *d++ = '~'; 100 | *d++ = '1'; 101 | } 102 | else if (*s == '~') 103 | { 104 | *d++ = '~'; 105 | *d++ = '0'; 106 | } 107 | else 108 | { 109 | *d++ = *s; 110 | } 111 | } 112 | 113 | *d = '\0'; 114 | } 115 | 116 | char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target) 117 | { 118 | int type = object->type; 119 | int c = 0; 120 | cJSON *obj = 0; 121 | 122 | if (object == target) 123 | { 124 | /* found */ 125 | return cJSONUtils_strdup(""); 126 | } 127 | 128 | /* recursively search all children of the object */ 129 | for (obj = object->child; obj; obj = obj->next, c++) 130 | { 131 | char *found = cJSONUtils_FindPointerFromObjectTo(obj, target); 132 | if (found) 133 | { 134 | if ((type & 0xFF) == cJSON_Array) 135 | { 136 | /* reserve enough memory for a 64 bit integer + '/' and '\0' */ 137 | char *ret = (char*)malloc(strlen(found) + 23); 138 | sprintf(ret, "/%d%s", c, found); /* / */ 139 | free(found); 140 | 141 | return ret; 142 | } 143 | else if ((type & 0xFF) == cJSON_Object) 144 | { 145 | char *ret = (char*)malloc(strlen(found) + cJSONUtils_PointerEncodedstrlen(obj->string) + 2); 146 | *ret = '/'; 147 | cJSONUtils_PointerEncodedstrcpy(ret + 1, obj->string); 148 | strcat(ret, found); 149 | free(found); 150 | 151 | return ret; 152 | } 153 | 154 | /* reached leaf of the tree, found nothing */ 155 | free(found); 156 | return NULL; 157 | } 158 | } 159 | 160 | /* not found */ 161 | return NULL; 162 | } 163 | 164 | cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer) 165 | { 166 | /* follow path of the pointer */ 167 | while ((*pointer++ == '/') && object) 168 | { 169 | if ((object->type & 0xFF) == cJSON_Array) 170 | { 171 | int which = 0; 172 | /* parse array index */ 173 | while ((*pointer >= '0') && (*pointer <= '9')) 174 | { 175 | which = (10 * which) + (*pointer++ - '0'); 176 | } 177 | if (*pointer && (*pointer != '/')) 178 | { 179 | /* not end of string or new path token */ 180 | return NULL; 181 | } 182 | object = cJSON_GetArrayItem(object, which); 183 | } 184 | else if ((object->type & 0xFF) == cJSON_Object) 185 | { 186 | object = object->child; 187 | /* GetObjectItem. */ 188 | while (object && cJSONUtils_Pstrcasecmp(object->string, pointer)) 189 | { 190 | object = object->next; 191 | } 192 | /* skip to the next path token or end of string */ 193 | while (*pointer && (*pointer != '/')) 194 | { 195 | pointer++; 196 | } 197 | } 198 | else 199 | { 200 | return NULL; 201 | } 202 | } 203 | 204 | return object; 205 | } 206 | 207 | /* JSON Patch implementation. */ 208 | static void cJSONUtils_InplaceDecodePointerString(char *string) 209 | { 210 | char *s2 = string; 211 | for (; *string; s2++, string++) 212 | { 213 | *s2 = (*string != '~') 214 | ? (*string) 215 | : ((*(++string) == '0') 216 | ? '~' 217 | : '/'); 218 | } 219 | 220 | *s2 = '\0'; 221 | } 222 | 223 | static cJSON *cJSONUtils_PatchDetach(cJSON *object, const char *path) 224 | { 225 | char *parentptr = NULL; 226 | char *childptr = NULL; 227 | cJSON *parent = NULL; 228 | cJSON *ret = NULL; 229 | 230 | /* copy path and split it in parent and child */ 231 | parentptr = cJSONUtils_strdup(path); 232 | childptr = strrchr(parentptr, '/'); /* last '/' */ 233 | if (childptr) 234 | { 235 | /* split strings */ 236 | *childptr++ = '\0'; 237 | } 238 | parent = cJSONUtils_GetPointer(object, parentptr); 239 | cJSONUtils_InplaceDecodePointerString(childptr); 240 | 241 | if (!parent) 242 | { 243 | /* Couldn't find object to remove child from. */ 244 | ret = NULL; 245 | } 246 | else if ((parent->type & 0xFF) == cJSON_Array) 247 | { 248 | ret = cJSON_DetachItemFromArray(parent, atoi(childptr)); 249 | } 250 | else if ((parent->type & 0xFF) == cJSON_Object) 251 | { 252 | ret = cJSON_DetachItemFromObject(parent, childptr); 253 | } 254 | free(parentptr); 255 | 256 | /* return the detachted item */ 257 | return ret; 258 | } 259 | 260 | static int cJSONUtils_Compare(cJSON *a, cJSON *b) 261 | { 262 | if ((a->type & 0xFF) != (b->type & 0xFF)) 263 | { 264 | /* mismatched type. */ 265 | return -1; 266 | } 267 | switch (a->type & 0xFF) 268 | { 269 | case cJSON_Number: 270 | /* numeric mismatch. */ 271 | return ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble)) ? -2 : 0; 272 | case cJSON_String: 273 | /* string mismatch. */ 274 | return (strcmp(a->valuestring, b->valuestring) != 0) ? -3 : 0; 275 | case cJSON_Array: 276 | for (a = a->child, b = b->child; a && b; a = a->next, b = b->next) 277 | { 278 | int err = cJSONUtils_Compare(a, b); 279 | if (err) 280 | { 281 | return err; 282 | } 283 | } 284 | /* array size mismatch? (one of both children is not NULL) */ 285 | return (a || b) ? -4 : 0; 286 | case cJSON_Object: 287 | cJSONUtils_SortObject(a); 288 | cJSONUtils_SortObject(b); 289 | a = a->child; 290 | b = b->child; 291 | while (a && b) 292 | { 293 | int err = 0; 294 | /* compare object keys */ 295 | if (cJSONUtils_strcasecmp(a->string, b->string)) 296 | { 297 | /* missing member */ 298 | return -6; 299 | } 300 | err = cJSONUtils_Compare(a, b); 301 | if (err) 302 | { 303 | return err; 304 | } 305 | a = a->next; 306 | b = b->next; 307 | } 308 | /* object length mismatch (one of both children is not null) */ 309 | return (a || b) ? -5 : 0; 310 | 311 | default: 312 | break; 313 | } 314 | /* null, true or false */ 315 | return 0; 316 | } 317 | 318 | static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch) 319 | { 320 | cJSON *op = NULL; 321 | cJSON *path = NULL; 322 | cJSON *value = NULL; 323 | cJSON *parent = NULL; 324 | int opcode = 0; 325 | char *parentptr = NULL; 326 | char *childptr = NULL; 327 | 328 | op = cJSON_GetObjectItem(patch, "op"); 329 | path = cJSON_GetObjectItem(patch, "path"); 330 | if (!op || !path) 331 | { 332 | /* malformed patch. */ 333 | return 2; 334 | } 335 | 336 | /* decode operation */ 337 | if (!strcmp(op->valuestring, "add")) 338 | { 339 | opcode = 0; 340 | } 341 | else if (!strcmp(op->valuestring, "remove")) 342 | { 343 | opcode = 1; 344 | } 345 | else if (!strcmp(op->valuestring, "replace")) 346 | { 347 | opcode = 2; 348 | } 349 | else if (!strcmp(op->valuestring, "move")) 350 | { 351 | opcode = 3; 352 | } 353 | else if (!strcmp(op->valuestring, "copy")) 354 | { 355 | opcode = 4; 356 | } 357 | else if (!strcmp(op->valuestring, "test")) 358 | { 359 | /* compare value: {...} with the given path */ 360 | return cJSONUtils_Compare(cJSONUtils_GetPointer(object, path->valuestring), cJSON_GetObjectItem(patch, "value")); 361 | } 362 | else 363 | { 364 | /* unknown opcode. */ 365 | return 3; 366 | } 367 | 368 | /* Remove/Replace */ 369 | if ((opcode == 1) || (opcode == 2)) 370 | { 371 | /* Get rid of old. */ 372 | cJSON_Delete(cJSONUtils_PatchDetach(object, path->valuestring)); 373 | if (opcode == 1) 374 | { 375 | /* For Remove, this is job done. */ 376 | return 0; 377 | } 378 | } 379 | 380 | /* Copy/Move uses "from". */ 381 | if ((opcode == 3) || (opcode == 4)) 382 | { 383 | cJSON *from = cJSON_GetObjectItem(patch, "from"); 384 | if (!from) 385 | { 386 | /* missing "from" for copy/move. */ 387 | return 4; 388 | } 389 | 390 | if (opcode == 3) 391 | { 392 | /* move */ 393 | value = cJSONUtils_PatchDetach(object, from->valuestring); 394 | } 395 | if (opcode == 4) 396 | { 397 | /* copy */ 398 | value = cJSONUtils_GetPointer(object, from->valuestring); 399 | } 400 | if (!value) 401 | { 402 | /* missing "from" for copy/move. */ 403 | return 5; 404 | } 405 | if (opcode == 4) 406 | { 407 | value = cJSON_Duplicate(value, 1); 408 | } 409 | if (!value) 410 | { 411 | /* out of memory for copy/move. */ 412 | return 6; 413 | } 414 | } 415 | else /* Add/Replace uses "value". */ 416 | { 417 | value = cJSON_GetObjectItem(patch, "value"); 418 | if (!value) 419 | { 420 | /* missing "value" for add/replace. */ 421 | return 7; 422 | } 423 | value = cJSON_Duplicate(value, 1); 424 | if (!value) 425 | { 426 | /* out of memory for add/replace. */ 427 | return 8; 428 | } 429 | } 430 | 431 | /* Now, just add "value" to "path". */ 432 | 433 | /* split pointer in parent and child */ 434 | parentptr = cJSONUtils_strdup(path->valuestring); 435 | childptr = strrchr(parentptr, '/'); 436 | if (childptr) 437 | { 438 | *childptr++ = '\0'; 439 | } 440 | parent = cJSONUtils_GetPointer(object, parentptr); 441 | cJSONUtils_InplaceDecodePointerString(childptr); 442 | 443 | /* add, remove, replace, move, copy, test. */ 444 | if (!parent) 445 | { 446 | /* Couldn't find object to add to. */ 447 | free(parentptr); 448 | cJSON_Delete(value); 449 | return 9; 450 | } 451 | else if ((parent->type & 0xFF) == cJSON_Array) 452 | { 453 | if (!strcmp(childptr, "-")) 454 | { 455 | cJSON_AddItemToArray(parent, value); 456 | } 457 | else 458 | { 459 | cJSON_InsertItemInArray(parent, atoi(childptr), value); 460 | } 461 | } 462 | else if ((parent->type & 0xFF) == cJSON_Object) 463 | { 464 | cJSON_DeleteItemFromObject(parent, childptr); 465 | cJSON_AddItemToObject(parent, childptr, value); 466 | } 467 | else 468 | { 469 | cJSON_Delete(value); 470 | } 471 | free(parentptr); 472 | 473 | return 0; 474 | } 475 | 476 | int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches) 477 | { 478 | int err = 0; 479 | if ((patches->type & 0xFF) != cJSON_Array) 480 | { 481 | /* malformed patches. */ 482 | return 1; 483 | } 484 | if (patches) 485 | { 486 | patches = patches->child; 487 | } 488 | while (patches) 489 | { 490 | if ((err = cJSONUtils_ApplyPatch(object, patches))) 491 | { 492 | return err; 493 | } 494 | patches = patches->next; 495 | } 496 | 497 | return 0; 498 | } 499 | 500 | static void cJSONUtils_GeneratePatch(cJSON *patches, const char *op, const char *path, const char *suffix, cJSON *val) 501 | { 502 | cJSON *patch = cJSON_CreateObject(); 503 | cJSON_AddItemToObject(patch, "op", cJSON_CreateString(op)); 504 | if (suffix) 505 | { 506 | char *newpath = (char*)malloc(strlen(path) + cJSONUtils_PointerEncodedstrlen(suffix) + 2); 507 | cJSONUtils_PointerEncodedstrcpy(newpath + sprintf(newpath, "%s/", path), suffix); 508 | cJSON_AddItemToObject(patch, "path", cJSON_CreateString(newpath)); 509 | free(newpath); 510 | } 511 | else 512 | { 513 | cJSON_AddItemToObject(patch, "path", cJSON_CreateString(path)); 514 | } 515 | if (val) 516 | { 517 | cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(val, 1)); 518 | } 519 | cJSON_AddItemToArray(patches, patch); 520 | } 521 | 522 | void cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val) 523 | { 524 | cJSONUtils_GeneratePatch(array, op, path, 0, val); 525 | } 526 | 527 | static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *from, cJSON *to) 528 | { 529 | if ((from->type & 0xFF) != (to->type & 0xFF)) 530 | { 531 | cJSONUtils_GeneratePatch(patches, "replace", path, 0, to); 532 | return; 533 | } 534 | 535 | switch ((from->type & 0xFF)) 536 | { 537 | case cJSON_Number: 538 | if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble)) 539 | { 540 | cJSONUtils_GeneratePatch(patches, "replace", path, 0, to); 541 | } 542 | return; 543 | 544 | case cJSON_String: 545 | if (strcmp(from->valuestring, to->valuestring) != 0) 546 | { 547 | cJSONUtils_GeneratePatch(patches, "replace", path, 0, to); 548 | } 549 | return; 550 | 551 | case cJSON_Array: 552 | { 553 | int c = 0; 554 | char *newpath = (char*)malloc(strlen(path) + 23); /* Allow space for 64bit int. */ 555 | /* generate patches for all array elements that exist in "from" and "to" */ 556 | for (c = 0, from = from->child, to = to->child; from && to; from = from->next, to = to->next, c++) 557 | { 558 | sprintf(newpath, "%s/%d", path, c); /* path of the current array element */ 559 | cJSONUtils_CompareToPatch(patches, newpath, from, to); 560 | } 561 | /* remove leftover elements from 'from' that are not in 'to' */ 562 | for (; from; from = from->next, c++) 563 | { 564 | sprintf(newpath, "%d", c); 565 | cJSONUtils_GeneratePatch(patches, "remove", path, newpath, 0); 566 | } 567 | /* add new elements in 'to' that were not in 'from' */ 568 | for (; to; to = to->next, c++) 569 | { 570 | cJSONUtils_GeneratePatch(patches, "add", path, "-", to); 571 | } 572 | free(newpath); 573 | return; 574 | } 575 | 576 | case cJSON_Object: 577 | { 578 | cJSON *a = NULL; 579 | cJSON *b = NULL; 580 | cJSONUtils_SortObject(from); 581 | cJSONUtils_SortObject(to); 582 | 583 | a = from->child; 584 | b = to->child; 585 | /* for all object values in the object with more of them */ 586 | while (a || b) 587 | { 588 | int diff = (!a) ? 1 : ((!b) ? -1 : cJSONUtils_strcasecmp(a->string, b->string)); 589 | if (!diff) 590 | { 591 | /* both object keys are the same */ 592 | char *newpath = (char*)malloc(strlen(path) + cJSONUtils_PointerEncodedstrlen(a->string) + 2); 593 | cJSONUtils_PointerEncodedstrcpy(newpath + sprintf(newpath, "%s/", path), a->string); 594 | /* create a patch for the element */ 595 | cJSONUtils_CompareToPatch(patches, newpath, a, b); 596 | free(newpath); 597 | a = a->next; 598 | b = b->next; 599 | } 600 | else if (diff < 0) 601 | { 602 | /* object element doesn't exist in 'to' --> remove it */ 603 | cJSONUtils_GeneratePatch(patches, "remove", path, a->string, 0); 604 | a = a->next; 605 | } 606 | else 607 | { 608 | /* object element doesn't exist in 'from' --> add it */ 609 | cJSONUtils_GeneratePatch(patches, "add", path, b->string, b); 610 | b = b->next; 611 | } 612 | } 613 | return; 614 | } 615 | 616 | default: 617 | break; 618 | } 619 | } 620 | 621 | cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to) 622 | { 623 | cJSON *patches = cJSON_CreateArray(); 624 | cJSONUtils_CompareToPatch(patches, "", from, to); 625 | 626 | return patches; 627 | } 628 | 629 | /* sort lists using mergesort */ 630 | static cJSON *cJSONUtils_SortList(cJSON *list) 631 | { 632 | cJSON *first = list; 633 | cJSON *second = list; 634 | cJSON *ptr = list; 635 | 636 | if (!list || !list->next) 637 | { 638 | /* One entry is sorted already. */ 639 | return list; 640 | } 641 | 642 | while (ptr && ptr->next && (cJSONUtils_strcasecmp(ptr->string, ptr->next->string) < 0)) 643 | { 644 | /* Test for list sorted. */ 645 | ptr = ptr->next; 646 | } 647 | if (!ptr || !ptr->next) 648 | { 649 | /* Leave sorted lists unmodified. */ 650 | return list; 651 | } 652 | 653 | /* reset ptr to the beginning */ 654 | ptr = list; 655 | while (ptr) 656 | { 657 | /* Walk two pointers to find the middle. */ 658 | second = second->next; 659 | ptr = ptr->next; 660 | /* advances ptr two steps at a time */ 661 | if (ptr) 662 | { 663 | ptr = ptr->next; 664 | } 665 | } 666 | if (second && second->prev) 667 | { 668 | /* Split the lists */ 669 | second->prev->next = NULL; 670 | } 671 | 672 | /* Recursively sort the sub-lists. */ 673 | first = cJSONUtils_SortList(first); 674 | second = cJSONUtils_SortList(second); 675 | list = ptr = NULL; 676 | 677 | while (first && second) /* Merge the sub-lists */ 678 | { 679 | if (cJSONUtils_strcasecmp(first->string, second->string) < 0) 680 | { 681 | if (!list) 682 | { 683 | /* start merged list with the first element of the first list */ 684 | list = ptr = first; 685 | } 686 | else 687 | { 688 | /* add first element of first list to merged list */ 689 | ptr->next = first; 690 | first->prev = ptr; 691 | ptr = first; 692 | } 693 | first = first->next; 694 | } 695 | else 696 | { 697 | if (!list) 698 | { 699 | /* start merged list with the first element of the second list */ 700 | list = ptr = second; 701 | } 702 | else 703 | { 704 | /* add first element of second list to merged list */ 705 | ptr->next = second; 706 | second->prev = ptr; 707 | ptr = second; 708 | } 709 | second = second->next; 710 | } 711 | } 712 | if (first) 713 | { 714 | /* Append rest of first list. */ 715 | if (!list) 716 | { 717 | return first; 718 | } 719 | ptr->next = first; 720 | first->prev = ptr; 721 | } 722 | if (second) 723 | { 724 | /* Append rest of second list */ 725 | if (!list) 726 | { 727 | return second; 728 | } 729 | ptr->next = second; 730 | second->prev = ptr; 731 | } 732 | 733 | return list; 734 | } 735 | 736 | void cJSONUtils_SortObject(cJSON *object) 737 | { 738 | object->child = cJSONUtils_SortList(object->child); 739 | } 740 | 741 | cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch) 742 | { 743 | if (!patch || ((patch->type & 0xFF) != cJSON_Object)) 744 | { 745 | /* scalar value, array or NULL, just duplicate */ 746 | cJSON_Delete(target); 747 | return cJSON_Duplicate(patch, 1); 748 | } 749 | 750 | if (!target || ((target->type & 0xFF) != cJSON_Object)) 751 | { 752 | cJSON_Delete(target); 753 | target = cJSON_CreateObject(); 754 | } 755 | 756 | patch = patch->child; 757 | while (patch) 758 | { 759 | if ((patch->type & 0xFF) == cJSON_NULL) 760 | { 761 | /* NULL is the indicator to remove a value, see RFC7396 */ 762 | cJSON_DeleteItemFromObject(target, patch->string); 763 | } 764 | else 765 | { 766 | cJSON *replaceme = cJSON_DetachItemFromObject(target, patch->string); 767 | cJSON_AddItemToObject(target, patch->string, cJSONUtils_MergePatch(replaceme, patch)); 768 | } 769 | patch = patch->next; 770 | } 771 | return target; 772 | } 773 | 774 | cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to) 775 | { 776 | cJSON *patch = NULL; 777 | if (!to) 778 | { 779 | /* patch to delete everything */ 780 | return cJSON_CreateNull(); 781 | } 782 | if (((to->type & 0xFF) != cJSON_Object) || !from || ((from->type & 0xFF) != cJSON_Object)) 783 | { 784 | return cJSON_Duplicate(to, 1); 785 | } 786 | 787 | cJSONUtils_SortObject(from); 788 | cJSONUtils_SortObject(to); 789 | 790 | from = from->child; 791 | to = to->child; 792 | patch = cJSON_CreateObject(); 793 | while (from || to) 794 | { 795 | int compare = from ? (to ? strcmp(from->string, to->string) : -1) : 1; 796 | if (compare < 0) 797 | { 798 | /* from has a value that to doesn't have -> remove */ 799 | cJSON_AddItemToObject(patch, from->string, cJSON_CreateNull()); 800 | from = from->next; 801 | } 802 | else if (compare > 0) 803 | { 804 | /* to has a value that from doesn't have -> add to patch */ 805 | cJSON_AddItemToObject(patch, to->string, cJSON_Duplicate(to, 1)); 806 | to = to->next; 807 | } 808 | else 809 | { 810 | /* object key exists in both objects */ 811 | if (cJSONUtils_Compare(from, to)) 812 | { 813 | /* not identical --> generate a patch */ 814 | cJSON_AddItemToObject(patch, to->string, cJSONUtils_GenerateMergePatch(from, to)); 815 | } 816 | /* next key in the object */ 817 | from = from->next; 818 | to = to->next; 819 | } 820 | } 821 | if (!patch->child) 822 | { 823 | cJSON_Delete(patch); 824 | return NULL; 825 | } 826 | 827 | return patch; 828 | } 829 | -------------------------------------------------------------------------------- /cJSON_Utils.h: -------------------------------------------------------------------------------- 1 | #include "cJSON.h" 2 | 3 | /* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */ 4 | cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer); 5 | 6 | /* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */ 7 | cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to); 8 | /* Utility for generating patch array entries. */ 9 | void cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val); 10 | /* Returns 0 for success. */ 11 | int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches); 12 | 13 | /* 14 | // Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use: 15 | //int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches) 16 | //{ 17 | // cJSON *modme = cJSON_Duplicate(*object, 1); 18 | // int error = cJSONUtils_ApplyPatches(modme, patches); 19 | // if (!error) 20 | // { 21 | // cJSON_Delete(*object); 22 | // *object = modme; 23 | // } 24 | // else 25 | // { 26 | // cJSON_Delete(modme); 27 | // } 28 | // 29 | // return error; 30 | //} 31 | // Code not added to library since this strategy is a LOT slower. 32 | */ 33 | 34 | /* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */ 35 | /* target will be modified by patch. return value is new ptr for target. */ 36 | cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch); 37 | /* generates a patch to move from -> to */ 38 | cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to); 39 | 40 | /* Given a root object and a target object, construct a pointer from one to the other. */ 41 | char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target); 42 | 43 | /* Sorts the members of the object into alphabetical order. */ 44 | void cJSONUtils_SortObject(cJSON *object); 45 | -------------------------------------------------------------------------------- /libcjson.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | libdir=${prefix}/@libdir@ 3 | includedir=${prefix}/@includedir@ 4 | 5 | Name: libcjson 6 | Version: @version@ 7 | Description: Ultralightweight JSON parser in ANSI C 8 | URL: https://github.com/DaveGamble/cJSON 9 | Libs: -L${libdir} -lcjson 10 | Libs.Private: -lm 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /libcjson_utils.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | libdir=${prefix}/@libdir@ 3 | includedir=${prefix}/@includedir@ 4 | 5 | Name: libcjson_utils 6 | Version: @version@ 7 | Description: An implementation of JSON Pointer, Patch and Merge Patch based on cJSON. 8 | URL: https://github.com/DaveGamble/cJSON 9 | Libs: -L${libdir} -lcjson_utils 10 | Cflags: -I${includedir} 11 | Requires: libcjson 12 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009 Dave Gamble 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include "cJSON.h" 26 | 27 | /* Parse text to JSON, then render back to text, and print! */ 28 | void doit(char *text) 29 | { 30 | char *out = NULL; 31 | cJSON *json = NULL; 32 | 33 | json = cJSON_Parse(text); 34 | if (!json) 35 | { 36 | printf("Error before: [%s]\n", cJSON_GetErrorPtr()); 37 | } 38 | else 39 | { 40 | out = cJSON_Print(json); 41 | cJSON_Delete(json); 42 | printf("%s\n", out); 43 | free(out); 44 | } 45 | } 46 | 47 | /* Read a file, parse, render back, etc. */ 48 | void dofile(char *filename) 49 | { 50 | FILE *f = NULL; 51 | long len = 0; 52 | char *data = NULL; 53 | 54 | /* open in read binary mode */ 55 | f = fopen(filename,"rb"); 56 | /* get the length */ 57 | fseek(f, 0, SEEK_END); 58 | len = ftell(f); 59 | fseek(f, 0, SEEK_SET); 60 | 61 | data = (char*)malloc(len + 1); 62 | 63 | fread(data, 1, len, f); 64 | data[len] = '\0'; 65 | fclose(f); 66 | 67 | doit(data); 68 | free(data); 69 | } 70 | 71 | /* Used by some code below as an example datatype. */ 72 | struct record 73 | { 74 | const char *precision; 75 | double lat; 76 | double lon; 77 | const char *address; 78 | const char *city; 79 | const char *state; 80 | const char *zip; 81 | const char *country; 82 | }; 83 | 84 | /* Create a bunch of objects as demonstration. */ 85 | void create_objects(void) 86 | { 87 | /* declare a few. */ 88 | cJSON *root = NULL; 89 | cJSON *fmt = NULL; 90 | cJSON *img = NULL; 91 | cJSON *thm = NULL; 92 | cJSON *fld = NULL; 93 | char *out = NULL; 94 | int i = 0; 95 | 96 | /* Our "days of the week" array: */ 97 | const char *strings[7] = 98 | { 99 | "Sunday", 100 | "Monday", 101 | "Tuesday", 102 | "Wednesday", 103 | "Thursday", 104 | "Friday", 105 | "Saturday" 106 | }; 107 | /* Our matrix: */ 108 | int numbers[3][3] = 109 | { 110 | {0, -1, 0}, 111 | {1, 0, 0}, 112 | {0 ,0, 1} 113 | }; 114 | /* Our "gallery" item: */ 115 | int ids[4] = { 116, 943, 234, 38793 }; 116 | /* Our array of "records": */ 117 | struct record fields[2] = 118 | { 119 | { 120 | "zip", 121 | 37.7668, 122 | -1.223959e+2, 123 | "", 124 | "SAN FRANCISCO", 125 | "CA", 126 | "94107", 127 | "US" 128 | }, 129 | { 130 | "zip", 131 | 37.371991, 132 | -1.22026e+2, 133 | "", 134 | "SUNNYVALE", 135 | "CA", 136 | "94085", 137 | "US" 138 | } 139 | }; 140 | volatile double zero = 0.0; 141 | 142 | /* Here we construct some JSON standards, from the JSON site. */ 143 | 144 | /* Our "Video" datatype: */ 145 | root = cJSON_CreateObject(); 146 | cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble")); 147 | cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject()); 148 | cJSON_AddStringToObject(fmt, "type", "rect"); 149 | cJSON_AddNumberToObject(fmt, "width", 1920); 150 | cJSON_AddNumberToObject(fmt, "height", 1080); 151 | cJSON_AddFalseToObject (fmt, "interlace"); 152 | cJSON_AddNumberToObject(fmt, "frame rate", 24); 153 | 154 | /* Print to text */ 155 | out = cJSON_Print(root); 156 | /* Delete the cJSON */ 157 | cJSON_Delete(root); 158 | /* print it */ 159 | printf("%s\n",out); 160 | /* release the string */ 161 | free(out); 162 | 163 | /* Our "days of the week" array: */ 164 | root = cJSON_CreateStringArray(strings, 7); 165 | 166 | out = cJSON_Print(root); 167 | cJSON_Delete(root); 168 | printf("%s\n", out); 169 | free(out); 170 | 171 | /* Our matrix: */ 172 | root = cJSON_CreateArray(); 173 | for (i = 0; i < 3; i++) 174 | { 175 | cJSON_AddItemToArray(root, cJSON_CreateIntArray(numbers[i], 3)); 176 | } 177 | 178 | /* cJSON_ReplaceItemInArray(root, 1, cJSON_CreateString("Replacement")); */ 179 | 180 | out = cJSON_Print(root); 181 | cJSON_Delete(root); 182 | printf("%s\n", out); 183 | free(out); 184 | 185 | 186 | /* Our "gallery" item: */ 187 | root = cJSON_CreateObject(); 188 | cJSON_AddItemToObject(root, "Image", img = cJSON_CreateObject()); 189 | cJSON_AddNumberToObject(img, "Width", 800); 190 | cJSON_AddNumberToObject(img, "Height", 600); 191 | cJSON_AddStringToObject(img, "Title", "View from 15th Floor"); 192 | cJSON_AddItemToObject(img, "Thumbnail", thm = cJSON_CreateObject()); 193 | cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943"); 194 | cJSON_AddNumberToObject(thm, "Height", 125); 195 | cJSON_AddStringToObject(thm, "Width", "100"); 196 | cJSON_AddItemToObject(img, "IDs", cJSON_CreateIntArray(ids, 4)); 197 | 198 | out = cJSON_Print(root); 199 | cJSON_Delete(root); 200 | printf("%s\n", out); 201 | free(out); 202 | 203 | /* Our array of "records": */ 204 | 205 | root = cJSON_CreateArray(); 206 | for (i = 0; i < 2; i++) 207 | { 208 | cJSON_AddItemToArray(root, fld = cJSON_CreateObject()); 209 | cJSON_AddStringToObject(fld, "precision", fields[i].precision); 210 | cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat); 211 | cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon); 212 | cJSON_AddStringToObject(fld, "Address", fields[i].address); 213 | cJSON_AddStringToObject(fld, "City", fields[i].city); 214 | cJSON_AddStringToObject(fld, "State", fields[i].state); 215 | cJSON_AddStringToObject(fld, "Zip", fields[i].zip); 216 | cJSON_AddStringToObject(fld, "Country", fields[i].country); 217 | } 218 | 219 | /* cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root, 1), "City", cJSON_CreateIntArray(ids, 4)); */ 220 | 221 | out = cJSON_Print(root); 222 | cJSON_Delete(root); 223 | printf("%s\n", out); 224 | free(out); 225 | 226 | root = cJSON_CreateObject(); 227 | cJSON_AddNumberToObject(root, "number", 1.0 / zero); 228 | out = cJSON_Print(root); 229 | cJSON_Delete(root); 230 | printf("%s\n", out); 231 | free(out); 232 | } 233 | 234 | int main(void) 235 | { 236 | /* a bunch of json: */ 237 | char text1[] = 238 | "{\n" 239 | "\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n" 240 | "\"format\": {\"type\": \"rect\", \n" 241 | "\"width\": 1920, \n" 242 | "\"height\": 1080, \n" 243 | "\"interlace\": false,\"frame rate\": 24\n" 244 | "}\n" 245 | "}"; 246 | char text2[] = "[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]"; 247 | char text3[] = 248 | "[\n" 249 | " [0, -1, 0],\n" 250 | " [1, 0, 0],\n" 251 | " [0, 0, 1]\n" 252 | "\t]\n"; 253 | char text4[] = 254 | "{\n" 255 | "\t\t\"Image\": {\n" 256 | "\t\t\t\"Width\": 800,\n" 257 | "\t\t\t\"Height\": 600,\n" 258 | "\t\t\t\"Title\": \"View from 15th Floor\",\n" 259 | "\t\t\t\"Thumbnail\": {\n" 260 | "\t\t\t\t\"Url\": \"http:/*www.example.com/image/481989943\",\n" 261 | "\t\t\t\t\"Height\": 125,\n" 262 | "\t\t\t\t\"Width\": \"100\"\n" 263 | "\t\t\t},\n" 264 | "\t\t\t\"IDs\": [116, 943, 234, 38793]\n" 265 | "\t\t}\n" 266 | "\t}"; 267 | char text5[] = 268 | "[\n" 269 | "\t {\n" 270 | "\t \"precision\": \"zip\",\n" 271 | "\t \"Latitude\": 37.7668,\n" 272 | "\t \"Longitude\": -122.3959,\n" 273 | "\t \"Address\": \"\",\n" 274 | "\t \"City\": \"SAN FRANCISCO\",\n" 275 | "\t \"State\": \"CA\",\n" 276 | "\t \"Zip\": \"94107\",\n" 277 | "\t \"Country\": \"US\"\n" 278 | "\t },\n" 279 | "\t {\n" 280 | "\t \"precision\": \"zip\",\n" 281 | "\t \"Latitude\": 37.371991,\n" 282 | "\t \"Longitude\": -122.026020,\n" 283 | "\t \"Address\": \"\",\n" 284 | "\t \"City\": \"SUNNYVALE\",\n" 285 | "\t \"State\": \"CA\",\n" 286 | "\t \"Zip\": \"94085\",\n" 287 | "\t \"Country\": \"US\"\n" 288 | "\t }\n" 289 | "\t ]"; 290 | 291 | char text6[] = 292 | "" 293 | "\n" 294 | "\n" 295 | " \n" 296 | " \n" 300 | "Application Error\n" 301 | "\n" 302 | "\n" 303 | " \n" 306 | "\n" 307 | "\n"; 308 | 309 | /* Process each json textblock by parsing, then rebuilding: */ 310 | doit(text1); 311 | doit(text2); 312 | doit(text3); 313 | doit(text4); 314 | doit(text5); 315 | doit(text6); 316 | 317 | /* Parse standard testfiles: */ 318 | /* dofile("../../tests/test1"); */ 319 | /* dofile("../../tests/test2"); */ 320 | /* dofile("../../tests/test3"); */ 321 | /* dofile("../../tests/test4"); */ 322 | /* dofile("../../tests/test5"); */ 323 | /* dofile("../../tests/test6"); */ 324 | 325 | /* Now some samplecode for building objects concisely: */ 326 | create_objects(); 327 | 328 | return 0; 329 | } 330 | -------------------------------------------------------------------------------- /test_utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "cJSON_Utils.h" 5 | 6 | int main(void) 7 | { 8 | /* Some variables */ 9 | char *temp = NULL; 10 | char *patchtext = NULL; 11 | char *patchedtext = NULL; 12 | 13 | int i = 0; 14 | /* JSON Pointer tests: */ 15 | cJSON *root = NULL; 16 | const char *json= 17 | "{" 18 | "\"foo\": [\"bar\", \"baz\"]," 19 | "\"\": 0," 20 | "\"a/b\": 1," 21 | "\"c%d\": 2," 22 | "\"e^f\": 3," 23 | "\"g|h\": 4," 24 | "\"i\\\\j\": 5," 25 | "\"k\\\"l\": 6," 26 | "\" \": 7," 27 | "\"m~n\": 8" 28 | "}"; 29 | 30 | const char *tests[12] = {"","/foo","/foo/0","/","/a~1b","/c%d","/e^f","/g|h","/i\\j","/k\"l","/ ","/m~0n"}; 31 | 32 | /* JSON Apply Patch tests: */ 33 | const char *patches[15][3] = 34 | { 35 | {"{ \"foo\": \"bar\"}", "[{ \"op\": \"add\", \"path\": \"/baz\", \"value\": \"qux\" }]","{\"baz\": \"qux\",\"foo\": \"bar\"}"}, 36 | {"{ \"foo\": [ \"bar\", \"baz\" ] }", "[{ \"op\": \"add\", \"path\": \"/foo/1\", \"value\": \"qux\" }]","{\"foo\": [ \"bar\", \"qux\", \"baz\" ] }"}, 37 | {"{\"baz\": \"qux\",\"foo\": \"bar\"}"," [{ \"op\": \"remove\", \"path\": \"/baz\" }]","{\"foo\": \"bar\" }"}, 38 | {"{ \"foo\": [ \"bar\", \"qux\", \"baz\" ] }","[{ \"op\": \"remove\", \"path\": \"/foo/1\" }]","{\"foo\": [ \"bar\", \"baz\" ] }"}, 39 | {"{ \"baz\": \"qux\",\"foo\": \"bar\"}","[{ \"op\": \"replace\", \"path\": \"/baz\", \"value\": \"boo\" }]","{\"baz\": \"boo\",\"foo\": \"bar\"}"}, 40 | {"{\"foo\": {\"bar\": \"baz\",\"waldo\": \"fred\"},\"qux\": {\"corge\": \"grault\"}}","[{ \"op\": \"move\", \"from\": \"/foo/waldo\", \"path\": \"/qux/thud\" }]","{\"foo\": {\"bar\": \"baz\"},\"qux\": {\"corge\": \"grault\",\"thud\": \"fred\"}}"}, 41 | {"{ \"foo\": [ \"all\", \"grass\", \"cows\", \"eat\" ] }","[ { \"op\": \"move\", \"from\": \"/foo/1\", \"path\": \"/foo/3\" }]","{ \"foo\": [ \"all\", \"cows\", \"eat\", \"grass\" ] }"}, 42 | {"{\"baz\": \"qux\",\"foo\": [ \"a\", 2, \"c\" ]}","[{ \"op\": \"test\", \"path\": \"/baz\", \"value\": \"qux\" },{ \"op\": \"test\", \"path\": \"/foo/1\", \"value\": 2 }]",""}, 43 | {"{ \"baz\": \"qux\" }","[ { \"op\": \"test\", \"path\": \"/baz\", \"value\": \"bar\" }]",""}, 44 | {"{ \"foo\": \"bar\" }","[{ \"op\": \"add\", \"path\": \"/child\", \"value\": { \"grandchild\": { } } }]","{\"foo\": \"bar\",\"child\": {\"grandchild\": {}}}"}, 45 | {"{ \"foo\": \"bar\" }","[{ \"op\": \"add\", \"path\": \"/baz\", \"value\": \"qux\", \"xyz\": 123 }]","{\"foo\": \"bar\",\"baz\": \"qux\"}"}, 46 | {"{ \"foo\": \"bar\" }","[{ \"op\": \"add\", \"path\": \"/baz/bat\", \"value\": \"qux\" }]",""}, 47 | {"{\"/\": 9,\"~1\": 10}","[{\"op\": \"test\", \"path\": \"/~01\", \"value\": 10}]",""}, 48 | {"{\"/\": 9,\"~1\": 10}","[{\"op\": \"test\", \"path\": \"/~01\", \"value\": \"10\"}]",""}, 49 | {"{ \"foo\": [\"bar\"] }","[ { \"op\": \"add\", \"path\": \"/foo/-\", \"value\": [\"abc\", \"def\"] }]","{\"foo\": [\"bar\", [\"abc\", \"def\"]] }"} 50 | }; 51 | 52 | /* JSON Apply Merge tests: */ 53 | const char *merges[15][3] = 54 | { 55 | {"{\"a\":\"b\"}", "{\"a\":\"c\"}", "{\"a\":\"c\"}"}, 56 | {"{\"a\":\"b\"}", "{\"b\":\"c\"}", "{\"a\":\"b\",\"b\":\"c\"}"}, 57 | {"{\"a\":\"b\"}", "{\"a\":null}", "{}"}, 58 | {"{\"a\":\"b\",\"b\":\"c\"}", "{\"a\":null}", "{\"b\":\"c\"}"}, 59 | {"{\"a\":[\"b\"]}", "{\"a\":\"c\"}", "{\"a\":\"c\"}"}, 60 | {"{\"a\":\"c\"}", "{\"a\":[\"b\"]}", "{\"a\":[\"b\"]}"}, 61 | {"{\"a\":{\"b\":\"c\"}}", "{\"a\":{\"b\":\"d\",\"c\":null}}", "{\"a\":{\"b\":\"d\"}}"}, 62 | {"{\"a\":[{\"b\":\"c\"}]}", "{\"a\":[1]}", "{\"a\":[1]}"}, 63 | {"[\"a\",\"b\"]", "[\"c\",\"d\"]", "[\"c\",\"d\"]"}, 64 | {"{\"a\":\"b\"}", "[\"c\"]", "[\"c\"]"}, 65 | {"{\"a\":\"foo\"}", "null", "null"}, 66 | {"{\"a\":\"foo\"}", "\"bar\"", "\"bar\""}, 67 | {"{\"e\":null}", "{\"a\":1}", "{\"e\":null,\"a\":1}"}, 68 | {"[1,2]", "{\"a\":\"b\",\"c\":null}", "{\"a\":\"b\"}"}, 69 | {"{}","{\"a\":{\"bb\":{\"ccc\":null}}}", "{\"a\":{\"bb\":{}}}"} 70 | }; 71 | 72 | 73 | /* Misc tests */ 74 | int numbers[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 75 | const char *random = "QWERTYUIOPASDFGHJKLZXCVBNM"; 76 | char buf[2] = {0,0}; 77 | char *before = NULL; 78 | char *after = NULL; 79 | cJSON *object = NULL; 80 | cJSON *nums = NULL; 81 | cJSON *num6 = NULL; 82 | cJSON *sortme = NULL; 83 | 84 | 85 | printf("JSON Pointer Tests\n"); 86 | root = cJSON_Parse(json); 87 | for (i = 0; i < 12; i++) 88 | { 89 | char *output = cJSON_Print(cJSONUtils_GetPointer(root, tests[i])); 90 | printf("Test %d:\n%s\n\n", i + 1, output); 91 | free(output); 92 | } 93 | cJSON_Delete(root); 94 | 95 | 96 | printf("JSON Apply Patch Tests\n"); 97 | for (i = 0; i < 15; i++) 98 | { 99 | cJSON *object = cJSON_Parse(patches[i][0]); 100 | cJSON *patch = cJSON_Parse(patches[i][1]); 101 | int err = cJSONUtils_ApplyPatches(object, patch); 102 | char *output = cJSON_Print(object); 103 | printf("Test %d (err %d):\n%s\n\n", i + 1, err, output); 104 | 105 | free(output); 106 | cJSON_Delete(object); 107 | cJSON_Delete(patch); 108 | } 109 | 110 | /* JSON Generate Patch tests: */ 111 | printf("JSON Generate Patch Tests\n"); 112 | for (i = 0; i < 15; i++) 113 | { 114 | cJSON *from; 115 | cJSON *to; 116 | cJSON *patch; 117 | char *out; 118 | if (!strlen(patches[i][2])) 119 | { 120 | continue; 121 | } 122 | from = cJSON_Parse(patches[i][0]); 123 | to = cJSON_Parse(patches[i][2]); 124 | patch = cJSONUtils_GeneratePatches(from, to); 125 | out = cJSON_Print(patch); 126 | printf("Test %d: (patch: %s):\n%s\n\n", i + 1, patches[i][1], out); 127 | 128 | free(out); 129 | cJSON_Delete(from); 130 | cJSON_Delete(to); 131 | cJSON_Delete(patch); 132 | } 133 | 134 | /* Misc tests: */ 135 | printf("JSON Pointer construct\n"); 136 | object = cJSON_CreateObject(); 137 | nums = cJSON_CreateIntArray(numbers, 10); 138 | num6 = cJSON_GetArrayItem(nums, 6); 139 | cJSON_AddItemToObject(object, "numbers", nums); 140 | temp = cJSONUtils_FindPointerFromObjectTo(object, num6); 141 | printf("Pointer: [%s]\n", temp); 142 | free(temp); 143 | temp = cJSONUtils_FindPointerFromObjectTo(object, nums); 144 | printf("Pointer: [%s]\n", temp); 145 | free(temp); 146 | temp = cJSONUtils_FindPointerFromObjectTo(object, object); 147 | printf("Pointer: [%s]\n", temp); 148 | free(temp); 149 | cJSON_Delete(object); 150 | 151 | /* JSON Sort test: */ 152 | sortme = cJSON_CreateObject(); 153 | for (i = 0; i < 26; i++) 154 | { 155 | buf[0] = random[i]; 156 | cJSON_AddItemToObject(sortme, buf, cJSON_CreateNumber(1)); 157 | } 158 | before = cJSON_PrintUnformatted(sortme); 159 | cJSONUtils_SortObject(sortme); 160 | after = cJSON_PrintUnformatted(sortme); 161 | printf("Before: [%s]\nAfter: [%s]\n\n", before, after); 162 | 163 | free(before); 164 | free(after); 165 | cJSON_Delete(sortme); 166 | 167 | /* Merge tests: */ 168 | printf("JSON Merge Patch tests\n"); 169 | for (i = 0; i < 15; i++) 170 | { 171 | cJSON *object = cJSON_Parse(merges[i][0]); 172 | cJSON *patch = cJSON_Parse(merges[i][1]); 173 | char *before = cJSON_PrintUnformatted(object); 174 | patchtext = cJSON_PrintUnformatted(patch); 175 | printf("Before: [%s] -> [%s] = ", before, patchtext); 176 | object = cJSONUtils_MergePatch(object, patch); 177 | after = cJSON_PrintUnformatted(object); 178 | printf("[%s] vs [%s] (%s)\n", after, merges[i][2], strcmp(after, merges[i][2]) ? "FAIL" : "OK"); 179 | 180 | free(before); 181 | free(patchtext); 182 | free(after); 183 | cJSON_Delete(object); 184 | cJSON_Delete(patch); 185 | } 186 | 187 | /* Generate Merge tests: */ 188 | for (i = 0; i < 15; i++) 189 | { 190 | cJSON *from = cJSON_Parse(merges[i][0]); 191 | cJSON *to = cJSON_Parse(merges[i][2]); 192 | cJSON *patch = cJSONUtils_GenerateMergePatch(from,to); 193 | from = cJSONUtils_MergePatch(from,patch); 194 | patchtext = cJSON_PrintUnformatted(patch); 195 | patchedtext = cJSON_PrintUnformatted(from); 196 | printf("Patch [%s] vs [%s] = [%s] vs [%s] (%s)\n", patchtext, merges[i][1], patchedtext, merges[i][2], strcmp(patchedtext, merges[i][2]) ? "FAIL" : "OK"); 197 | 198 | cJSON_Delete(from); 199 | cJSON_Delete(to); 200 | cJSON_Delete(patch); 201 | free(patchtext); 202 | free(patchedtext); 203 | } 204 | 205 | return 0; 206 | } 207 | -------------------------------------------------------------------------------- /tests/test1: -------------------------------------------------------------------------------- 1 | { 2 | "glossary": { 3 | "title": "example glossary", 4 | "GlossDiv": { 5 | "title": "S", 6 | "GlossList": { 7 | "GlossEntry": { 8 | "ID": "SGML", 9 | "SortAs": "SGML", 10 | "GlossTerm": "Standard Generalized Markup Language", 11 | "Acronym": "SGML", 12 | "Abbrev": "ISO 8879:1986", 13 | "GlossDef": { 14 | "para": "A meta-markup language, used to create markup languages such as DocBook.", 15 | "GlossSeeAlso": ["GML", "XML"] 16 | }, 17 | "GlossSee": "markup" 18 | } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/test2: -------------------------------------------------------------------------------- 1 | {"menu": { 2 | "id": "file", 3 | "value": "File", 4 | "popup": { 5 | "menuitem": [ 6 | {"value": "New", "onclick": "CreateNewDoc()"}, 7 | {"value": "Open", "onclick": "OpenDoc()"}, 8 | {"value": "Close", "onclick": "CloseDoc()"} 9 | ] 10 | } 11 | }} 12 | -------------------------------------------------------------------------------- /tests/test3: -------------------------------------------------------------------------------- 1 | {"widget": { 2 | "debug": "on", 3 | "window": { 4 | "title": "Sample Konfabulator Widget", 5 | "name": "main_window", 6 | "width": 500, 7 | "height": 500 8 | }, 9 | "image": { 10 | "src": "Images/Sun.png", 11 | "name": "sun1", 12 | "hOffset": 250, 13 | "vOffset": 250, 14 | "alignment": "center" 15 | }, 16 | "text": { 17 | "data": "Click Here", 18 | "size": 36, 19 | "style": "bold", 20 | "name": "text1", 21 | "hOffset": 250, 22 | "vOffset": 100, 23 | "alignment": "center", 24 | "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" 25 | } 26 | }} -------------------------------------------------------------------------------- /tests/test4: -------------------------------------------------------------------------------- 1 | {"web-app": { 2 | "servlet": [ 3 | { 4 | "servlet-name": "cofaxCDS", 5 | "servlet-class": "org.cofax.cds.CDSServlet", 6 | "init-param": { 7 | "configGlossary:installationAt": "Philadelphia, PA", 8 | "configGlossary:adminEmail": "ksm@pobox.com", 9 | "configGlossary:poweredBy": "Cofax", 10 | "configGlossary:poweredByIcon": "/images/cofax.gif", 11 | "configGlossary:staticPath": "/content/static", 12 | "templateProcessorClass": "org.cofax.WysiwygTemplate", 13 | "templateLoaderClass": "org.cofax.FilesTemplateLoader", 14 | "templatePath": "templates", 15 | "templateOverridePath": "", 16 | "defaultListTemplate": "listTemplate.htm", 17 | "defaultFileTemplate": "articleTemplate.htm", 18 | "useJSP": false, 19 | "jspListTemplate": "listTemplate.jsp", 20 | "jspFileTemplate": "articleTemplate.jsp", 21 | "cachePackageTagsTrack": 200, 22 | "cachePackageTagsStore": 200, 23 | "cachePackageTagsRefresh": 60, 24 | "cacheTemplatesTrack": 100, 25 | "cacheTemplatesStore": 50, 26 | "cacheTemplatesRefresh": 15, 27 | "cachePagesTrack": 200, 28 | "cachePagesStore": 100, 29 | "cachePagesRefresh": 10, 30 | "cachePagesDirtyRead": 10, 31 | "searchEngineListTemplate": "forSearchEnginesList.htm", 32 | "searchEngineFileTemplate": "forSearchEngines.htm", 33 | "searchEngineRobotsDb": "WEB-INF/robots.db", 34 | "useDataStore": true, 35 | "dataStoreClass": "org.cofax.SqlDataStore", 36 | "redirectionClass": "org.cofax.SqlRedirection", 37 | "dataStoreName": "cofax", 38 | "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", 39 | "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", 40 | "dataStoreUser": "sa", 41 | "dataStorePassword": "dataStoreTestQuery", 42 | "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", 43 | "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", 44 | "dataStoreInitConns": 10, 45 | "dataStoreMaxConns": 100, 46 | "dataStoreConnUsageLimit": 100, 47 | "dataStoreLogLevel": "debug", 48 | "maxUrlLength": 500}}, 49 | { 50 | "servlet-name": "cofaxEmail", 51 | "servlet-class": "org.cofax.cds.EmailServlet", 52 | "init-param": { 53 | "mailHost": "mail1", 54 | "mailHostOverride": "mail2"}}, 55 | { 56 | "servlet-name": "cofaxAdmin", 57 | "servlet-class": "org.cofax.cds.AdminServlet"}, 58 | 59 | { 60 | "servlet-name": "fileServlet", 61 | "servlet-class": "org.cofax.cds.FileServlet"}, 62 | { 63 | "servlet-name": "cofaxTools", 64 | "servlet-class": "org.cofax.cms.CofaxToolsServlet", 65 | "init-param": { 66 | "templatePath": "toolstemplates/", 67 | "log": 1, 68 | "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", 69 | "logMaxSize": "", 70 | "dataLog": 1, 71 | "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", 72 | "dataLogMaxSize": "", 73 | "removePageCache": "/content/admin/remove?cache=pages&id=", 74 | "removeTemplateCache": "/content/admin/remove?cache=templates&id=", 75 | "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", 76 | "lookInContext": 1, 77 | "adminGroupID": 4, 78 | "betaServer": true}}], 79 | "servlet-mapping": { 80 | "cofaxCDS": "/", 81 | "cofaxEmail": "/cofaxutil/aemail/*", 82 | "cofaxAdmin": "/admin/*", 83 | "fileServlet": "/static/*", 84 | "cofaxTools": "/tools/*"}, 85 | 86 | "taglib": { 87 | "taglib-uri": "cofax.tld", 88 | "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} -------------------------------------------------------------------------------- /tests/test5: -------------------------------------------------------------------------------- 1 | {"menu": { 2 | "header": "SVG Viewer", 3 | "items": [ 4 | {"id": "Open"}, 5 | {"id": "OpenNew", "label": "Open New"}, 6 | null, 7 | {"id": "ZoomIn", "label": "Zoom In"}, 8 | {"id": "ZoomOut", "label": "Zoom Out"}, 9 | {"id": "OriginalView", "label": "Original View"}, 10 | null, 11 | {"id": "Quality"}, 12 | {"id": "Pause"}, 13 | {"id": "Mute"}, 14 | null, 15 | {"id": "Find", "label": "Find..."}, 16 | {"id": "FindAgain", "label": "Find Again"}, 17 | {"id": "Copy"}, 18 | {"id": "CopyAgain", "label": "Copy Again"}, 19 | {"id": "CopySVG", "label": "Copy SVG"}, 20 | {"id": "ViewSVG", "label": "View SVG"}, 21 | {"id": "ViewSource", "label": "View Source"}, 22 | {"id": "SaveAs", "label": "Save As"}, 23 | null, 24 | {"id": "Help"}, 25 | {"id": "About", "label": "About Adobe CVG Viewer..."} 26 | ] 27 | }} 28 | -------------------------------------------------------------------------------- /tests/test6: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | Application Error 10 | 11 | 12 | 15 | 16 | --------------------------------------------------------------------------------