aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPhilipp Stephani2017-12-13 23:35:07 +0100
committerPhilipp Stephani2017-12-19 18:20:55 +0100
commitdb4f12e93f466832a5e5e1d512aff87ea90ef197 (patch)
tree585a95c4cfb8cf765e8df7dc0fe623ed786b6444 /src
parent16813e6faa32b1741685ee429132251846d253a3 (diff)
downloademacs-db4f12e93f466832a5e5e1d512aff87ea90ef197.tar.gz
emacs-db4f12e93f466832a5e5e1d512aff87ea90ef197.zip
Allow JSON parser functions to return alists
* src/json.c (Fjson_parse_string, Fjson_parse_buffer): Give these functions a keyword argument to specify the return type for JSON objects. (json_to_lisp): Convert objects to alists if requested. (json_parse_object_type): New helper function to parse keyword arguments. * test/src/json-tests.el (json-parse-string/object): Add a unit test. * doc/lispref/text.texi (Parsing JSON): Document new functionality.
Diffstat (limited to 'src')
-rw-r--r--src/json.c129
1 files changed, 99 insertions, 30 deletions
diff --git a/src/json.c b/src/json.c
index 29e4400fc91..47c5b8ff468 100644
--- a/src/json.c
+++ b/src/json.c
@@ -518,10 +518,15 @@ OBJECT. */)
518 return unbind_to (count, Qnil); 518 return unbind_to (count, Qnil);
519} 519}
520 520
521enum json_object_type {
522 json_object_hashtable,
523 json_object_alist,
524};
525
521/* Convert a JSON object to a Lisp object. */ 526/* Convert a JSON object to a Lisp object. */
522 527
523static _GL_ARG_NONNULL ((1)) Lisp_Object 528static _GL_ARG_NONNULL ((1)) Lisp_Object
524json_to_lisp (json_t *json) 529json_to_lisp (json_t *json, enum json_object_type object_type)
525{ 530{
526 switch (json_typeof (json)) 531 switch (json_typeof (json))
527 { 532 {
@@ -555,7 +560,7 @@ json_to_lisp (json_t *json)
555 Lisp_Object result = Fmake_vector (make_natnum (size), Qunbound); 560 Lisp_Object result = Fmake_vector (make_natnum (size), Qunbound);
556 for (ptrdiff_t i = 0; i < size; ++i) 561 for (ptrdiff_t i = 0; i < size; ++i)
557 ASET (result, i, 562 ASET (result, i,
558 json_to_lisp (json_array_get (json, i))); 563 json_to_lisp (json_array_get (json, i), object_type));
559 --lisp_eval_depth; 564 --lisp_eval_depth;
560 return result; 565 return result;
561 } 566 }
@@ -563,23 +568,49 @@ json_to_lisp (json_t *json)
563 { 568 {
564 if (++lisp_eval_depth > max_lisp_eval_depth) 569 if (++lisp_eval_depth > max_lisp_eval_depth)
565 xsignal0 (Qjson_object_too_deep); 570 xsignal0 (Qjson_object_too_deep);
566 size_t size = json_object_size (json); 571 Lisp_Object result;
567 if (FIXNUM_OVERFLOW_P (size)) 572 switch (object_type)
568 xsignal0 (Qoverflow_error);
569 Lisp_Object result = CALLN (Fmake_hash_table, QCtest, Qequal,
570 QCsize, make_natnum (size));
571 struct Lisp_Hash_Table *h = XHASH_TABLE (result);
572 const char *key_str;
573 json_t *value;
574 json_object_foreach (json, key_str, value)
575 { 573 {
576 Lisp_Object key = json_build_string (key_str); 574 case json_object_hashtable:
577 EMACS_UINT hash; 575 {
578 ptrdiff_t i = hash_lookup (h, key, &hash); 576 size_t size = json_object_size (json);
579 /* Keys in JSON objects are unique, so the key can't be 577 if (FIXNUM_OVERFLOW_P (size))
580 present yet. */ 578 xsignal0 (Qoverflow_error);
581 eassert (i < 0); 579 result = CALLN (Fmake_hash_table, QCtest, Qequal, QCsize,
582 hash_put (h, key, json_to_lisp (value), hash); 580 make_natnum (size));
581 struct Lisp_Hash_Table *h = XHASH_TABLE (result);
582 const char *key_str;
583 json_t *value;
584 json_object_foreach (json, key_str, value)
585 {
586 Lisp_Object key = json_build_string (key_str);
587 EMACS_UINT hash;
588 ptrdiff_t i = hash_lookup (h, key, &hash);
589 /* Keys in JSON objects are unique, so the key can't
590 be present yet. */
591 eassert (i < 0);
592 hash_put (h, key, json_to_lisp (value, object_type), hash);
593 }
594 break;
595 }
596 case json_object_alist:
597 {
598 result = Qnil;
599 const char *key_str;
600 json_t *value;
601 json_object_foreach (json, key_str, value)
602 {
603 Lisp_Object key = Fintern (json_build_string (key_str), Qnil);
604 result
605 = Fcons (Fcons (key, json_to_lisp (value, object_type)),
606 result);
607 }
608 result = Fnreverse (result);
609 break;
610 }
611 default:
612 /* Can't get here. */
613 emacs_abort ();
583 } 614 }
584 --lisp_eval_depth; 615 --lisp_eval_depth;
585 return result; 616 return result;
@@ -589,15 +620,44 @@ json_to_lisp (json_t *json)
589 emacs_abort (); 620 emacs_abort ();
590} 621}
591 622
592DEFUN ("json-parse-string", Fjson_parse_string, Sjson_parse_string, 1, 1, NULL, 623static enum json_object_type
624json_parse_object_type (ptrdiff_t nargs, Lisp_Object *args)
625{
626 switch (nargs)
627 {
628 case 0:
629 return json_object_hashtable;
630 case 2:
631 {
632 Lisp_Object key = args[0];
633 Lisp_Object value = args[1];
634 if (!EQ (key, QCobject_type))
635 wrong_choice (list1 (QCobject_type), key);
636 if (EQ (value, Qhash_table))
637 return json_object_hashtable;
638 else if (EQ (value, Qalist))
639 return json_object_alist;
640 else
641 wrong_choice (list2 (Qhash_table, Qalist), value);
642 }
643 default:
644 wrong_type_argument (Qplistp, Flist (nargs, args));
645 }
646}
647
648DEFUN ("json-parse-string", Fjson_parse_string, Sjson_parse_string, 1, MANY,
649 NULL,
593 doc: /* Parse the JSON STRING into a Lisp object. 650 doc: /* Parse the JSON STRING into a Lisp object.
594This is essentially the reverse operation of `json-serialize', which 651This is essentially the reverse operation of `json-serialize', which
595see. The returned object will be a vector or hashtable. Its elements 652see. The returned object will be a vector, hashtable, or alist. Its
596will be `:null', `:false', t, numbers, strings, or further vectors and 653elements will be `:null', `:false', t, numbers, strings, or further
597hashtables. If there are duplicate keys in an object, all but the 654vectors, hashtables, and alists. If there are duplicate keys in an
598last one are ignored. If STRING doesn't contain a valid JSON object, 655object, all but the last one are ignored. If STRING doesn't contain a
599an error of type `json-parse-error' is signaled. */) 656valid JSON object, an error of type `json-parse-error' is signaled.
600 (Lisp_Object string) 657The keyword argument `:object-type' specifies which Lisp type is used
658to represent objects; it can be `hash-table' or `alist'.
659usage: (string &key (OBJECT-TYPE \\='hash-table)) */)
660 (ptrdiff_t nargs, Lisp_Object *args)
601{ 661{
602 ptrdiff_t count = SPECPDL_INDEX (); 662 ptrdiff_t count = SPECPDL_INDEX ();
603 663
@@ -616,8 +676,11 @@ an error of type `json-parse-error' is signaled. */)
616 } 676 }
617#endif 677#endif
618 678
679 Lisp_Object string = args[0];
619 Lisp_Object encoded = json_encode (string); 680 Lisp_Object encoded = json_encode (string);
620 check_string_without_embedded_nulls (encoded); 681 check_string_without_embedded_nulls (encoded);
682 enum json_object_type object_type
683 = json_parse_object_type (nargs - 1, args + 1);
621 684
622 json_error_t error; 685 json_error_t error;
623 json_t *object = json_loads (SSDATA (encoded), 0, &error); 686 json_t *object = json_loads (SSDATA (encoded), 0, &error);
@@ -628,7 +691,7 @@ an error of type `json-parse-error' is signaled. */)
628 if (object != NULL) 691 if (object != NULL)
629 record_unwind_protect_ptr (json_release_object, object); 692 record_unwind_protect_ptr (json_release_object, object);
630 693
631 return unbind_to (count, json_to_lisp (object)); 694 return unbind_to (count, json_to_lisp (object, object_type));
632} 695}
633 696
634struct json_read_buffer_data 697struct json_read_buffer_data
@@ -661,12 +724,13 @@ json_read_buffer_callback (void *buffer, size_t buflen, void *data)
661} 724}
662 725
663DEFUN ("json-parse-buffer", Fjson_parse_buffer, Sjson_parse_buffer, 726DEFUN ("json-parse-buffer", Fjson_parse_buffer, Sjson_parse_buffer,
664 0, 0, NULL, 727 0, MANY, NULL,
665 doc: /* Read JSON object from current buffer starting at point. 728 doc: /* Read JSON object from current buffer starting at point.
666This is similar to `json-parse-string', which see. Move point after 729This is similar to `json-parse-string', which see. Move point after
667the end of the object if parsing was successful. On error, point is 730the end of the object if parsing was successful. On error, point is
668not moved. */) 731not moved.
669 (void) 732usage: (&key (OBJECT-TYPE \\='hash-table)) */)
733 (ptrdiff_t nargs, Lisp_Object *args)
670{ 734{
671 ptrdiff_t count = SPECPDL_INDEX (); 735 ptrdiff_t count = SPECPDL_INDEX ();
672 736
@@ -685,6 +749,8 @@ not moved. */)
685 } 749 }
686#endif 750#endif
687 751
752 enum json_object_type object_type = json_parse_object_type (nargs, args);
753
688 ptrdiff_t point = PT_BYTE; 754 ptrdiff_t point = PT_BYTE;
689 struct json_read_buffer_data data = {.point = point}; 755 struct json_read_buffer_data data = {.point = point};
690 json_error_t error; 756 json_error_t error;
@@ -698,7 +764,7 @@ not moved. */)
698 record_unwind_protect_ptr (json_release_object, object); 764 record_unwind_protect_ptr (json_release_object, object);
699 765
700 /* Convert and then move point only if everything succeeded. */ 766 /* Convert and then move point only if everything succeeded. */
701 Lisp_Object lisp = json_to_lisp (object); 767 Lisp_Object lisp = json_to_lisp (object, object_type);
702 768
703 /* Adjust point by how much we just read. */ 769 /* Adjust point by how much we just read. */
704 point += error.position; 770 point += error.position;
@@ -761,6 +827,9 @@ syms_of_json (void)
761 Fput (Qjson_parse_string, Qpure, Qt); 827 Fput (Qjson_parse_string, Qpure, Qt);
762 Fput (Qjson_parse_string, Qside_effect_free, Qt); 828 Fput (Qjson_parse_string, Qside_effect_free, Qt);
763 829
830 DEFSYM (QCobject_type, ":object-type");
831 DEFSYM (Qalist, "alist");
832
764 defsubr (&Sjson_serialize); 833 defsubr (&Sjson_serialize);
765 defsubr (&Sjson_insert); 834 defsubr (&Sjson_insert);
766 defsubr (&Sjson_parse_string); 835 defsubr (&Sjson_parse_string);