Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_json_from_value.c
Go to the documentation of this file.
1 // See the 'COPYING' file in the project root for licensing information.
2 /*
3  * AFW JSON convert from value
4  *
5  * Copyright (c) 2010-2023 Clemson University
6  *
7  */
8 
14 #include "afw_internal.h"
15 #include <apr_buckets.h>
16 
17 typedef struct from_value_wa_s {
18  afw_xctx_t *xctx;
19  const afw_pool_t *p;
20  const afw_object_options_t *options;
21  void * context;
22  afw_write_cb_t callback;
23  afw_size_t indent;
24  afw_size_t object_depth;
25  afw_boolean_t skip_next_ws;
26  afw_boolean_t do_ws;
27  afw_boolean_t do_typed_values;
29 
30 static void
31 impl_put_ws(impl_from_value_wa_t *wa);
32 
33 static void
34 impl_put_json_string(
36  const afw_utf8_t *string);
37 
38 static void
39 impl_put_quoted_to_json(
41  const afw_utf8_t *s);
42 
43 static void
44 impl_convert_boolean_to_json(
46  afw_boolean_t b);
47 
48 static void
49 impl_convert_list_to_json(
51  const afw_list_t *list);
52 
53 static void
54 impl_convert_object_to_json(
56  const afw_object_t *obj);
57 
58 static void
59 impl_convert_value_to_json(
61  const afw_value_t *value);
62 
63 AFW_DEFINE_STATIC_INLINE(void)
64 impl_putc(impl_from_value_wa_t *wa, afw_byte_t c)
65 {
66  char ch;
67 
68  ch = c;
69  wa->callback(wa->context, &ch, 1, wa->p, wa->xctx);
70 }
71 
72 
73 AFW_DEFINE_STATIC_INLINE(void)
74 impl_puts(impl_from_value_wa_t *wa,
75  const afw_utf8_z_t *s)
76 {
77  wa->callback(wa->context, s, strlen(s), wa->p, wa->xctx);
78 }
79 
80 
81 AFW_DEFINE_STATIC_INLINE(void)
82 impl_write(impl_from_value_wa_t *wa,
83  const void * buffer, afw_size_t size)
84 {
85  wa->callback(wa->context, buffer, size, wa->p, wa->xctx);
86 }
87 
88 
89 static void
90 impl_printf(impl_from_value_wa_t *wa,
91  const afw_utf8_z_t *format_z, ...)
92 {
93  va_list arg;
94  const afw_utf8_t *s;
95 
96  va_start(arg, format_z);
97  s = afw_utf8_printf_v(format_z, arg, wa->p, wa->xctx);
98  va_end(arg);
99 
100  wa->callback(wa->context, s->s, s->len, wa->p, wa->xctx);
101 }
102 
103 
104 static void
105 impl_put_ws(impl_from_value_wa_t *wa)
106 {
107  afw_size_t indent;
108 
109  if (wa->do_ws) {
110  if (!wa->skip_next_ws) {
111  impl_putc(wa, '\n');
112  for (indent = 1; indent <= wa->indent; indent++) {
113  /* default whitespace indentation to 4 spaces */
114  impl_puts(wa, " ");
115  }
116  }
117  wa->skip_next_ws = 0;
118  }
119 }
120 
121 
122 static void
123 impl_put_json_string(
125  const afw_utf8_t *string)
126 {
128  afw_size_t len;
129  const afw_utf8_octet_t *s;
130 
131  impl_putc(wa, '"');
132 
133  s = string->s;
134  len = string->len;
135 
136  while (len > 0) {
137  c = *s;
138 
139  /* Add an extra backslash if character is backslash or quote. */
140  if (c == '\\' || c == '"') {
141  impl_putc(wa, '\\');
142  }
143 
144  /* If not a control character, output the character. */
145  if (c >= 32) {
146  impl_putc(wa, c);
147  }
148 
149  /* If \n */
150  else if (c == 10) {
151  impl_puts(wa, "\\n");
152  }
153 
154  /* If \r */
155  else if (c == 13) {
156  impl_puts(wa, "\\r");
157  }
158 
159  /* If \t */
160  else if (c == 9) {
161  impl_puts(wa, "\\t");
162  }
163 
164  /* If \f */
165  else if (c == 12) {
166  impl_puts(wa, "\\f");
167  }
168 
169  /* If \b */
170  else if (c == 7) {
171  impl_puts(wa, "\\b");
172  }
173 
174  /*
175  * If other control character, output as \u followed by character as four
176  * byte hex characters.
177  */
178  else {
179  impl_printf(wa, "\\u%04x", c);
180  }
181 
182  len--;
183  s++;
184  }
185 
186  impl_putc(wa, '"');
187 }
188 
189 
190 static void
191 impl_put_quoted_to_json(
193  const afw_utf8_t *s)
194 {
195  impl_putc(wa, '"');
197  if (s) {
198  impl_write(wa, s->s, s->len);
199  }
200  impl_putc(wa, '"');
201 }
202 
203 
204 static void
205 impl_convert_boolean_to_json(
207  afw_boolean_t b)
208 {
209  if (b) {
210  impl_puts(wa, AFW_JSON_Q_PRIMITIVE_BOOLEAN_TRUE);
211  }
212  else {
213  impl_puts(wa, AFW_JSON_Q_PRIMITIVE_BOOLEAN_FALSE);
214  }
215 }
216 
217 
218 static void
219 impl_convert_list_to_json(
221  const afw_list_t *list)
222 {
223  const afw_iterator_t *list_iterator;
224  const afw_value_t *next;
225 
226  /* Put [ and increment indent. */
227  impl_putc(wa, '[');
228  (wa->indent)++;
229 
230  list_iterator = NULL;
231  next = afw_list_get_next_value(list, &list_iterator,
232  wa->p, wa->xctx);
233 
234  while (next) {
235  impl_convert_value_to_json(wa, next);
236  next = afw_list_get_next_value(list, &list_iterator,
237  wa->p, wa->xctx);
238  if (next) {
239  impl_putc(wa, ',');
240  }
241  }
242 
243  /* Decrement indent and put ']'. */
244  (wa->indent)--;
245  impl_put_ws(wa);
246  impl_putc(wa, ']');
247 }
248 
249 
250 
251 static void
252 impl_convert_object_to_json(
254  const afw_object_t *obj)
255 {
256  const afw_iterator_t *property_iterator;
257  const afw_utf8_t *property_name;
258  const afw_value_t *next;
259  afw_boolean_t starting_comma_needed;
260  const afw_object_t *meta;
261 
262  /* Put { and increment indent. */
263  impl_putc(wa, '{');
264  (wa->indent)++;
265  (wa->object_depth)++;
266 
267  /* If object has meta, convert it first. */
268  starting_comma_needed = false;
270  wa->options, wa->p, wa->xctx);
271  if (meta) {
272  impl_put_ws(wa);
273  wa->skip_next_ws = 1;
274  if (wa->do_ws) {
275  impl_puts(wa, "\"_meta_\": ");
276  }
277  else {
278  impl_puts(wa, "\"_meta_\":");
279  }
280  impl_put_ws(wa);
281  impl_convert_object_to_json(wa, meta);
282  starting_comma_needed = true;
283  }
284 
285  /* Add each object property. */
286  property_iterator = NULL;
287  next = afw_object_get_next_property(obj, &property_iterator,
288  &property_name, wa->xctx);
289  if (next) {
290  while (1) {
291  if (starting_comma_needed) {
292  impl_putc(wa, ',');
293  starting_comma_needed = false;
294  }
295  impl_put_ws(wa);
296  wa->skip_next_ws = 1;
297  impl_put_quoted_to_json(wa, property_name);
298 
299  impl_putc(wa, ':');
300  if (wa->do_ws) {
301  impl_putc(wa, ' ');
302  }
303 
304  impl_convert_value_to_json(wa, next);
305  next = afw_object_get_next_property(obj,
306  &property_iterator, &property_name, wa->xctx);
307  if (!next) break;
308  impl_putc(wa, ',');
309  }
310  }
311 
312  /* Decrement indent and put '}'. */
313  (wa->indent)--;
314  (wa->object_depth)--;
315  impl_put_ws(wa);
316  impl_putc(wa, '}');
317 }
318 
319 
320 static void
321 impl_convert_value_to_json(
323  const afw_value_t *value)
324 {
325  const afw_utf8_t *string;
326  const afw_object_t *object;
327  const afw_utf8_t *s;
328  const afw_data_type_t *value_data_type;
329  afw_value_info_t info;
330 
331  /* Change NULL value pointer to null. */
332  if (!value) {
333  value = afw_value_null;
334  }
335 
336  /* Put whitespace if needed. */
337  impl_put_ws(wa);
338 
339  /* If doing type values, start typed info. */
340  if (wa->do_typed_values) {
341  impl_putc(wa, '[');
342  (wa->indent)++;
343  }
344 
345  /* Value must be evaluated already. */
347  afw_value_get_info(value, &info, wa->p, wa->xctx);
348  AFW_THROW_ERROR_FZ(general, wa->xctx,
349  "Unevaluated value encountered producing json "
350  "(%" AFW_UTF8_FMT " %" AFW_UTF8_FMT ")",
351  AFW_UTF8_FMT_ARG(info.value_inf_id),
352  AFW_UTF8_FMT_OPTIONAL_ARG(info.detail)
353  );
354  }
355 
356  /* Get data type. */
357  value_data_type = afw_value_get_data_type(value, wa->xctx);
358 
359  /* If value is a list, convert list to json. */
360  if (afw_value_is_list(value)) {
361  impl_convert_list_to_json(wa, ((afw_value_list_t *)value)->internal);
362  }
363 
364  /* If value is object, convert object to json. */
365  else if (afw_value_is_object(value)) {
366  impl_convert_object_to_json(wa,
367  ((afw_value_object_t *)value)->internal);
368  }
369 
370  /* If value is single, process based on jsonPrimitive value of dataType. */
371  else {
372 
373  /* Primitive json type is null. */
374  if (afw_utf8_equal(&value_data_type->jsonPrimitive,
375  &AFW_JSON_S_PRIMITIVE_NULL))
376  {
377  impl_puts(wa, AFW_JSON_Q_PRIMITIVE_NULL);
378  }
379 
380  /* Primitive json type is string. */
381  else if (afw_utf8_equal(&value_data_type->jsonPrimitive,
382  &AFW_JSON_S_PRIMITIVE_STRING))
383  {
384  string = afw_value_as_utf8(value, wa->p, wa->xctx);
385  if (!string) {
386  AFW_THROW_ERROR_Z(general, "Error converting string.", wa->xctx);
387  }
388  impl_put_json_string(wa, string);
389  }
390 
391  /* Primitive json type is number. */
392  else if (afw_utf8_equal(&value_data_type->jsonPrimitive,
393  &AFW_JSON_S_PRIMITIVE_NUMBER))
394  {
395  if (afw_value_is_integer(value) &&
398  ((const afw_value_integer_t *)value)->internal))
399  )
400  {
401  string = afw_value_as_utf8(value, wa->p, wa->xctx);
402  if (!string) {
403  AFW_THROW_ERROR_Z(general, "Error converting string.",
404  wa->xctx);
405  }
406  impl_put_json_string(wa, string);
407  }
408 
409  else {
411  value_data_type,
412  &((const afw_value_evaluated_t *)value)->internal,
413  wa->p, wa->xctx);
414  impl_write(wa, string->s, string->len);
415  }
416  }
417 
418  /* Primitive json type is boolean. */
419  else if (afw_utf8_equal(&value_data_type->jsonPrimitive,
420  &AFW_JSON_S_PRIMITIVE_BOOLEAN))
421  {
422  impl_convert_boolean_to_json(wa,
423  ((afw_value_boolean_t *)value)->internal);
424  }
425 
426  /* If none of above then it's an error. */
427  else {
428  AFW_THROW_ERROR_Z(general,
429  "Value inf is invalid", wa->xctx);
430  }
431  }
432 
433  /* If doing type values, end typed info. */
434  if (wa->do_typed_values) {
435  impl_putc(wa, ',');
436  impl_put_ws(wa);
437  if (value_data_type) {
438  if (afw_value_is_object(value)) {
439  object = ((const afw_value_object_t *)value)->internal;
440  s = &afw_s_object;
441  if (afw_object_meta_get_object_type_id(object, xctx)) {
442  s = afw_utf8_printf(wa->p, wa->xctx,
443  "object:%" AFW_UTF8_FMT,
446  );
447  }
448  impl_put_json_string(wa, s);
449  }
450  else {
451  impl_put_json_string(wa, &value_data_type->data_type_id);
452  }
453  }
454  else {
455  impl_puts(wa, "null");
456  }
457  (wa->indent)--;
458  impl_put_ws(wa);
459  impl_putc(wa, ']');
460  }
461 }
462 
463 
464 /* Convert a value to json and write it. */
465 void
466 afw_json_internal_write_value(
467  const afw_value_t *value,
468  const afw_object_options_t *options,
469  void * context,
470  afw_write_cb_t callback,
471  const afw_pool_t *p,
472  afw_xctx_t *xctx)
473 {
475 
476  /* Initialize workarea and associated resources. */
477  afw_memory_clear(&wa);
478  wa.xctx = xctx;
479  wa.p = p;
480  wa.options = options;
481  wa.do_ws = AFW_OBJECT_OPTION_IS(options, whitespace);
482  wa.do_typed_values = AFW_OBJECT_OPTION_IS(options, typedValues);
483  wa.skip_next_ws = 1;
484  wa.context = context;
485  wa.callback = callback;
486 
487  /* Convert object to json. */
488  impl_convert_value_to_json(&wa, value);
489 }
490 
491 
492 /* Convert a value to json. */
493 AFW_DEFINE(const afw_utf8_t *)
495  const afw_value_t *value,
496  const afw_object_options_t *options,
497  const afw_pool_t *p, afw_xctx_t *xctx)
498 {
499  const afw_memory_writer_t *writer;
500  const afw_memory_t *raw;
501 
502  writer = afw_memory_create_writer(p, xctx);
503 
504  afw_json_internal_write_value(value, options,
505  writer->context, writer->callback, p, xctx);
506 
507  raw = afw_memory_writer_retrieve_and_release(writer, xctx);
508  return afw_utf8_from_raw(raw, p, xctx);
509 }
510 
511 
512 AFW_DEFINE(void)
514  const afw_utf8_t *string,
515  const afw_writer_t *writer,
516  afw_xctx_t *xctx)
517 {
519  afw_utf8_octet_t c2[2];
520  afw_size_t len;
521  const afw_utf8_octet_t *s;
522 
523  afw_writer_write_z(writer, "\"", xctx);
524 
525  s = string->s;
526  len = string->len;
527 
528  while (len > 0) {
529  c = *s;
530 
531  /* Add an extra backslash if character is backslash or quote. */
532  if (c == '\\' || c == '"') {
533  afw_writer_write_z(writer, "\\", xctx);
534  }
535 
536  /* If not a control character, output the character. */
537  if (c >= 32) {
538  afw_writer_write(writer, &c, 1, xctx);
539  }
540 
541  /* If \n */
542  else if (c == 10) {
543  afw_writer_write_z(writer, "\\n", xctx);
544  }
545 
546  /* If \r */
547  else if (c == 13) {
548  afw_writer_write_z(writer, "\\r", xctx);
549  }
550 
551  /* If \t */
552  else if (c == 9) {
553  afw_writer_write_z(writer, "\\t", xctx);
554  }
555 
556  /* If \f */
557  else if (c == 12) {
558  afw_writer_write_z(writer, "\\f", xctx);
559  }
560 
561  /* If \b */
562  else if (c == 7) {
563  afw_writer_write_z(writer, "\\b", xctx);
564  }
565 
566  /*
567  * If other control character, output as \u followed by character as four
568  * byte hex characters.
569  */
570  else {
571  afw_writer_write_z(writer, "\\u00", xctx);
572  c2[0] = c / 16 + '0';
573  c2[1] = c % 16 + '0';
574  afw_writer_write(writer, &c2, 2, xctx);
575  }
576 
577  len--;
578  s++;
579  }
580 
581  afw_writer_write_z(writer, "\"", xctx);
582 }
583 
584 
585 
586 AFW_DEFINE(const afw_utf8_t *)
588  const afw_utf8_t *string,
589  const afw_pool_t *p,
590  afw_xctx_t *xctx)
591 {
592  afw_value_string_t value;
593  const afw_utf8_t *result;
594 
596  afw_memory_copy(&value.internal, string);
597 
598  result = afw_json_from_value(
599  (const afw_value_t *)&value, NULL, p, xctx);
600 
601  return result;
602 }
AFW_DEFINE(const afw_object_t *)
Adaptive Framework Core Internal.
afw_value_evaluated_ia5String_inf
Unmanaged evaluated value inf for data type ia5String.
#define afw_value_is_integer(A_VALUE)
Macro to determine if value is evaluated integer.
#define afw_value_is_list(A_VALUE)
Macro to determine if value is evaluated list.
#define afw_value_is_object(A_VALUE)
Macro to determine if value is evaluated object.
#define AFW_UTF8_FMT_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify arg.
Definition: afw_common.h:605
struct afw_iterator_s afw_iterator_t
#define AFW_UTF8_FMT_OPTIONAL_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify optional arg.
Definition: afw_common.h:616
_Bool afw_boolean_t
Definition: afw_common.h:373
unsigned char afw_byte_t
A byte of memory (unsigned).
Definition: afw_common.h:208
#define AFW_UTF8_FMT
Format string specifier used for afw_utf8_t.
Definition: afw_common.h:588
afw_utf8_octet_t afw_utf8_z_t
NFC normalized UTF-8 null terminated string.
Definition: afw_common.h:523
char afw_utf8_octet_t
8 bits of utf-8 codepoint.
Definition: afw_common.h:236
apr_size_t afw_size_t
size_t.
Definition: afw_common.h:151
#define AFW_INTEGER_IS_SAFE_DOUBLE(integer)
Tests integer to be safely held in a double.
Definition: afw_common.h:313
afw_size_t(* afw_write_cb_t)(void *context, const void *buffer, afw_size_t size, const afw_pool_t *p, afw_xctx_t *xctx)
Typedef for write callback function.
Definition: afw_common.h:1226
#define afw_data_type_internal_to_utf8(instance, from_internal, p, xctx)
Call method internal_to_utf8 of interface afw_data_type.
#define AFW_THROW_ERROR_FZ(code, xctx, format_z,...)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:319
#define AFW_THROW_ERROR_Z(code, message_z, xctx)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:283
afw_json_utf8_string_create(const afw_utf8_t *string, const afw_pool_t *p, afw_xctx_t *xctx)
Create a json encoded quoted string.
afw_json_write_encoded_string(const afw_utf8_t *string, const afw_writer_t *writer, afw_xctx_t *xctx)
Write string as json encoded string.
afw_json_from_value(const afw_value_t *value, const afw_object_options_t *options, const afw_pool_t *p, afw_xctx_t *xctx)
Convert an adaptive value to JSON.
#define afw_list_get_next_value(instance, iterator, p, xctx)
Call method get_next_value of interface afw_list.
#define afw_memory_clear(to)
Clear preallocated memory for sizeof(*(to)).
Definition: afw_memory.h:47
afw_memory_writer_retrieve_and_release(const afw_memory_writer_t *writer, afw_xctx_t *xctx)
Retrieve memory as one chunk from memory writer and release writer.
Definition: afw_memory.c:441
afw_memory_create_writer(const afw_pool_t *p, afw_xctx_t *xctx)
Create a memory writer.
Definition: afw_memory.c:377
#define afw_memory_copy(to, from)
Copy to preallocated memory of same type.
Definition: afw_memory.h:39
#define afw_object_get_next_property(instance, iterator, property_name, xctx)
Call method get_next_property of interface afw_object.
#define afw_object_meta_get_object_type_id(instance, xctx)
Get object's object_type_id.
const afw_object_t * afw_object_meta_create_accessor_with_options(const afw_object_t *instance, const afw_object_options_t *options, const afw_pool_t *p, afw_xctx_t *xctx)
Create an object instance to access an object's meta with options.
#define AFW_OBJECT_OPTION_IS(_options, _option)
Test mask.
const afw_utf8_t * afw_utf8_from_raw(const afw_memory_t *raw, const afw_pool_t *p, afw_xctx_t *xctx)
Convert raw to a utf-8 NFC normalizing if necessary in specified pool.
Definition: afw_utf8.h:199
afw_utf8_printf_v(const afw_utf8_z_t *format, va_list arg, const afw_pool_t *p, afw_xctx_t *xctx)
Create a utf-8 string using a c format string in specified pool.
Definition: afw_utf8.c:477
afw_boolean_t afw_utf8_equal(const afw_utf8_t *s1, const afw_utf8_t *s2)
Check to see if a string equals another string.
afw_utf8_printf(const afw_pool_t *p, afw_xctx_t *xctx, const afw_utf8_z_t *format,...)
Create a utf-8 string using a c format string in specified pool.
Definition: afw_utf8.c:459
#define afw_value_get_data_type(instance, xctx)
Call method get_data_type of interface afw_value.
#define afw_value_get_info(instance, info, p, xctx)
Call method get_info of interface afw_value.
afw_value_as_utf8(const afw_value_t *value, const afw_pool_t *p, afw_xctx_t *xctx)
Definition: afw_value.c:456
#define afw_value_is_defined_and_evaluated(A_VALUE)
Macro to determine if value is defined and evaluated.
Definition: afw_value.h:481
afw_value_null
Adaptive value null.
Definition: afw_value.h:320
#define afw_writer_write(instance, buffer, size, xctx)
Call method write of interface afw_writer.
#define afw_writer_write_z(writer, s_z, xctx)
Call afw_writer_write() with zero terminated string.
Definition: afw_writer.h:35
Interface afw_data_type public struct.
Interface afw_list public struct.
Struct for memory pointer and size.
Definition: afw_common.h:505
Return value from afw_memory_create_writer().
Definition: afw_memory.h:102
Struct for object processing options.
Interface afw_object public struct.
Interface afw_pool public struct.
NFC normalized UTF-8 string.
Definition: afw_common.h:545
struct for data type boolean values.
Struct to access internal of all evaluated values.
Definition: afw_value.h:60
Filled in by afw_value get_info method.
Definition: afw_value.h:49
struct for data type integer values.
struct for data type list values.
struct for data type object values.
Interface afw_value public struct.
struct for data type string values.
Interface afw_writer public struct.
Interface afw_xctx public struct.