diff options
| author | João Távora | 2018-06-07 17:41:19 +0100 |
|---|---|---|
| committer | João Távora | 2018-06-15 00:11:56 +0100 |
| commit | 9348039ed45c8e493e8bfef0220249d4d31ef6da (patch) | |
| tree | e7f79a9013d4b80bfb6b980a216419662f982866 /src/json.c | |
| parent | 8cb9beb32163fa3ce3b052ced646fd673814ddc6 (diff) | |
| download | emacs-9348039ed45c8e493e8bfef0220249d4d31ef6da.tar.gz emacs-9348039ed45c8e493e8bfef0220249d4d31ef6da.zip | |
Support custom null and false objects when parsing JSON
* doc/lispref/text.texi (Parsing JSON): Describe new :null-object
and :false-object kwargs to json-parse-string and
json-parse-buffer.
* src/json.c
(struct json_configuration): New type.
(json_to_lisp): Accept a struct json_configuration* param.
(json_parse_args): Rename from json_parse_object_type.
(Fjson_parse_string): Rework docstring.
(Fjson_parse_string, Fjson_parse_buffer): Update call to
json_to_lisp.
(syms_of_json): Two new syms, QCnull_object and QCfalse_object.
* test/src/json-tests.el
(json-parse-with-custom-null-and-false-objects): New test.
Diffstat (limited to 'src/json.c')
| -rw-r--r-- | src/json.c | 136 |
1 files changed, 82 insertions, 54 deletions
diff --git a/src/json.c b/src/json.c index c28e14d63c6..e86ef237d03 100644 --- a/src/json.c +++ b/src/json.c | |||
| @@ -7,7 +7,7 @@ This file is part of GNU Emacs. | |||
| 7 | GNU Emacs is free software: you can redistribute it and/or modify | 7 | GNU Emacs is free software: you can redistribute it and/or modify |
| 8 | it under the terms of the GNU General Public License as published by | 8 | it under the terms of the GNU General Public License as published by |
| 9 | the Free Software Foundation, either version 3 of the License, or (at | 9 | the Free Software Foundation, either version 3 of the License, or (at |
| 10 | your option) any later version. | 10 | nyour option) any later version. |
| 11 | 11 | ||
| 12 | GNU Emacs is distributed in the hope that it will be useful, | 12 | GNU Emacs is distributed in the hope that it will be useful, |
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| @@ -502,7 +502,7 @@ and plists are converted to JSON objects. Hashtable keys must be | |||
| 502 | strings without embedded null characters and must be unique within | 502 | strings without embedded null characters and must be unique within |
| 503 | each object. Alist and plist keys must be symbols; if a key is | 503 | each object. Alist and plist keys must be symbols; if a key is |
| 504 | duplicate, the first instance is used. */) | 504 | duplicate, the first instance is used. */) |
| 505 | (Lisp_Object object) | 505 | (Lisp_Object object) |
| 506 | { | 506 | { |
| 507 | ptrdiff_t count = SPECPDL_INDEX (); | 507 | ptrdiff_t count = SPECPDL_INDEX (); |
| 508 | 508 | ||
| @@ -579,10 +579,10 @@ json_insert_callback (const char *buffer, size_t size, void *data) | |||
| 579 | 579 | ||
| 580 | DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, 1, NULL, | 580 | DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, 1, NULL, |
| 581 | doc: /* Insert the JSON representation of OBJECT before point. | 581 | doc: /* Insert the JSON representation of OBJECT before point. |
| 582 | This is the same as (insert (json-serialize OBJECT)), but potentially | 582 | This is the same as (insert (json-serialize OBJECT)), but potentially |
| 583 | faster. See the function `json-serialize' for allowed values of | 583 | faster. See the function `json-serialize' for allowed values of |
| 584 | OBJECT. */) | 584 | OBJECT. */) |
| 585 | (Lisp_Object object) | 585 | (Lisp_Object object) |
| 586 | { | 586 | { |
| 587 | ptrdiff_t count = SPECPDL_INDEX (); | 587 | ptrdiff_t count = SPECPDL_INDEX (); |
| 588 | 588 | ||
| @@ -621,22 +621,28 @@ OBJECT. */) | |||
| 621 | } | 621 | } |
| 622 | 622 | ||
| 623 | enum json_object_type { | 623 | enum json_object_type { |
| 624 | json_object_hashtable, | 624 | json_object_hashtable, |
| 625 | json_object_alist, | 625 | json_object_alist, |
| 626 | json_object_plist | 626 | json_object_plist |
| 627 | }; | ||
| 628 | |||
| 629 | struct json_configuration { | ||
| 630 | enum json_object_type object_type; | ||
| 631 | Lisp_Object null_object; | ||
| 632 | Lisp_Object false_object; | ||
| 627 | }; | 633 | }; |
| 628 | 634 | ||
| 629 | /* Convert a JSON object to a Lisp object. */ | 635 | /* Convert a JSON object to a Lisp object. */ |
| 630 | 636 | ||
| 631 | static _GL_ARG_NONNULL ((1)) Lisp_Object | 637 | static _GL_ARG_NONNULL ((1)) Lisp_Object |
| 632 | json_to_lisp (json_t *json, enum json_object_type object_type) | 638 | json_to_lisp (json_t *json, struct json_configuration *conf) |
| 633 | { | 639 | { |
| 634 | switch (json_typeof (json)) | 640 | switch (json_typeof (json)) |
| 635 | { | 641 | { |
| 636 | case JSON_NULL: | 642 | case JSON_NULL: |
| 637 | return QCnull; | 643 | return conf->null_object; |
| 638 | case JSON_FALSE: | 644 | case JSON_FALSE: |
| 639 | return QCfalse; | 645 | return conf->false_object; |
| 640 | case JSON_TRUE: | 646 | case JSON_TRUE: |
| 641 | return Qt; | 647 | return Qt; |
| 642 | case JSON_INTEGER: | 648 | case JSON_INTEGER: |
| @@ -644,9 +650,9 @@ json_to_lisp (json_t *json, enum json_object_type object_type) | |||
| 644 | otherwise. This loses precision for integers with large | 650 | otherwise. This loses precision for integers with large |
| 645 | magnitude; however, such integers tend to be nonportable | 651 | magnitude; however, such integers tend to be nonportable |
| 646 | anyway because many JSON implementations use only 64-bit | 652 | anyway because many JSON implementations use only 64-bit |
| 647 | floating-point numbers with 53 mantissa bits. See | 653 | floating-point numbers with 53 mantissa bits. See |
| 648 | https://tools.ietf.org/html/rfc7159#section-6 for some | 654 | https://tools.ietf.org/html/rfc7159#section-6 for some |
| 649 | discussion. */ | 655 | discussion. */ |
| 650 | return make_fixnum_or_float (json_integer_value (json)); | 656 | return make_fixnum_or_float (json_integer_value (json)); |
| 651 | case JSON_REAL: | 657 | case JSON_REAL: |
| 652 | return make_float (json_real_value (json)); | 658 | return make_float (json_real_value (json)); |
| @@ -663,7 +669,7 @@ json_to_lisp (json_t *json, enum json_object_type object_type) | |||
| 663 | Lisp_Object result = Fmake_vector (make_natnum (size), Qunbound); | 669 | Lisp_Object result = Fmake_vector (make_natnum (size), Qunbound); |
| 664 | for (ptrdiff_t i = 0; i < size; ++i) | 670 | for (ptrdiff_t i = 0; i < size; ++i) |
| 665 | ASET (result, i, | 671 | ASET (result, i, |
| 666 | json_to_lisp (json_array_get (json, i), object_type)); | 672 | json_to_lisp (json_array_get (json, i), conf)); |
| 667 | --lisp_eval_depth; | 673 | --lisp_eval_depth; |
| 668 | return result; | 674 | return result; |
| 669 | } | 675 | } |
| @@ -672,7 +678,7 @@ json_to_lisp (json_t *json, enum json_object_type object_type) | |||
| 672 | if (++lisp_eval_depth > max_lisp_eval_depth) | 678 | if (++lisp_eval_depth > max_lisp_eval_depth) |
| 673 | xsignal0 (Qjson_object_too_deep); | 679 | xsignal0 (Qjson_object_too_deep); |
| 674 | Lisp_Object result; | 680 | Lisp_Object result; |
| 675 | switch (object_type) | 681 | switch (conf->object_type) |
| 676 | { | 682 | { |
| 677 | case json_object_hashtable: | 683 | case json_object_hashtable: |
| 678 | { | 684 | { |
| @@ -692,7 +698,7 @@ json_to_lisp (json_t *json, enum json_object_type object_type) | |||
| 692 | /* Keys in JSON objects are unique, so the key can't | 698 | /* Keys in JSON objects are unique, so the key can't |
| 693 | be present yet. */ | 699 | be present yet. */ |
| 694 | eassert (i < 0); | 700 | eassert (i < 0); |
| 695 | hash_put (h, key, json_to_lisp (value, object_type), hash); | 701 | hash_put (h, key, json_to_lisp (value, conf), hash); |
| 696 | } | 702 | } |
| 697 | break; | 703 | break; |
| 698 | } | 704 | } |
| @@ -705,7 +711,7 @@ json_to_lisp (json_t *json, enum json_object_type object_type) | |||
| 705 | { | 711 | { |
| 706 | Lisp_Object key = Fintern (json_build_string (key_str), Qnil); | 712 | Lisp_Object key = Fintern (json_build_string (key_str), Qnil); |
| 707 | result | 713 | result |
| 708 | = Fcons (Fcons (key, json_to_lisp (value, object_type)), | 714 | = Fcons (Fcons (key, json_to_lisp (value, conf)), |
| 709 | result); | 715 | result); |
| 710 | } | 716 | } |
| 711 | result = Fnreverse (result); | 717 | result = Fnreverse (result); |
| @@ -727,7 +733,7 @@ json_to_lisp (json_t *json, enum json_object_type object_type) | |||
| 727 | /* Build the plist as value-key since we're going to | 733 | /* Build the plist as value-key since we're going to |
| 728 | reverse it in the end.*/ | 734 | reverse it in the end.*/ |
| 729 | result = Fcons (key, result); | 735 | result = Fcons (key, result); |
| 730 | result = Fcons (json_to_lisp (value, object_type), result); | 736 | result = Fcons (json_to_lisp (value, conf), result); |
| 731 | SAFE_FREE (); | 737 | SAFE_FREE (); |
| 732 | } | 738 | } |
| 733 | result = Fnreverse (result); | 739 | result = Fnreverse (result); |
| @@ -745,47 +751,66 @@ json_to_lisp (json_t *json, enum json_object_type object_type) | |||
| 745 | emacs_abort (); | 751 | emacs_abort (); |
| 746 | } | 752 | } |
| 747 | 753 | ||
| 748 | static enum json_object_type | 754 | static void |
| 749 | json_parse_object_type (ptrdiff_t nargs, Lisp_Object *args) | 755 | json_parse_args (ptrdiff_t nargs, |
| 750 | { | 756 | Lisp_Object *args, |
| 751 | switch (nargs) | 757 | struct json_configuration *conf) |
| 752 | { | 758 | { |
| 753 | case 0: | 759 | if ((nargs % 2) != 0) |
| 754 | return json_object_hashtable; | 760 | wrong_type_argument (Qplistp, Flist (nargs, args)); |
| 755 | case 2: | 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)) | ||
| 756 | { | 768 | { |
| 757 | Lisp_Object key = args[0]; | ||
| 758 | Lisp_Object value = args[1]; | ||
| 759 | if (!EQ (key, QCobject_type)) | ||
| 760 | wrong_choice (list1 (QCobject_type), key); | ||
| 761 | if (EQ (value, Qhash_table)) | 769 | if (EQ (value, Qhash_table)) |
| 762 | return json_object_hashtable; | 770 | conf->object_type = json_object_hashtable; |
| 763 | else if (EQ (value, Qalist)) | 771 | else if (EQ (value, Qalist)) |
| 764 | return json_object_alist; | 772 | conf->object_type = json_object_alist; |
| 765 | else if (EQ (value, Qplist)) | 773 | else if (EQ (value, Qplist)) |
| 766 | return json_object_plist; | 774 | conf->object_type = json_object_plist; |
| 767 | else | 775 | else |
| 768 | wrong_choice (list3 (Qhash_table, Qalist, Qplist), value); | 776 | wrong_choice (list3 (Qhash_table, Qalist, Qplist), value); |
| 769 | } | 777 | } |
| 770 | default: | 778 | else if (EQ (key, QCnull_object)) |
| 771 | wrong_type_argument (Qplistp, Flist (nargs, args)); | 779 | conf->null_object = value; |
| 772 | } | 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 | } | ||
| 773 | } | 788 | } |
| 774 | 789 | ||
| 775 | DEFUN ("json-parse-string", Fjson_parse_string, Sjson_parse_string, 1, MANY, | 790 | DEFUN ("json-parse-string", Fjson_parse_string, Sjson_parse_string, 1, MANY, |
| 776 | NULL, | 791 | NULL, |
| 777 | doc: /* Parse the JSON STRING into a Lisp object. | 792 | doc: /* Parse the JSON STRING into a Lisp object. |
| 793 | |||
| 778 | This is essentially the reverse operation of `json-serialize', which | 794 | This is essentially the reverse operation of `json-serialize', which |
| 779 | see. The returned object will be a vector, hashtable, alist, or | 795 | see. The returned object will be a vector, hashtable, alist, or |
| 780 | plist. Its elements will be `:null', `:false', t, numbers, strings, | 796 | plist. Its elements will be the JSON null value, the JSON false |
| 781 | or further vectors, hashtables, alists, or plists. If there are | 797 | value, t, numbers, strings, or further vectors, hashtables, alists, or |
| 782 | duplicate keys in an object, all but the last one are ignored. If | 798 | plists. If there are duplicate keys in an object, all but the last |
| 783 | STRING doesn't contain a valid JSON object, an error of type | 799 | one are ignored. If STRING doesn't contain a valid JSON object, an |
| 784 | `json-parse-error' is signaled. The keyword argument `:object-type' | 800 | error of type `json-parse-error' is signaled. The arguments ARGS are |
| 785 | specifies which Lisp type is used to represent objects; it can be | 801 | a list of keyword/argument pairs: |
| 786 | `hash-table', `alist' or `plist'. | 802 | |
| 787 | usage: (json-parse-string STRING &key (OBJECT-TYPE \\='hash-table)) */) | 803 | The keyword argument `:object-type' specifies which Lisp type is used |
| 788 | (ptrdiff_t nargs, Lisp_Object *args) | 804 | to represent objects; it can be `hash-table', `alist' or `plist'. |
| 805 | |||
| 806 | The keyword argument `:null-object' specifies which object to use | ||
| 807 | to represent a JSON null value. It defaults to `:null'. | ||
| 808 | |||
| 809 | The keyword argument `:false-object' specifies which object to use to | ||
| 810 | represent a JSON false value. It defaults to `:false'. | ||
| 811 | |||
| 812 | usage: (json-parse-string STRING &rest args) */) | ||
| 813 | (ptrdiff_t nargs, Lisp_Object *args) | ||
| 789 | { | 814 | { |
| 790 | ptrdiff_t count = SPECPDL_INDEX (); | 815 | ptrdiff_t count = SPECPDL_INDEX (); |
| 791 | 816 | ||
| @@ -807,8 +832,8 @@ usage: (json-parse-string STRING &key (OBJECT-TYPE \\='hash-table)) */) | |||
| 807 | Lisp_Object string = args[0]; | 832 | Lisp_Object string = args[0]; |
| 808 | Lisp_Object encoded = json_encode (string); | 833 | Lisp_Object encoded = json_encode (string); |
| 809 | check_string_without_embedded_nulls (encoded); | 834 | check_string_without_embedded_nulls (encoded); |
| 810 | enum json_object_type object_type | 835 | struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse}; |
| 811 | = json_parse_object_type (nargs - 1, args + 1); | 836 | json_parse_args (nargs - 1, args + 1, &conf); |
| 812 | 837 | ||
| 813 | json_error_t error; | 838 | json_error_t error; |
| 814 | json_t *object = json_loads (SSDATA (encoded), 0, &error); | 839 | json_t *object = json_loads (SSDATA (encoded), 0, &error); |
| @@ -819,7 +844,7 @@ usage: (json-parse-string STRING &key (OBJECT-TYPE \\='hash-table)) */) | |||
| 819 | if (object != NULL) | 844 | if (object != NULL) |
| 820 | record_unwind_protect_ptr (json_release_object, object); | 845 | record_unwind_protect_ptr (json_release_object, object); |
| 821 | 846 | ||
| 822 | return unbind_to (count, json_to_lisp (object, object_type)); | 847 | return unbind_to (count, json_to_lisp (object, &conf)); |
| 823 | } | 848 | } |
| 824 | 849 | ||
| 825 | struct json_read_buffer_data | 850 | struct json_read_buffer_data |
| @@ -857,8 +882,8 @@ DEFUN ("json-parse-buffer", Fjson_parse_buffer, Sjson_parse_buffer, | |||
| 857 | This is similar to `json-parse-string', which see. Move point after | 882 | This is similar to `json-parse-string', which see. Move point after |
| 858 | the end of the object if parsing was successful. On error, point is | 883 | the end of the object if parsing was successful. On error, point is |
| 859 | not moved. | 884 | not moved. |
| 860 | usage: (json-parse-buffer &key (OBJECT-TYPE \\='hash-table)) */) | 885 | usage: (json-parse-buffer &rest args) */) |
| 861 | (ptrdiff_t nargs, Lisp_Object *args) | 886 | (ptrdiff_t nargs, Lisp_Object *args) |
| 862 | { | 887 | { |
| 863 | ptrdiff_t count = SPECPDL_INDEX (); | 888 | ptrdiff_t count = SPECPDL_INDEX (); |
| 864 | 889 | ||
| @@ -877,7 +902,8 @@ usage: (json-parse-buffer &key (OBJECT-TYPE \\='hash-table)) */) | |||
| 877 | } | 902 | } |
| 878 | #endif | 903 | #endif |
| 879 | 904 | ||
| 880 | enum json_object_type object_type = json_parse_object_type (nargs, args); | 905 | struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse}; |
| 906 | json_parse_args (nargs, args, &conf); | ||
| 881 | 907 | ||
| 882 | ptrdiff_t point = PT_BYTE; | 908 | ptrdiff_t point = PT_BYTE; |
| 883 | struct json_read_buffer_data data = {.point = point}; | 909 | struct json_read_buffer_data data = {.point = point}; |
| @@ -892,7 +918,7 @@ usage: (json-parse-buffer &key (OBJECT-TYPE \\='hash-table)) */) | |||
| 892 | record_unwind_protect_ptr (json_release_object, object); | 918 | record_unwind_protect_ptr (json_release_object, object); |
| 893 | 919 | ||
| 894 | /* Convert and then move point only if everything succeeded. */ | 920 | /* Convert and then move point only if everything succeeded. */ |
| 895 | Lisp_Object lisp = json_to_lisp (object, object_type); | 921 | Lisp_Object lisp = json_to_lisp (object, &conf); |
| 896 | 922 | ||
| 897 | /* Adjust point by how much we just read. */ | 923 | /* Adjust point by how much we just read. */ |
| 898 | point += error.position; | 924 | point += error.position; |
| @@ -955,6 +981,8 @@ syms_of_json (void) | |||
| 955 | Fput (Qjson_parse_string, Qside_effect_free, Qt); | 981 | Fput (Qjson_parse_string, Qside_effect_free, Qt); |
| 956 | 982 | ||
| 957 | DEFSYM (QCobject_type, ":object-type"); | 983 | DEFSYM (QCobject_type, ":object-type"); |
| 984 | DEFSYM (QCnull_object, ":null-object"); | ||
| 985 | DEFSYM (QCfalse_object, ":false-object"); | ||
| 958 | DEFSYM (Qalist, "alist"); | 986 | DEFSYM (Qalist, "alist"); |
| 959 | DEFSYM (Qplist, "plist"); | 987 | DEFSYM (Qplist, "plist"); |
| 960 | 988 | ||