diff options
| author | João Távora | 2018-05-30 02:48:31 +0100 |
|---|---|---|
| committer | João Távora | 2018-05-30 02:53:19 +0100 |
| commit | 506d38434b54a3999163a78c91fb06a524a5cad2 (patch) | |
| tree | dfd7846f020cc41c5863b2af567856107b90cc9d | |
| parent | 224f8ea95f00cc60ee77aeaad6585bb2ef845f70 (diff) | |
| download | emacs-scratch/support-plists-in-jsonc.tar.gz emacs-scratch/support-plists-in-jsonc.zip | |
Instead of a json-serialize-use-plists global, use function argsscratch/support-plists-in-jsonc
* src/json.c (syms_of_json): Don't defive
json-serialize-use-plists
(Fjson_insert, lisp_to_json, list_to_json_toplevel_1)
(list_to_json_toplevel, Fjson_serialize): Use and pass around
use_plists arg.
* doc/lispref/text.texi:
(Parsing JSON): json-serialize and json-insert now have a
USE-PLISTS arg.
* test/src/json-tests.el (json-serialize/object): Use
json-serialize's USE-PLISTS arg.
| -rw-r--r-- | doc/lispref/text.texi | 15 | ||||
| -rw-r--r-- | src/json.c | 66 | ||||
| -rw-r--r-- | test/src/json-tests.el | 21 |
3 files changed, 49 insertions, 53 deletions
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index 3995102237a..d2371d61811 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi | |||
| @@ -5067,22 +5067,19 @@ either @code{hash-table} to parse JSON objects as hashtables with | |||
| 5067 | string keys (the default), @code{alist} to parse them as alists or | 5067 | string keys (the default), @code{alist} to parse them as alists or |
| 5068 | @code{plist} to parse them as plists. | 5068 | @code{plist} to parse them as plists. |
| 5069 | 5069 | ||
| 5070 | @vindex json-serialize-use-plists | ||
| 5071 | @cindex serializing plists as json | 5070 | @cindex serializing plists as json |
| 5072 | For the serialization function, the variable | ||
| 5073 | @var{json-serialize-use-plists} controls the converse process, | ||
| 5074 | resolving the ambiguity when a list is found in the Lisp object to | ||
| 5075 | serialize. If @code{nil}, its default, the list is interpreted as an | ||
| 5076 | alist, otherwise it is interpreted as a plist. | ||
| 5077 | |||
| 5078 | @defun json-serialize object | 5071 | @defun json-serialize object |
| 5079 | This function returns a new Lisp string which contains the JSON | 5072 | This function returns a new Lisp string which contains the JSON |
| 5080 | representation of @var{object}. | 5073 | representation of @var{object}. The optional argument @var{use-plists} |
| 5074 | resolves the ambiguity when a list is found in the Lisp object to | ||
| 5075 | serialize: if it is @code{nil}, its default, the list is interpreted | ||
| 5076 | as an alist, otherwise it is interpreted as a plist. | ||
| 5081 | @end defun | 5077 | @end defun |
| 5082 | 5078 | ||
| 5083 | @defun json-insert object | 5079 | @defun json-insert object |
| 5084 | This function inserts the JSON representation of @var{object} into the | 5080 | This function inserts the JSON representation of @var{object} into the |
| 5085 | current buffer before point. | 5081 | current buffer before point. The optional argument @var{use-plists} is |
| 5082 | interpreted as in @dfn{json-serialize}. | ||
| 5086 | @end defun | 5083 | @end defun |
| 5087 | 5084 | ||
| 5088 | @defun json-parse-string string &key (object-type @code{hash-table}) | 5085 | @defun json-parse-string string &key (object-type @code{hash-table}) |
diff --git a/src/json.c b/src/json.c index ccd58c047e5..8318b637e05 100644 --- a/src/json.c +++ b/src/json.c | |||
| @@ -325,14 +325,14 @@ 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 | ||
| 328 | static json_t *lisp_to_json (Lisp_Object); | 328 | static json_t *lisp_to_json (Lisp_Object, Lisp_Object); |
| 329 | 329 | ||
| 330 | /* Convert a Lisp object to a toplevel JSON object (array or object). | 330 | /* Convert a Lisp object to a toplevel JSON object (array or object). |
| 331 | This returns Lisp_Object so we can use unbind_to. The return value | 331 | This returns Lisp_Object so we can use unbind_to. The return value |
| 332 | is always nil. */ | 332 | is always nil. */ |
| 333 | 333 | ||
| 334 | static _GL_ARG_NONNULL ((2)) Lisp_Object | 334 | static _GL_ARG_NONNULL ((2)) Lisp_Object |
| 335 | lisp_to_json_toplevel_1 (Lisp_Object lisp, json_t **json) | 335 | lisp_to_json_toplevel_1 (Lisp_Object lisp, Lisp_Object use_plists, json_t **json) |
| 336 | { | 336 | { |
| 337 | if (VECTORP (lisp)) | 337 | if (VECTORP (lisp)) |
| 338 | { | 338 | { |
| @@ -343,7 +343,8 @@ lisp_to_json_toplevel_1 (Lisp_Object lisp, json_t **json) | |||
| 343 | for (ptrdiff_t i = 0; i < size; ++i) | 343 | for (ptrdiff_t i = 0; i < size; ++i) |
| 344 | { | 344 | { |
| 345 | int status | 345 | int status |
| 346 | = json_array_append_new (*json, lisp_to_json (AREF (lisp, i))); | 346 | = json_array_append_new (*json, lisp_to_json (AREF (lisp, i), |
| 347 | use_plists)); | ||
| 347 | if (status == -1) | 348 | if (status == -1) |
| 348 | json_out_of_memory (); | 349 | json_out_of_memory (); |
| 349 | } | 350 | } |
| @@ -370,7 +371,8 @@ lisp_to_json_toplevel_1 (Lisp_Object lisp, json_t **json) | |||
| 370 | if (json_object_get (*json, key_str) != NULL) | 371 | if (json_object_get (*json, key_str) != NULL) |
| 371 | wrong_type_argument (Qjson_value_p, lisp); | 372 | wrong_type_argument (Qjson_value_p, lisp); |
| 372 | int status = json_object_set_new (*json, key_str, | 373 | int status = json_object_set_new (*json, key_str, |
| 373 | lisp_to_json (HASH_VALUE (h, i))); | 374 | lisp_to_json (HASH_VALUE (h, i), |
| 375 | use_plists)); | ||
| 374 | if (status == -1) | 376 | if (status == -1) |
| 375 | { | 377 | { |
| 376 | /* A failure can be caused either by an invalid key or | 378 | /* A failure can be caused either by an invalid key or |
| @@ -398,7 +400,7 @@ lisp_to_json_toplevel_1 (Lisp_Object lisp, json_t **json) | |||
| 398 | const char *key_str; | 400 | const char *key_str; |
| 399 | Lisp_Object value; | 401 | Lisp_Object value; |
| 400 | Lisp_Object key_symbol; | 402 | Lisp_Object key_symbol; |
| 401 | if ( EQ (Vjson_serialize_use_plists, Qt) ) { | 403 | if ( EQ (use_plists, Qt) ) { |
| 402 | key_symbol = XCAR (tail); | 404 | key_symbol = XCAR (tail); |
| 403 | tail = XCDR(tail); | 405 | tail = XCDR(tail); |
| 404 | CHECK_CONS (tail); | 406 | CHECK_CONS (tail); |
| @@ -417,14 +419,15 @@ lisp_to_json_toplevel_1 (Lisp_Object lisp, json_t **json) | |||
| 417 | check_string_without_embedded_nulls (key); | 419 | check_string_without_embedded_nulls (key); |
| 418 | key_str = SSDATA (key); | 420 | key_str = SSDATA (key); |
| 419 | /* If using plists, maybe strip the ":" from symbol-name */ | 421 | /* If using plists, maybe strip the ":" from symbol-name */ |
| 420 | if (EQ (Vjson_serialize_use_plists, Qt) && | 422 | if (EQ (use_plists, Qt) && |
| 421 | ':' == key_str[0] && | 423 | ':' == key_str[0] && |
| 422 | key_str[1] ) key_str = &key_str[1]; | 424 | key_str[1] ) key_str = &key_str[1]; |
| 423 | /* Only add element if key is not already present. */ | 425 | /* Only add element if key is not already present. */ |
| 424 | if (json_object_get (*json, key_str) == NULL) | 426 | if (json_object_get (*json, key_str) == NULL) |
| 425 | { | 427 | { |
| 426 | int status | 428 | int status |
| 427 | = json_object_set_new (*json, key_str, lisp_to_json (value)); | 429 | = json_object_set_new (*json, key_str, |
| 430 | lisp_to_json (value, use_plists)); | ||
| 428 | if (status == -1) | 431 | if (status == -1) |
| 429 | json_out_of_memory (); | 432 | json_out_of_memory (); |
| 430 | } | 433 | } |
| @@ -441,12 +444,12 @@ lisp_to_json_toplevel_1 (Lisp_Object lisp, json_t **json) | |||
| 441 | hashtable, or alist. */ | 444 | hashtable, or alist. */ |
| 442 | 445 | ||
| 443 | static json_t * | 446 | static json_t * |
| 444 | lisp_to_json_toplevel (Lisp_Object lisp) | 447 | lisp_to_json_toplevel (Lisp_Object lisp, Lisp_Object use_plists) |
| 445 | { | 448 | { |
| 446 | if (++lisp_eval_depth > max_lisp_eval_depth) | 449 | if (++lisp_eval_depth > max_lisp_eval_depth) |
| 447 | xsignal0 (Qjson_object_too_deep); | 450 | xsignal0 (Qjson_object_too_deep); |
| 448 | json_t *json; | 451 | json_t *json; |
| 449 | lisp_to_json_toplevel_1 (lisp, &json); | 452 | lisp_to_json_toplevel_1 (lisp, use_plists, &json); |
| 450 | --lisp_eval_depth; | 453 | --lisp_eval_depth; |
| 451 | return json; | 454 | return json; |
| 452 | } | 455 | } |
| @@ -456,7 +459,7 @@ lisp_to_json_toplevel (Lisp_Object lisp) | |||
| 456 | JSON object. */ | 459 | JSON object. */ |
| 457 | 460 | ||
| 458 | static json_t * | 461 | static json_t * |
| 459 | lisp_to_json (Lisp_Object lisp) | 462 | lisp_to_json (Lisp_Object lisp, Lisp_Object use_plists) |
| 460 | { | 463 | { |
| 461 | if (EQ (lisp, QCnull)) | 464 | if (EQ (lisp, QCnull)) |
| 462 | return json_check (json_null ()); | 465 | return json_check (json_null ()); |
| @@ -486,24 +489,26 @@ lisp_to_json (Lisp_Object lisp) | |||
| 486 | } | 489 | } |
| 487 | 490 | ||
| 488 | /* LISP now must be a vector, hashtable, or alist. */ | 491 | /* LISP now must be a vector, hashtable, or alist. */ |
| 489 | return lisp_to_json_toplevel (lisp); | 492 | return lisp_to_json_toplevel (lisp, use_plists); |
| 490 | } | 493 | } |
| 491 | 494 | ||
| 492 | DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, 1, NULL, | 495 | DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, 2, NULL, |
| 493 | doc: /* Return the JSON representation of OBJECT as a string. | 496 | doc: /* Return the JSON representation of OBJECT as a string. |
| 494 | 497 | ||
| 495 | OBJECT must be a vector of values or a key-value map. Hashtables, | 498 | OBJECT must be a vector of values or a key-value map. Hashtables, |
| 496 | alists and plists are accepted as maps, the variable | 499 | alists and plists are accepted as maps. Since the latter two are both |
| 497 | `json-serialize-use-plists' controlling which one of the latter two to | 500 | lists and this function can't currently guess the format from the |
| 498 | use. In any of these cases, values can be `:null', `:false', t, | 501 | variable, the optional argument USE-PLISTS is used to control which of |
| 499 | numbers, strings, or, recursively, other vectors, hashtables, alists | 502 | the two to use. In any of these cases, values can be `:null', |
| 500 | or plists. `:null', `:false', and t will be converted to JSON null, | 503 | `:false', t, numbers, strings, or, recursively, other vectors, |
| 501 | false, and true values, respectively. Vectors will be converted to | 504 | hashtables, alists or plists. `:null', `:false', and t will be |
| 502 | JSON arrays, and hashtables, alists and plists to JSON objects. | 505 | converted to JSON null, false, and true values, respectively. Vectors |
| 503 | Hashtable keys must be strings without embedded null characters and | 506 | will be converted to JSON arrays, and hashtables, alists and plists to |
| 504 | must be unique within each object. Alist or plist keys must be | 507 | JSON objects. Hashtable keys must be strings without embedded null |
| 505 | symbols; if a key is duplicate, the first instance is used. */) | 508 | characters and must be unique within each object. Alist or plist keys |
| 506 | (Lisp_Object object) | 509 | must be symbols; if a key is duplicate, the first instance is |
| 510 | used. */) | ||
| 511 | (Lisp_Object object, Lisp_Object use_plists) | ||
| 507 | { | 512 | { |
| 508 | ptrdiff_t count = SPECPDL_INDEX (); | 513 | ptrdiff_t count = SPECPDL_INDEX (); |
| 509 | 514 | ||
| @@ -522,7 +527,7 @@ symbols; if a key is duplicate, the first instance is used. */) | |||
| 522 | } | 527 | } |
| 523 | #endif | 528 | #endif |
| 524 | 529 | ||
| 525 | json_t *json = lisp_to_json_toplevel (object); | 530 | json_t *json = lisp_to_json_toplevel (object, use_plists); |
| 526 | record_unwind_protect_ptr (json_release_object, json); | 531 | record_unwind_protect_ptr (json_release_object, json); |
| 527 | 532 | ||
| 528 | /* If desired, we might want to add the following flags: | 533 | /* If desired, we might want to add the following flags: |
| @@ -578,12 +583,12 @@ json_insert_callback (const char *buffer, size_t size, void *data) | |||
| 578 | return NILP (d->error) ? 0 : -1; | 583 | return NILP (d->error) ? 0 : -1; |
| 579 | } | 584 | } |
| 580 | 585 | ||
| 581 | DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, 1, NULL, | 586 | DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, 2, NULL, |
| 582 | doc: /* Insert the JSON representation of OBJECT before point. | 587 | doc: /* Insert the JSON representation of OBJECT before point. |
| 583 | This is the same as (insert (json-serialize OBJECT)), but potentially | 588 | This is the same as (insert (json-serialize OBJECT)), but potentially |
| 584 | faster. See the function `json-serialize' for allowed values of | 589 | faster. See the function `json-serialize' for allowed values of |
| 585 | OBJECT. */) | 590 | OBJECT and the meaning of USE-PLISTS */) |
| 586 | (Lisp_Object object) | 591 | (Lisp_Object object, Lisp_Object use_plists) |
| 587 | { | 592 | { |
| 588 | ptrdiff_t count = SPECPDL_INDEX (); | 593 | ptrdiff_t count = SPECPDL_INDEX (); |
| 589 | 594 | ||
| @@ -602,7 +607,7 @@ OBJECT. */) | |||
| 602 | } | 607 | } |
| 603 | #endif | 608 | #endif |
| 604 | 609 | ||
| 605 | json_t *json = lisp_to_json (object); | 610 | json_t *json = lisp_to_json (object, use_plists); |
| 606 | record_unwind_protect_ptr (json_release_object, json); | 611 | record_unwind_protect_ptr (json_release_object, json); |
| 607 | 612 | ||
| 608 | struct json_insert_data data; | 613 | struct json_insert_data data; |
| @@ -950,11 +955,6 @@ syms_of_json (void) | |||
| 950 | DEFSYM (Qpure, "pure"); | 955 | DEFSYM (Qpure, "pure"); |
| 951 | DEFSYM (Qside_effect_free, "side-effect-free"); | 956 | DEFSYM (Qside_effect_free, "side-effect-free"); |
| 952 | 957 | ||
| 953 | DEFVAR_LISP ("json-serialize-use-plists", Vjson_serialize_use_plists, | ||
| 954 | doc: | ||
| 955 | /* If non-nil use plists instead of alists in json-serialize.*/); | ||
| 956 | Vjson_serialize_use_plists = Qnil; | ||
| 957 | |||
| 958 | DEFSYM (Qjson_serialize, "json-serialize"); | 958 | DEFSYM (Qjson_serialize, "json-serialize"); |
| 959 | DEFSYM (Qjson_parse_string, "json-parse-string"); | 959 | DEFSYM (Qjson_parse_string, "json-parse-string"); |
| 960 | Fput (Qjson_serialize, Qpure, Qt); | 960 | Fput (Qjson_serialize, Qpure, Qt); |
diff --git a/test/src/json-tests.el b/test/src/json-tests.el index 5c9be20e957..b7c17a4cf10 100644 --- a/test/src/json-tests.el +++ b/test/src/json-tests.el | |||
| @@ -71,17 +71,16 @@ | |||
| 71 | (should-error (json-serialize '#1=((a . 1) . #1#)) :type 'circular-list) | 71 | (should-error (json-serialize '#1=((a . 1) . #1#)) :type 'circular-list) |
| 72 | (should-error (json-serialize '(#1=(a #1#)))) | 72 | (should-error (json-serialize '(#1=(a #1#)))) |
| 73 | 73 | ||
| 74 | (let ((json-serialize-use-plists t)) | 74 | (should (equal (json-serialize '(:abc [1 2 t] :def :null) t) |
| 75 | (should (equal (json-serialize '(:abc [1 2 t] :def :null)) | 75 | "{\"abc\":[1,2,true],\"def\":null}")) |
| 76 | "{\"abc\":[1,2,true],\"def\":null}")) | 76 | (should (equal (json-serialize '(abc [1 2 t] :def :null) t) |
| 77 | (should (equal (json-serialize '(abc [1 2 t] :def :null)) | 77 | "{\"abc\":[1,2,true],\"def\":null}")) |
| 78 | "{\"abc\":[1,2,true],\"def\":null}")) | 78 | (should-error (json-serialize '#1=(:a 1 . #1#) t) :type 'circular-list) |
| 79 | (should-error (json-serialize '#1=(:a 1 . #1#)) :type 'circular-list) | 79 | (should-error (json-serialize '((abc . 1)) t) :type 'wrong-type-argument) |
| 80 | (should-error (json-serialize '((abc . 1))) :type 'wrong-type-argument) | 80 | (should-error (json-serialize '(:foo bar (abc . 1)) t) |
| 81 | (should-error (json-serialize '(:foo bar (abc . 1))) | 81 | :type 'wrong-type-argument) |
| 82 | :type 'wrong-type-argument) | 82 | (should-error (json-serialize '(:foo bar :odd-numbered) t) |
| 83 | (should-error (json-serialize '(:foo bar :odd-numbered)) | 83 | :type 'wrong-type-argument)) |
| 84 | :type 'wrong-type-argument))) | ||
| 85 | 84 | ||
| 86 | (ert-deftest json-serialize/object-with-duplicate-keys () | 85 | (ert-deftest json-serialize/object-with-duplicate-keys () |
| 87 | (skip-unless (fboundp 'json-serialize)) | 86 | (skip-unless (fboundp 'json-serialize)) |