diff options
| author | Mattias Engdegård | 2024-09-08 20:02:34 +0200 |
|---|---|---|
| committer | Mattias Engdegård | 2024-09-08 20:02:34 +0200 |
| commit | e55e2e1c6baebbd105f930fa553ec65c74a3000d (patch) | |
| tree | c171c29f7bcfb8a4a52e74e641506aaf9a2b960d | |
| parent | 89c99891b2b3ab087cd7e824cef391ef26800ab4 (diff) | |
| download | emacs-e55e2e1c6baebbd105f930fa553ec65c74a3000d.tar.gz emacs-e55e2e1c6baebbd105f930fa553ec65c74a3000d.zip | |
Make json-serialize always return a unibyte string (bug#70007)
The JSON format is defined as a byte sequence and will always be used as
such, so returning a multibyte string makes little sense.
* src/json.c (json_out_to_string): Remove.
(Fjson_serialize): Return unibyte string.
* test/src/json-tests.el (json-serialize/roundtrip)
(json-serialize/roundtrip-scalars, json-serialize/string):
Update tests.
* doc/lispref/text.texi (Parsing JSON): Document.
* etc/NEWS: Announce.
| -rw-r--r-- | doc/lispref/text.texi | 2 | ||||
| -rw-r--r-- | etc/NEWS | 7 | ||||
| -rw-r--r-- | src/json.c | 14 | ||||
| -rw-r--r-- | test/src/json-tests.el | 50 |
4 files changed, 36 insertions, 37 deletions
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index 278b53d7f65..df56433fd18 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi | |||
| @@ -5844,7 +5844,7 @@ can be serialized to JSON@. Likewise, the parsing functions will | |||
| 5844 | return any of the possible types described above. | 5844 | return any of the possible types described above. |
| 5845 | 5845 | ||
| 5846 | @defun json-serialize object &rest args | 5846 | @defun json-serialize object &rest args |
| 5847 | This function returns a new Lisp string which contains the JSON | 5847 | This function returns a new Lisp unibyte string which contains the JSON |
| 5848 | representation of @var{object}. The argument @var{args} is a list of | 5848 | representation of @var{object}. The argument @var{args} is a list of |
| 5849 | keyword/argument pairs. The following keywords are accepted: | 5849 | keyword/argument pairs. The following keywords are accepted: |
| 5850 | 5850 | ||
| @@ -3071,6 +3071,13 @@ entire SQL file. | |||
| 3071 | 3071 | ||
| 3072 | ** JSON | 3072 | ** JSON |
| 3073 | 3073 | ||
| 3074 | +++ | ||
| 3075 | *** 'json-serialize' now always returns a unibyte string. | ||
| 3076 | This is appropriate since it is an encoding operation. In the unlikely | ||
| 3077 | event that a multibyte string is needed, the result can be decoded using | ||
| 3078 | |||
| 3079 | (decode-coding-string RESULT 'utf-8) | ||
| 3080 | |||
| 3074 | --- | 3081 | --- |
| 3075 | *** The parser keeps duplicated object keys in alist and plist output. | 3082 | *** The parser keeps duplicated object keys in alist and plist output. |
| 3076 | A JSON object such as '{"a":1,"a":2}' will now be translated into the | 3083 | A JSON object such as '{"a":1,"a":2}' will now be translated into the |
diff --git a/src/json.c b/src/json.c index 21066d21328..41566f8369b 100644 --- a/src/json.c +++ b/src/json.c | |||
| @@ -559,16 +559,6 @@ json_out_something (json_out_t *jo, Lisp_Object obj) | |||
| 559 | wrong_type_argument (Qjson_value_p, obj); | 559 | wrong_type_argument (Qjson_value_p, obj); |
| 560 | } | 560 | } |
| 561 | 561 | ||
| 562 | static Lisp_Object | ||
| 563 | json_out_to_string (json_out_t *jo) | ||
| 564 | { | ||
| 565 | /* FIXME: should this be a unibyte or multibyte string? | ||
| 566 | Right now we make a multibyte string for test compatibility, | ||
| 567 | but we are really encoding so unibyte would make more sense. */ | ||
| 568 | ptrdiff_t nchars = jo->size - jo->chars_delta; | ||
| 569 | return make_multibyte_string (jo->buf, nchars, jo->size); | ||
| 570 | } | ||
| 571 | |||
| 572 | static void | 562 | static void |
| 573 | json_serialize (json_out_t *jo, Lisp_Object object, | 563 | json_serialize (json_out_t *jo, Lisp_Object object, |
| 574 | ptrdiff_t nargs, Lisp_Object *args) | 564 | ptrdiff_t nargs, Lisp_Object *args) |
| @@ -596,7 +586,7 @@ json_serialize (json_out_t *jo, Lisp_Object object, | |||
| 596 | 586 | ||
| 597 | DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, MANY, | 587 | DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, MANY, |
| 598 | NULL, | 588 | NULL, |
| 599 | doc: /* Return the JSON representation of OBJECT as a string. | 589 | doc: /* Return the JSON representation of OBJECT as a unibyte string. |
| 600 | 590 | ||
| 601 | OBJECT is translated as follows: | 591 | OBJECT is translated as follows: |
| 602 | 592 | ||
| @@ -629,7 +619,7 @@ usage: (json-serialize OBJECT &rest ARGS) */) | |||
| 629 | specpdl_ref count = SPECPDL_INDEX (); | 619 | specpdl_ref count = SPECPDL_INDEX (); |
| 630 | json_out_t jo; | 620 | json_out_t jo; |
| 631 | json_serialize (&jo, args[0], nargs - 1, args + 1); | 621 | json_serialize (&jo, args[0], nargs - 1, args + 1); |
| 632 | return unbind_to (count, json_out_to_string (&jo)); | 622 | return unbind_to (count, make_unibyte_string (jo.buf, jo.size)); |
| 633 | } | 623 | } |
| 634 | 624 | ||
| 635 | DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, MANY, | 625 | DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, MANY, |
diff --git a/test/src/json-tests.el b/test/src/json-tests.el index ebac70fb1c7..1d7491a4593 100644 --- a/test/src/json-tests.el +++ b/test/src/json-tests.el | |||
| @@ -36,7 +36,7 @@ | |||
| 36 | (json | 36 | (json |
| 37 | "[null,false,true,0,123,-456,3.75,\"abc\uFFFFαβγ𝔸𝐁𝖢\\\"\\\\\"]") | 37 | "[null,false,true,0,123,-456,3.75,\"abc\uFFFFαβγ𝔸𝐁𝖢\\\"\\\\\"]") |
| 38 | (json-bytes (encode-coding-string json 'utf-8))) | 38 | (json-bytes (encode-coding-string json 'utf-8))) |
| 39 | (should (equal (json-serialize lisp) json)) ; or `json-bytes'? | 39 | (should (equal (json-serialize lisp) json-bytes)) |
| 40 | (with-temp-buffer | 40 | (with-temp-buffer |
| 41 | ;; multibyte buffer | 41 | ;; multibyte buffer |
| 42 | (json-insert lisp) | 42 | (json-insert lisp) |
| @@ -82,28 +82,29 @@ | |||
| 82 | "\"abc\uFFFFαβγ𝔸𝐁𝖢\\\"\\\\\""))) | 82 | "\"abc\uFFFFαβγ𝔸𝐁𝖢\\\"\\\\\""))) |
| 83 | (cl-destructuring-bind (lisp json) case | 83 | (cl-destructuring-bind (lisp json) case |
| 84 | (ert-info ((format "%S ↔ %S" lisp json)) | 84 | (ert-info ((format "%S ↔ %S" lisp json)) |
| 85 | (should (equal (json-serialize lisp) json)) | 85 | (let ((json-bytes (encode-coding-string json 'utf-8))) |
| 86 | (with-temp-buffer | 86 | (should (equal (json-serialize lisp) json-bytes)) |
| 87 | (json-insert lisp) | 87 | (with-temp-buffer |
| 88 | (should (equal (buffer-string) json)) | 88 | (json-insert lisp) |
| 89 | (should (eobp))) | 89 | (should (equal (buffer-string) json)) |
| 90 | (with-temp-buffer | 90 | (should (eobp))) |
| 91 | (set-buffer-multibyte nil) | 91 | (with-temp-buffer |
| 92 | (json-insert lisp) | 92 | (set-buffer-multibyte nil) |
| 93 | (should (equal (buffer-string) (encode-coding-string json 'utf-8))) | 93 | (json-insert lisp) |
| 94 | (should (eobp))) | 94 | (should (equal (buffer-string) (encode-coding-string json 'utf-8))) |
| 95 | (should (equal (json-parse-string json) lisp)) | 95 | (should (eobp))) |
| 96 | (with-temp-buffer | 96 | (should (equal (json-parse-string json) lisp)) |
| 97 | (insert json) | 97 | (with-temp-buffer |
| 98 | (goto-char 1) | 98 | (insert json) |
| 99 | (should (equal (json-parse-buffer) lisp)) | 99 | (goto-char 1) |
| 100 | (should (eobp))) | 100 | (should (equal (json-parse-buffer) lisp)) |
| 101 | (with-temp-buffer | 101 | (should (eobp))) |
| 102 | (set-buffer-multibyte nil) | 102 | (with-temp-buffer |
| 103 | (insert (encode-coding-string json 'utf-8)) | 103 | (set-buffer-multibyte nil) |
| 104 | (goto-char 1) | 104 | (insert (encode-coding-string json 'utf-8)) |
| 105 | (should (equal (json-parse-buffer) lisp)) | 105 | (goto-char 1) |
| 106 | (should (eobp))))))) | 106 | (should (equal (json-parse-buffer) lisp)) |
| 107 | (should (eobp)))))))) | ||
| 107 | 108 | ||
| 108 | (ert-deftest json-serialize/object () | 109 | (ert-deftest json-serialize/object () |
| 109 | (let ((table (make-hash-table :test #'equal))) | 110 | (let ((table (make-hash-table :test #'equal))) |
| @@ -226,7 +227,8 @@ | |||
| 226 | (should (equal (json-serialize ["foo"]) "[\"foo\"]")) | 227 | (should (equal (json-serialize ["foo"]) "[\"foo\"]")) |
| 227 | (should (equal (json-serialize ["a\n\fb"]) "[\"a\\n\\fb\"]")) | 228 | (should (equal (json-serialize ["a\n\fb"]) "[\"a\\n\\fb\"]")) |
| 228 | (should (equal (json-serialize ["\nasdфыв\u001f\u007ffgh\t"]) | 229 | (should (equal (json-serialize ["\nasdфыв\u001f\u007ffgh\t"]) |
| 229 | "[\"\\nasdфыв\\u001F\u007ffgh\\t\"]")) | 230 | (encode-coding-string "[\"\\nasdфыв\\u001F\u007ffgh\\t\"]" |
| 231 | 'utf-8))) | ||
| 230 | (should (equal (json-serialize ["a\0b"]) "[\"a\\u0000b\"]")) | 232 | (should (equal (json-serialize ["a\0b"]) "[\"a\\u0000b\"]")) |
| 231 | (should-error (json-serialize ["\xC3\x84"])) | 233 | (should-error (json-serialize ["\xC3\x84"])) |
| 232 | (should-error (json-serialize ["\u00C4\xC3\x84"]))) | 234 | (should-error (json-serialize ["\u00C4\xC3\x84"]))) |