1 /*
2  * Contributed by Jacob Teplitsky <jacob.teplitsky@ericsson.com>
3  *
4  * libcbor is free software; you can redistribute it and/or modify
5  * it under the terms of the MIT license. See LICENSE for details.
6  */
7 
8 /**
9  * This code demonstrates how cJSON (https://github.com/DaveGamble/cJSON)
10  * callbacks can be used in conjuction with the streaming parser to translate
11  * JSON to CBOR. Please note that cbor_builder_* APIs are internal and thus
12  * subject to change.
13  *
14  * The example will only be compiled when cJSON is available
15  */
16 
17 #include <cjson/cJSON.h>
18 #include <float.h>
19 #include <math.h>
20 #include <string.h>
21 #include "cbor.h"
22 #include "cbor/internal/builder_callbacks.h"
23 #include "cbor/internal/loaders.h"
24 
25 typedef void (*cbor_load_callback_t)(cJSON *, const struct cbor_callbacks *,
26                                      void *);
27 
cjson_cbor_load(void * source,cbor_load_callback_t cbor_load_callback)28 cbor_item_t *cjson_cbor_load(void *source,
29                              cbor_load_callback_t cbor_load_callback) {
30   static struct cbor_callbacks callbacks = {
31       .uint64 = &cbor_builder_uint64_callback,
32       .negint64 = &cbor_builder_negint64_callback,
33       .string = &cbor_builder_string_callback,
34       .array_start = &cbor_builder_array_start_callback,
35       .map_start = &cbor_builder_map_start_callback,
36       .null = &cbor_builder_null_callback,
37       .boolean = &cbor_builder_boolean_callback,
38       .float4 = &cbor_builder_float4_callback,
39   };
40 
41   /* Context stack */
42   struct _cbor_stack stack = _cbor_stack_init();
43 
44   /* Target for callbacks */
45   struct _cbor_decoder_context context = (struct _cbor_decoder_context){
46       .stack = &stack,
47   };
48 
49   cbor_load_callback(source, &callbacks, &context);
50 
51   return context.root;
52 }
53 
cjson_cbor_stream_decode(cJSON * source,const struct cbor_callbacks * callbacks,void * context)54 void cjson_cbor_stream_decode(cJSON *source,
55                               const struct cbor_callbacks *callbacks,
56                               void *context) {
57   switch (source->type) {
58     case cJSON_False: {
59       callbacks->boolean(context, false);
60       return;
61     }
62     case cJSON_True: {
63       callbacks->boolean(context, true);
64       return;
65     }
66     case cJSON_NULL: {
67       callbacks->null(context);
68       return;
69     }
70     case cJSON_Number: {
71       // This is stupid -- ints and doubles cannot are not distinguished
72       if (fabs(source->valuedouble - source->valueint) > DBL_EPSILON) {
73         callbacks->float4(context, source->valuedouble);
74       } else {
75         // XXX: This is not portable
76         if (source->valueint >= 0) {
77           callbacks->uint64(context, source->valueint);
78         } else {
79           callbacks->negint64(context, source->valueint + 1);
80         }
81       }
82       return;
83     }
84     case cJSON_String: {
85       // XXX: Assume cJSON handled unicode correctly
86       callbacks->string(context, (unsigned char *)source->valuestring,
87                         strlen(source->valuestring));
88       return;
89     }
90     case cJSON_Array: {
91       callbacks->array_start(context, cJSON_GetArraySize(source));
92       cJSON *item = source->child;
93       while (item != NULL) {
94         cjson_cbor_stream_decode(item, callbacks, context);
95         item = item->next;
96       }
97       return;
98     }
99     case cJSON_Object: {
100       callbacks->map_start(context, cJSON_GetArraySize(source));
101       cJSON *item = source->child;
102       while (item != NULL) {
103         callbacks->string(context, (unsigned char *)item->string,
104                           strlen(item->string));
105         cjson_cbor_stream_decode(item, callbacks, context);
106         item = item->next;
107       }
108       return;
109     }
110   }
111 }
112 
usage(void)113 void usage(void) {
114   printf("Usage: cjson [input JSON file]\n");
115   exit(1);
116 }
117 
main(int argc,char * argv[])118 int main(int argc, char *argv[]) {
119   if (argc != 2) usage();
120   FILE *f = fopen(argv[1], "rb");
121   if (f == NULL) usage();
122   /* Read input file into a buffer (cJSON doesn't work with streams) */
123   fseek(f, 0, SEEK_END);
124   size_t length = (size_t)ftell(f);
125   fseek(f, 0, SEEK_SET);
126   char *json_buffer = malloc(length + 1);
127   fread(json_buffer, length, 1, f);
128   json_buffer[length] = '\0';
129 
130   /* Convert between JSON and CBOR */
131   cJSON *json = cJSON_Parse(json_buffer);
132   cbor_item_t *cbor = cjson_cbor_load(json, cjson_cbor_stream_decode);
133 
134   /* Print out CBOR bytes */
135   unsigned char *buffer;
136   size_t buffer_size;
137   cbor_serialize_alloc(cbor, &buffer, &buffer_size);
138 
139   fwrite(buffer, 1, buffer_size, stdout);
140 
141   free(buffer);
142   fflush(stdout);
143   cJSON_Delete(json);
144   cbor_decref(&cbor);
145 }
146