aboutsummaryrefslogtreecommitdiffstats
path: root/src/json.c
diff options
context:
space:
mode:
authorJoão Távora2018-06-08 02:35:50 +0100
committerJoão Távora2018-06-15 00:13:00 +0100
commit51adab5de24b3ee215fe636aedb7ff91d69a220c (patch)
tree8664618622113e4890c574d07fa2e025424b951b /src/json.c
parent9348039ed45c8e493e8bfef0220249d4d31ef6da (diff)
downloademacs-51adab5de24b3ee215fe636aedb7ff91d69a220c.tar.gz
emacs-51adab5de24b3ee215fe636aedb7ff91d69a220c.zip
Also allow custom false and null when serializing to JSON
* doc/lispref/text.texi (Parsing JSON): Describe new arguments of json-serialize and json-insert. * src/json.c (enum json_object_type, struct json_configuration): Move up in file before first usage. (lisp_to_json_toplevel, lisp_to_json_toplevel_1, lisp_to_json): Accept a struct json_configuration*. (Fjson_serialize, Fjson_insert): Accept multiple args. (json_parse_args): Accept new boolean configure_object_type. * test/src/json-tests.el (json-serialize, json-insert): Update forward decls. (json-parse-with-custom-null-and-false-objects): Add assertions for json-serialize.
Diffstat (limited to 'src/json.c')
-rw-r--r--src/json.c195
1 files changed, 113 insertions, 82 deletions
diff --git a/src/json.c b/src/json.c
index e86ef237d03..d30c997da4c 100644
--- a/src/json.c
+++ b/src/json.c
@@ -325,12 +325,25 @@ json_check_utf8 (Lisp_Object string)
325 CHECK_TYPE (utf8_string_p (string), Qutf_8_string_p, string); 325 CHECK_TYPE (utf8_string_p (string), Qutf_8_string_p, string);
326} 326}
327 327
328static json_t *lisp_to_json (Lisp_Object); 328enum json_object_type {
329 json_object_hashtable,
330 json_object_alist,
331 json_object_plist
332};
333
334struct json_configuration {
335 enum json_object_type object_type;
336 Lisp_Object null_object;
337 Lisp_Object false_object;
338};
339
340static json_t *lisp_to_json (Lisp_Object, struct json_configuration *conf);
329 341
330/* Convert a Lisp object to a toplevel JSON object (array or object). */ 342/* Convert a Lisp object to a toplevel JSON object (array or object). */
331 343
332static json_t * 344static json_t *
333lisp_to_json_toplevel_1 (Lisp_Object lisp) 345lisp_to_json_toplevel_1 (Lisp_Object lisp,
346 struct json_configuration *conf)
334{ 347{
335 json_t *json; 348 json_t *json;
336 ptrdiff_t count; 349 ptrdiff_t count;
@@ -344,7 +357,8 @@ lisp_to_json_toplevel_1 (Lisp_Object lisp)
344 for (ptrdiff_t i = 0; i < size; ++i) 357 for (ptrdiff_t i = 0; i < size; ++i)
345 { 358 {
346 int status 359 int status
347 = json_array_append_new (json, lisp_to_json (AREF (lisp, i))); 360 = json_array_append_new (json, lisp_to_json (AREF (lisp, i),
361 conf));
348 if (status == -1) 362 if (status == -1)
349 json_out_of_memory (); 363 json_out_of_memory ();
350 } 364 }
@@ -369,7 +383,8 @@ lisp_to_json_toplevel_1 (Lisp_Object lisp)
369 if (json_object_get (json, key_str) != NULL) 383 if (json_object_get (json, key_str) != NULL)
370 wrong_type_argument (Qjson_value_p, lisp); 384 wrong_type_argument (Qjson_value_p, lisp);
371 int status = json_object_set_new (json, key_str, 385 int status = json_object_set_new (json, key_str,
372 lisp_to_json (HASH_VALUE (h, i))); 386 lisp_to_json (HASH_VALUE (h, i),
387 conf));
373 if (status == -1) 388 if (status == -1)
374 { 389 {
375 /* A failure can be caused either by an invalid key or 390 /* A failure can be caused either by an invalid key or
@@ -424,7 +439,8 @@ lisp_to_json_toplevel_1 (Lisp_Object lisp)
424 if (json_object_get (json, key_str) == NULL) 439 if (json_object_get (json, key_str) == NULL)
425 { 440 {
426 int status 441 int status
427 = json_object_set_new (json, key_str, lisp_to_json (value)); 442 = json_object_set_new (json, key_str, lisp_to_json (value,
443 conf));
428 if (status == -1) 444 if (status == -1)
429 json_out_of_memory (); 445 json_out_of_memory ();
430 } 446 }
@@ -444,11 +460,11 @@ lisp_to_json_toplevel_1 (Lisp_Object lisp)
444 hashtable, alist, or plist. */ 460 hashtable, alist, or plist. */
445 461
446static json_t * 462static json_t *
447lisp_to_json_toplevel (Lisp_Object lisp) 463lisp_to_json_toplevel (Lisp_Object lisp, struct json_configuration *conf)
448{ 464{
449 if (++lisp_eval_depth > max_lisp_eval_depth) 465 if (++lisp_eval_depth > max_lisp_eval_depth)
450 xsignal0 (Qjson_object_too_deep); 466 xsignal0 (Qjson_object_too_deep);
451 json_t *json = lisp_to_json_toplevel_1 (lisp); 467 json_t *json = lisp_to_json_toplevel_1 (lisp, conf);
452 --lisp_eval_depth; 468 --lisp_eval_depth;
453 return json; 469 return json;
454} 470}
@@ -458,11 +474,11 @@ lisp_to_json_toplevel (Lisp_Object lisp)
458 JSON object. */ 474 JSON object. */
459 475
460static json_t * 476static json_t *
461lisp_to_json (Lisp_Object lisp) 477lisp_to_json (Lisp_Object lisp, struct json_configuration *conf)
462{ 478{
463 if (EQ (lisp, QCnull)) 479 if (EQ (lisp, conf->null_object))
464 return json_check (json_null ()); 480 return json_check (json_null ());
465 else if (EQ (lisp, QCfalse)) 481 else if (EQ (lisp, conf->false_object))
466 return json_check (json_false ()); 482 return json_check (json_false ());
467 else if (EQ (lisp, Qt)) 483 else if (EQ (lisp, Qt))
468 return json_check (json_true ()); 484 return json_check (json_true ());
@@ -488,21 +504,78 @@ lisp_to_json (Lisp_Object lisp)
488 } 504 }
489 505
490 /* LISP now must be a vector, hashtable, alist, or plist. */ 506 /* LISP now must be a vector, hashtable, alist, or plist. */
491 return lisp_to_json_toplevel (lisp); 507 return lisp_to_json_toplevel (lisp, conf);
492} 508}
493 509
494DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, 1, NULL, 510static void
511json_parse_args (ptrdiff_t nargs,
512 Lisp_Object *args,
513 struct json_configuration *conf,
514 bool configure_object_type)
515{
516 if ((nargs % 2) != 0)
517 wrong_type_argument (Qplistp, Flist (nargs, args));
518
519 /* Start from the back so keyword values appearing
520 first take precedence. */
521 for (ptrdiff_t i = nargs; i > 0; i -= 2) {
522 Lisp_Object key = args[i - 2];
523 Lisp_Object value = args[i - 1];
524 if (configure_object_type && EQ (key, QCobject_type))
525 {
526 if (EQ (value, Qhash_table))
527 conf->object_type = json_object_hashtable;
528 else if (EQ (value, Qalist))
529 conf->object_type = json_object_alist;
530 else if (EQ (value, Qplist))
531 conf->object_type = json_object_plist;
532 else
533 wrong_choice (list3 (Qhash_table, Qalist, Qplist), value);
534 }
535 else if (EQ (key, QCnull_object))
536 conf->null_object = value;
537 else if (EQ (key, QCfalse_object))
538 conf->false_object = value;
539 else if (configure_object_type)
540 wrong_choice (list3 (QCobject_type,
541 QCnull_object,
542 QCfalse_object),
543 value);
544 else
545 wrong_choice (list2 (QCnull_object,
546 QCfalse_object),
547 value);
548 }
549}
550
551DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, MANY,
552 NULL,
495 doc: /* Return the JSON representation of OBJECT as a string. 553 doc: /* Return the JSON representation of OBJECT as a string.
554
496OBJECT must be a vector, hashtable, alist, or plist and its elements 555OBJECT must be a vector, hashtable, alist, or plist and its elements
497can recursively contain `:null', `:false', t, numbers, strings, or 556can recursively contain the Lisp equivalents to the JSON null and
498other vectors hashtables, alists or plists. `:null', `:false', and t 557false values, t, numbers, strings, or other vectors hashtables, alists
499will be converted to JSON null, false, and true values, respectively. 558or plists. t will be converted to the JSON true value. Vectors will
500Vectors will be converted to JSON arrays, whereas hashtables, alists 559be converted to JSON arrays, whereas hashtables, alists and plists are
501and plists are converted to JSON objects. Hashtable keys must be 560converted to JSON objects. Hashtable keys must be strings without
502strings without embedded null characters and must be unique within 561embedded null characters and must be unique within each object. Alist
503each object. Alist and plist keys must be symbols; if a key is 562and plist keys must be symbols; if a key is duplicate, the first
504duplicate, the first instance is used. */) 563instance is used.
505 (Lisp_Object object) 564
565The Lisp equivalents to the JSON null and false values are
566configurable in the arguments ARGS, a list of keyword/argument pairs:
567
568The keyword argument `:null-object' specifies which object to use
569to represent a JSON null value. It defaults to `:null'.
570
571The keyword argument `:false-object' specifies which object to use to
572represent a JSON false value. It defaults to `:false'.
573
574In you specify the same value for `:null-object' and `:false-object',
575a potentially ambiguous situation, the JSON output will not contain
576any JSON false values.
577usage: (json-serialize STRING &rest ARGS) */)
578 (ptrdiff_t nargs, Lisp_Object *args)
506{ 579{
507 ptrdiff_t count = SPECPDL_INDEX (); 580 ptrdiff_t count = SPECPDL_INDEX ();
508 581
@@ -521,7 +594,10 @@ duplicate, the first instance is used. */)
521 } 594 }
522#endif 595#endif
523 596
524 json_t *json = lisp_to_json_toplevel (object); 597 struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
598 json_parse_args (nargs - 1, args + 1, &conf, false);
599
600 json_t *json = lisp_to_json_toplevel (args[0], &conf);
525 record_unwind_protect_ptr (json_release_object, json); 601 record_unwind_protect_ptr (json_release_object, json);
526 602
527 /* If desired, we might want to add the following flags: 603 /* If desired, we might want to add the following flags:
@@ -577,12 +653,13 @@ json_insert_callback (const char *buffer, size_t size, void *data)
577 return NILP (d->error) ? 0 : -1; 653 return NILP (d->error) ? 0 : -1;
578} 654}
579 655
580DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, 1, NULL, 656DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, MANY,
657 NULL,
581 doc: /* Insert the JSON representation of OBJECT before point. 658 doc: /* Insert the JSON representation of OBJECT before point.
582 This is the same as (insert (json-serialize OBJECT)), but potentially 659This is the same as (insert (json-serialize OBJECT)), but potentially
583 faster. See the function `json-serialize' for allowed values of 660faster. See the function `json-serialize' for allowed values of
584 OBJECT. */) 661OBJECT. */)
585 (Lisp_Object object) 662 (ptrdiff_t nargs, Lisp_Object *args)
586{ 663{
587 ptrdiff_t count = SPECPDL_INDEX (); 664 ptrdiff_t count = SPECPDL_INDEX ();
588 665
@@ -601,7 +678,10 @@ DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, 1, NULL,
601 } 678 }
602#endif 679#endif
603 680
604 json_t *json = lisp_to_json (object); 681 struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
682 json_parse_args (nargs - 1, args + 1, &conf, false);
683
684 json_t *json = lisp_to_json (args[0], &conf);
605 record_unwind_protect_ptr (json_release_object, json); 685 record_unwind_protect_ptr (json_release_object, json);
606 686
607 struct json_insert_data data; 687 struct json_insert_data data;
@@ -620,18 +700,6 @@ DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, 1, NULL,
620 return unbind_to (count, Qnil); 700 return unbind_to (count, Qnil);
621} 701}
622 702
623enum json_object_type {
624 json_object_hashtable,
625 json_object_alist,
626 json_object_plist
627};
628
629struct json_configuration {
630 enum json_object_type object_type;
631 Lisp_Object null_object;
632 Lisp_Object false_object;
633};
634
635/* Convert a JSON object to a Lisp object. */ 703/* Convert a JSON object to a Lisp object. */
636 704
637static _GL_ARG_NONNULL ((1)) Lisp_Object 705static _GL_ARG_NONNULL ((1)) Lisp_Object
@@ -751,42 +819,6 @@ json_to_lisp (json_t *json, struct json_configuration *conf)
751 emacs_abort (); 819 emacs_abort ();
752} 820}
753 821
754static void
755json_parse_args (ptrdiff_t nargs,
756 Lisp_Object *args,
757 struct json_configuration *conf)
758{
759 if ((nargs % 2) != 0)
760 wrong_type_argument (Qplistp, Flist (nargs, args));
761
762 /* Start from the back so keyword values appearing
763 first take precedence. */
764 for (ptrdiff_t i = nargs; i > 0; i -= 2) {
765 Lisp_Object key = args[i - 2];
766 Lisp_Object value = args[i - 1];
767 if (EQ (key, QCobject_type))
768 {
769 if (EQ (value, Qhash_table))
770 conf->object_type = json_object_hashtable;
771 else if (EQ (value, Qalist))
772 conf->object_type = json_object_alist;
773 else if (EQ (value, Qplist))
774 conf->object_type = json_object_plist;
775 else
776 wrong_choice (list3 (Qhash_table, Qalist, Qplist), value);
777 }
778 else if (EQ (key, QCnull_object))
779 conf->null_object = value;
780 else if (EQ (key, QCfalse_object))
781 conf->false_object = value;
782 else
783 wrong_choice (list3 (QCobject_type,
784 QCnull_object,
785 QCfalse_object),
786 value);
787 }
788}
789
790DEFUN ("json-parse-string", Fjson_parse_string, Sjson_parse_string, 1, MANY, 822DEFUN ("json-parse-string", Fjson_parse_string, Sjson_parse_string, 1, MANY,
791 NULL, 823 NULL,
792 doc: /* Parse the JSON STRING into a Lisp object. 824 doc: /* Parse the JSON STRING into a Lisp object.
@@ -808,9 +840,8 @@ to represent a JSON null value. It defaults to `:null'.
808 840
809The keyword argument `:false-object' specifies which object to use to 841The keyword argument `:false-object' specifies which object to use to
810represent a JSON false value. It defaults to `:false'. 842represent a JSON false value. It defaults to `:false'.
811 843usage: (json-parse-string STRING &rest ARGS) */)
812usage: (json-parse-string STRING &rest args) */) 844 (ptrdiff_t nargs, Lisp_Object *args)
813 (ptrdiff_t nargs, Lisp_Object *args)
814{ 845{
815 ptrdiff_t count = SPECPDL_INDEX (); 846 ptrdiff_t count = SPECPDL_INDEX ();
816 847
@@ -833,7 +864,7 @@ usage: (json-parse-string STRING &rest args) */)
833 Lisp_Object encoded = json_encode (string); 864 Lisp_Object encoded = json_encode (string);
834 check_string_without_embedded_nulls (encoded); 865 check_string_without_embedded_nulls (encoded);
835 struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse}; 866 struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
836 json_parse_args (nargs - 1, args + 1, &conf); 867 json_parse_args (nargs - 1, args + 1, &conf, true);
837 868
838 json_error_t error; 869 json_error_t error;
839 json_t *object = json_loads (SSDATA (encoded), 0, &error); 870 json_t *object = json_loads (SSDATA (encoded), 0, &error);
@@ -882,7 +913,7 @@ DEFUN ("json-parse-buffer", Fjson_parse_buffer, Sjson_parse_buffer,
882This is similar to `json-parse-string', which see. Move point after 913This is similar to `json-parse-string', which see. Move point after
883the end of the object if parsing was successful. On error, point is 914the end of the object if parsing was successful. On error, point is
884not moved. 915not moved.
885usage: (json-parse-buffer &rest args) */) 916usage: (json-parse-buffer &rest args) */)
886 (ptrdiff_t nargs, Lisp_Object *args) 917 (ptrdiff_t nargs, Lisp_Object *args)
887{ 918{
888 ptrdiff_t count = SPECPDL_INDEX (); 919 ptrdiff_t count = SPECPDL_INDEX ();
@@ -903,7 +934,7 @@ usage: (json-parse-buffer &rest args) */)
903#endif 934#endif
904 935
905 struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse}; 936 struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
906 json_parse_args (nargs, args, &conf); 937 json_parse_args (nargs, args, &conf, true);
907 938
908 ptrdiff_t point = PT_BYTE; 939 ptrdiff_t point = PT_BYTE;
909 struct json_read_buffer_data data = {.point = point}; 940 struct json_read_buffer_data data = {.point = point};