aboutsummaryrefslogtreecommitdiff
path: root/examples/cjson2cbor.c
blob: 2e3071fb47c556dac8d1a3ca862aebf3e785f3cf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
 * Contributed by Jacob Teplitsky <jacob.teplitsky@ericsson.com>
 *
 * libcbor is free software; you can redistribute it and/or modify
 * it under the terms of the MIT license. See LICENSE for details.
 */

/**
 * This code demonstrates how cJSON (https://github.com/DaveGamble/cJSON)
 * callbacks can be used in conjuction with the streaming parser to translate
 * JSON to CBOR. Please note that cbor_builder_* APIs are internal and thus
 * subject to change.
 *
 * The example will only be compiled when cJSON is available
 */

#include <cjson/cJSON.h>
#include <float.h>
#include <math.h>
#include <string.h>
#include "cbor.h"
#include "cbor/internal/builder_callbacks.h"
#include "cbor/internal/loaders.h"

typedef void (*cbor_load_callback_t)(cJSON *, const struct cbor_callbacks *,
                                     void *);

cbor_item_t *cjson_cbor_load(void *source,
                             cbor_load_callback_t cbor_load_callback) {
  static struct cbor_callbacks callbacks = {
      .uint64 = &cbor_builder_uint64_callback,
      .negint64 = &cbor_builder_negint64_callback,
      .string = &cbor_builder_string_callback,
      .array_start = &cbor_builder_array_start_callback,
      .map_start = &cbor_builder_map_start_callback,
      .null = &cbor_builder_null_callback,
      .boolean = &cbor_builder_boolean_callback,
      .float4 = &cbor_builder_float4_callback,
  };

  /* Context stack */
  struct _cbor_stack stack = _cbor_stack_init();

  /* Target for callbacks */
  struct _cbor_decoder_context context = (struct _cbor_decoder_context){
      .stack = &stack,
  };

  cbor_load_callback(source, &callbacks, &context);

  return context.root;
}

void cjson_cbor_stream_decode(cJSON *source,
                              const struct cbor_callbacks *callbacks,
                              void *context) {
  switch (source->type) {
    case cJSON_False: {
      callbacks->boolean(context, false);
      return;
    }
    case cJSON_True: {
      callbacks->boolean(context, true);
      return;
    }
    case cJSON_NULL: {
      callbacks->null(context);
      return;
    }
    case cJSON_Number: {
      // This is stupid -- ints and doubles cannot are not distinguished
      if (fabs(source->valuedouble - source->valueint) > DBL_EPSILON) {
        callbacks->float4(context, source->valuedouble);
      } else {
        // XXX: This is not portable
        if (source->valueint >= 0) {
          callbacks->uint64(context, source->valueint);
        } else {
          callbacks->negint64(context, source->valueint + 1);
        }
      }
      return;
    }
    case cJSON_String: {
      // XXX: Assume cJSON handled unicode correctly
      callbacks->string(context, (unsigned char *)source->valuestring,
                        strlen(source->valuestring));
      return;
    }
    case cJSON_Array: {
      callbacks->array_start(context, cJSON_GetArraySize(source));
      cJSON *item = source->child;
      while (item != NULL) {
        cjson_cbor_stream_decode(item, callbacks, context);
        item = item->next;
      }
      return;
    }
    case cJSON_Object: {
      callbacks->map_start(context, cJSON_GetArraySize(source));
      cJSON *item = source->child;
      while (item != NULL) {
        callbacks->string(context, (unsigned char *)item->string,
                          strlen(item->string));
        cjson_cbor_stream_decode(item, callbacks, context);
        item = item->next;
      }
      return;
    }
  }
}

void usage() {
  printf("Usage: cjson [input JSON file]\n");
  exit(1);
}

int main(int argc, char *argv[]) {
  if (argc != 2) usage();
  FILE *f = fopen(argv[1], "rb");
  if (f == NULL) usage();
  /* Read input file into a buffer (cJSON doesn't work with streams) */
  fseek(f, 0, SEEK_END);
  size_t length = (size_t)ftell(f);
  fseek(f, 0, SEEK_SET);
  char *json_buffer = malloc(length + 1);
  fread(json_buffer, length, 1, f);
  json_buffer[length] = '\0';

  /* Convert between JSON and CBOR */
  cJSON *json = cJSON_Parse(json_buffer);
  cbor_item_t *cbor = cjson_cbor_load(json, cjson_cbor_stream_decode);

  /* Print out CBOR bytes */
  unsigned char *buffer;
  size_t buffer_size,
      cbor_length = cbor_serialize_alloc(cbor, &buffer, &buffer_size);

  fwrite(buffer, 1, cbor_length, stdout);

  free(buffer);
  fflush(stdout);
  cJSON_Delete(json);
  cbor_decref(&cbor);
}