diff options
| author | Dmitry Gutov | 2019-04-13 01:33:05 +0300 |
|---|---|---|
| committer | Dmitry Gutov | 2019-04-13 01:33:42 +0300 |
| commit | b41c1ca10fab4ed94e59aea8ad5eae334c2452bd (patch) | |
| tree | 19e3521522c0924e696bbbae80e90fc466c5a61e | |
| parent | cc80eeb4a43d2079963de3d181002a6a6b56560d (diff) | |
| download | emacs-b41c1ca10fab4ed94e59aea8ad5eae334c2452bd.tar.gz emacs-b41c1ca10fab4ed94e59aea8ad5eae334c2452bd.zip | |
Add :array-type option to json-parse-string
* src/json.c (enum json_array_type): New type.
(struct json_configuration): New field array_type.
(json_parse_args): Rename the last argument. Handle the
:array-type keyword argument (bug#32793).
(Fjson_parse_string): Update the docstring accordingly.
(json_to_lisp): Handle the case of :array-type being `list'. Add
a call to 'rarely_quit' inside the loop.
(syms_of_json): Define new symbols.
(Fjson_serialize, Fjson_insert, Fjson_parse_string)
(Fjson_parse_buffer): Update the config struct initializers.
| -rw-r--r-- | doc/lispref/text.texi | 5 | ||||
| -rw-r--r-- | src/json.c | 78 | ||||
| -rw-r--r-- | test/src/json-tests.el | 8 |
3 files changed, 78 insertions, 13 deletions
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index 1ef836b8f94..b46ee647862 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi | |||
| @@ -5167,6 +5167,11 @@ key-value mappings of a JSON object. It can be either | |||
| 5167 | keys; @code{alist} to use alists with symbols as keys; or @code{plist} | 5167 | keys; @code{alist} to use alists with symbols as keys; or @code{plist} |
| 5168 | to use plists with keyword symbols as keys. | 5168 | to use plists with keyword symbols as keys. |
| 5169 | 5169 | ||
| 5170 | @item :array-type | ||
| 5171 | The value decides which Lisp object to use for representing a JSON | ||
| 5172 | array. It can be either @code{array}, the default, to use Lisp | ||
| 5173 | arrays; or @code{list} to use lists. | ||
| 5174 | |||
| 5170 | @item :null-object | 5175 | @item :null-object |
| 5171 | The value decides which Lisp object to use to represent the JSON | 5176 | The value decides which Lisp object to use to represent the JSON |
| 5172 | keyword @code{null}. It defaults to the symbol @code{:null}. | 5177 | keyword @code{null}. It defaults to the symbol @code{:null}. |
diff --git a/src/json.c b/src/json.c index 5e1439f881a..eb323b498c7 100644 --- a/src/json.c +++ b/src/json.c | |||
| @@ -337,8 +337,14 @@ enum json_object_type { | |||
| 337 | json_object_plist | 337 | json_object_plist |
| 338 | }; | 338 | }; |
| 339 | 339 | ||
| 340 | enum json_array_type { | ||
| 341 | json_array_array, | ||
| 342 | json_array_list | ||
| 343 | }; | ||
| 344 | |||
| 340 | struct json_configuration { | 345 | struct json_configuration { |
| 341 | enum json_object_type object_type; | 346 | enum json_object_type object_type; |
| 347 | enum json_array_type array_type; | ||
| 342 | Lisp_Object null_object; | 348 | Lisp_Object null_object; |
| 343 | Lisp_Object false_object; | 349 | Lisp_Object false_object; |
| 344 | }; | 350 | }; |
| @@ -521,7 +527,7 @@ static void | |||
| 521 | json_parse_args (ptrdiff_t nargs, | 527 | json_parse_args (ptrdiff_t nargs, |
| 522 | Lisp_Object *args, | 528 | Lisp_Object *args, |
| 523 | struct json_configuration *conf, | 529 | struct json_configuration *conf, |
| 524 | bool configure_object_type) | 530 | bool parse_object_types) |
| 525 | { | 531 | { |
| 526 | if ((nargs % 2) != 0) | 532 | if ((nargs % 2) != 0) |
| 527 | wrong_type_argument (Qplistp, Flist (nargs, args)); | 533 | wrong_type_argument (Qplistp, Flist (nargs, args)); |
| @@ -531,7 +537,7 @@ json_parse_args (ptrdiff_t nargs, | |||
| 531 | for (ptrdiff_t i = nargs; i > 0; i -= 2) { | 537 | for (ptrdiff_t i = nargs; i > 0; i -= 2) { |
| 532 | Lisp_Object key = args[i - 2]; | 538 | Lisp_Object key = args[i - 2]; |
| 533 | Lisp_Object value = args[i - 1]; | 539 | Lisp_Object value = args[i - 1]; |
| 534 | if (configure_object_type && EQ (key, QCobject_type)) | 540 | if (parse_object_types && EQ (key, QCobject_type)) |
| 535 | { | 541 | { |
| 536 | if (EQ (value, Qhash_table)) | 542 | if (EQ (value, Qhash_table)) |
| 537 | conf->object_type = json_object_hashtable; | 543 | conf->object_type = json_object_hashtable; |
| @@ -542,12 +548,22 @@ json_parse_args (ptrdiff_t nargs, | |||
| 542 | else | 548 | else |
| 543 | wrong_choice (list3 (Qhash_table, Qalist, Qplist), value); | 549 | wrong_choice (list3 (Qhash_table, Qalist, Qplist), value); |
| 544 | } | 550 | } |
| 551 | else if (parse_object_types && EQ (key, QCarray_type)) | ||
| 552 | { | ||
| 553 | if (EQ (value, Qarray)) | ||
| 554 | conf->array_type = json_array_array; | ||
| 555 | else if (EQ (value, Qlist)) | ||
| 556 | conf->array_type = json_array_list; | ||
| 557 | else | ||
| 558 | wrong_choice (list2 (Qarray, Qlist), value); | ||
| 559 | } | ||
| 545 | else if (EQ (key, QCnull_object)) | 560 | else if (EQ (key, QCnull_object)) |
| 546 | conf->null_object = value; | 561 | conf->null_object = value; |
| 547 | else if (EQ (key, QCfalse_object)) | 562 | else if (EQ (key, QCfalse_object)) |
| 548 | conf->false_object = value; | 563 | conf->false_object = value; |
| 549 | else if (configure_object_type) | 564 | else if (parse_object_types) |
| 550 | wrong_choice (list3 (QCobject_type, | 565 | wrong_choice (list4 (QCobject_type, |
| 566 | QCarray_type, | ||
| 551 | QCnull_object, | 567 | QCnull_object, |
| 552 | QCfalse_object), | 568 | QCfalse_object), |
| 553 | value); | 569 | value); |
| @@ -604,7 +620,8 @@ usage: (json-serialize OBJECT &rest ARGS) */) | |||
| 604 | } | 620 | } |
| 605 | #endif | 621 | #endif |
| 606 | 622 | ||
| 607 | struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse}; | 623 | struct json_configuration conf = |
| 624 | {json_object_hashtable, json_array_array, QCnull, QCfalse}; | ||
| 608 | json_parse_args (nargs - 1, args + 1, &conf, false); | 625 | json_parse_args (nargs - 1, args + 1, &conf, false); |
| 609 | 626 | ||
| 610 | json_t *json = lisp_to_json_toplevel (args[0], &conf); | 627 | json_t *json = lisp_to_json_toplevel (args[0], &conf); |
| @@ -701,7 +718,8 @@ usage: (json-insert OBJECT &rest ARGS) */) | |||
| 701 | } | 718 | } |
| 702 | #endif | 719 | #endif |
| 703 | 720 | ||
| 704 | struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse}; | 721 | struct json_configuration conf = |
| 722 | {json_object_hashtable, json_array_array, QCnull, QCfalse}; | ||
| 705 | json_parse_args (nargs - 1, args + 1, &conf, false); | 723 | json_parse_args (nargs - 1, args + 1, &conf, false); |
| 706 | 724 | ||
| 707 | json_t *json = lisp_to_json (args[0], &conf); | 725 | json_t *json = lisp_to_json (args[0], &conf); |
| @@ -817,10 +835,35 @@ json_to_lisp (json_t *json, struct json_configuration *conf) | |||
| 817 | size_t size = json_array_size (json); | 835 | size_t size = json_array_size (json); |
| 818 | if (PTRDIFF_MAX < size) | 836 | if (PTRDIFF_MAX < size) |
| 819 | overflow_error (); | 837 | overflow_error (); |
| 820 | Lisp_Object result = make_vector (size, Qunbound); | 838 | Lisp_Object result; |
| 821 | for (ptrdiff_t i = 0; i < size; ++i) | 839 | switch (conf->array_type) |
| 822 | ASET (result, i, | 840 | { |
| 823 | json_to_lisp (json_array_get (json, i), conf)); | 841 | case json_array_array: |
| 842 | { | ||
| 843 | result = make_vector (size, Qunbound); | ||
| 844 | for (ptrdiff_t i = 0; i < size; ++i) | ||
| 845 | { | ||
| 846 | rarely_quit (i); | ||
| 847 | ASET (result, i, | ||
| 848 | json_to_lisp (json_array_get (json, i), conf)); | ||
| 849 | } | ||
| 850 | break; | ||
| 851 | } | ||
| 852 | case json_array_list: | ||
| 853 | { | ||
| 854 | result = Qnil; | ||
| 855 | for (ptrdiff_t i = size - 1; i >= 0; --i) | ||
| 856 | { | ||
| 857 | rarely_quit (i); | ||
| 858 | result = Fcons (json_to_lisp (json_array_get (json, i), conf), | ||
| 859 | result); | ||
| 860 | } | ||
| 861 | break; | ||
| 862 | } | ||
| 863 | default: | ||
| 864 | /* Can't get here. */ | ||
| 865 | emacs_abort (); | ||
| 866 | } | ||
| 824 | --lisp_eval_depth; | 867 | --lisp_eval_depth; |
| 825 | return result; | 868 | return result; |
| 826 | } | 869 | } |
| @@ -916,7 +959,12 @@ error of type `json-parse-error' is signaled. The arguments ARGS are | |||
| 916 | a list of keyword/argument pairs: | 959 | a list of keyword/argument pairs: |
| 917 | 960 | ||
| 918 | The keyword argument `:object-type' specifies which Lisp type is used | 961 | The keyword argument `:object-type' specifies which Lisp type is used |
| 919 | to represent objects; it can be `hash-table', `alist' or `plist'. | 962 | to represent objects; it can be `hash-table', `alist' or `plist'. It |
| 963 | defaults to `hash-table'. | ||
| 964 | |||
| 965 | The keyword argument `:array-type' specifies which Lisp type is used | ||
| 966 | to represent arrays; it can be `array' or `list'. It defaults to | ||
| 967 | `array'. | ||
| 920 | 968 | ||
| 921 | The keyword argument `:null-object' specifies which object to use | 969 | The keyword argument `:null-object' specifies which object to use |
| 922 | to represent a JSON null value. It defaults to `:null'. | 970 | to represent a JSON null value. It defaults to `:null'. |
| @@ -946,7 +994,8 @@ usage: (json-parse-string STRING &rest ARGS) */) | |||
| 946 | Lisp_Object string = args[0]; | 994 | Lisp_Object string = args[0]; |
| 947 | Lisp_Object encoded = json_encode (string); | 995 | Lisp_Object encoded = json_encode (string); |
| 948 | check_string_without_embedded_nuls (encoded); | 996 | check_string_without_embedded_nuls (encoded); |
| 949 | struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse}; | 997 | struct json_configuration conf = |
| 998 | {json_object_hashtable, json_array_array, QCnull, QCfalse}; | ||
| 950 | json_parse_args (nargs - 1, args + 1, &conf, true); | 999 | json_parse_args (nargs - 1, args + 1, &conf, true); |
| 951 | 1000 | ||
| 952 | json_error_t error; | 1001 | json_error_t error; |
| @@ -1016,7 +1065,8 @@ usage: (json-parse-buffer &rest args) */) | |||
| 1016 | } | 1065 | } |
| 1017 | #endif | 1066 | #endif |
| 1018 | 1067 | ||
| 1019 | struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse}; | 1068 | struct json_configuration conf = |
| 1069 | {json_object_hashtable, json_array_array, QCnull, QCfalse}; | ||
| 1020 | json_parse_args (nargs, args, &conf, true); | 1070 | json_parse_args (nargs, args, &conf, true); |
| 1021 | 1071 | ||
| 1022 | ptrdiff_t point = PT_BYTE; | 1072 | ptrdiff_t point = PT_BYTE; |
| @@ -1095,10 +1145,12 @@ syms_of_json (void) | |||
| 1095 | Fput (Qjson_parse_string, Qside_effect_free, Qt); | 1145 | Fput (Qjson_parse_string, Qside_effect_free, Qt); |
| 1096 | 1146 | ||
| 1097 | DEFSYM (QCobject_type, ":object-type"); | 1147 | DEFSYM (QCobject_type, ":object-type"); |
| 1148 | DEFSYM (QCarray_type, ":array-type"); | ||
| 1098 | DEFSYM (QCnull_object, ":null-object"); | 1149 | DEFSYM (QCnull_object, ":null-object"); |
| 1099 | DEFSYM (QCfalse_object, ":false-object"); | 1150 | DEFSYM (QCfalse_object, ":false-object"); |
| 1100 | DEFSYM (Qalist, "alist"); | 1151 | DEFSYM (Qalist, "alist"); |
| 1101 | DEFSYM (Qplist, "plist"); | 1152 | DEFSYM (Qplist, "plist"); |
| 1153 | DEFSYM (Qarray, "array"); | ||
| 1102 | 1154 | ||
| 1103 | defsubr (&Sjson_serialize); | 1155 | defsubr (&Sjson_serialize); |
| 1104 | defsubr (&Sjson_insert); | 1156 | defsubr (&Sjson_insert); |
diff --git a/test/src/json-tests.el b/test/src/json-tests.el index 04f91f4abbc..542eec11bf3 100644 --- a/test/src/json-tests.el +++ b/test/src/json-tests.el | |||
| @@ -117,6 +117,14 @@ | |||
| 117 | (should (equal (json-parse-string input :object-type 'plist) | 117 | (should (equal (json-parse-string input :object-type 'plist) |
| 118 | '(:abc [9 :false] :def :null))))) | 118 | '(:abc [9 :false] :def :null))))) |
| 119 | 119 | ||
| 120 | (ert-deftest json-parse-string/array () | ||
| 121 | (skip-unless (fboundp 'json-parse-string)) | ||
| 122 | (let ((input "[\"a\", 1, [\"b\", 2]]")) | ||
| 123 | (should (equal (json-parse-string input) | ||
| 124 | ["a" 1 ["b" 2]])) | ||
| 125 | (should (equal (json-parse-string input :array-type 'list) | ||
| 126 | '("a" 1 ("b" 2)))))) | ||
| 127 | |||
| 120 | (ert-deftest json-parse-string/string () | 128 | (ert-deftest json-parse-string/string () |
| 121 | (skip-unless (fboundp 'json-parse-string)) | 129 | (skip-unless (fboundp 'json-parse-string)) |
| 122 | (should-error (json-parse-string "[\"formfeed\f\"]") :type 'json-parse-error) | 130 | (should-error (json-parse-string "[\"formfeed\f\"]") :type 'json-parse-error) |