aboutsummaryrefslogtreecommitdiffstats
path: root/src/json.c
diff options
context:
space:
mode:
authorJoão Távora2018-06-02 00:23:38 +0100
committerJoão Távora2018-06-07 12:30:39 +0100
commit3509aaaefe1996ea46b038850629b6d2f7a726fe (patch)
tree651fec98d47d8c77863f6f0b08c9b3c9c6ac2362 /src/json.c
parent2e2f61efa66b69fbd12c83bbd5370a4be2374f66 (diff)
downloademacs-3509aaaefe1996ea46b038850629b6d2f7a726fe.tar.gz
emacs-3509aaaefe1996ea46b038850629b6d2f7a726fe.zip
Accept plists when serializing and parsing JSON
* doc/lispref/text.texi (Parsing JSON): Mention plist support. * src/json.c (lisp_to_json_toplevel_1): Serialize plists to json. (Fjson_serialize): Mention plists in docstring. (enum json_object_type): Add json_object_plist. (json_to_lisp): Parse JSON into plists. (json_parse_object_type): Consider plists. (Fjson_parse_string): Mention plists in docstring. (syms_of_json): New Qplist sym_of_json. (lisp_to_json): Update comment. * test/src/json-tests.el (json-serialize/object) (json-parse-string/object): New plist tests.
Diffstat (limited to 'src/json.c')
-rw-r--r--src/json.c97
1 files changed, 73 insertions, 24 deletions
diff --git a/src/json.c b/src/json.c
index b046d34f667..afb81587a47 100644
--- a/src/json.c
+++ b/src/json.c
@@ -393,18 +393,39 @@ lisp_to_json_toplevel_1 (Lisp_Object lisp, json_t **json)
393 *json = json_check (json_object ()); 393 *json = json_check (json_object ());
394 ptrdiff_t count = SPECPDL_INDEX (); 394 ptrdiff_t count = SPECPDL_INDEX ();
395 record_unwind_protect_ptr (json_release_object, *json); 395 record_unwind_protect_ptr (json_release_object, *json);
396 bool is_plist = !CONSP (XCAR (tail));
396 FOR_EACH_TAIL (tail) 397 FOR_EACH_TAIL (tail)
397 { 398 {
398 Lisp_Object pair = XCAR (tail); 399 const char *key_str;
399 CHECK_CONS (pair); 400 Lisp_Object value;
400 Lisp_Object key_symbol = XCAR (pair); 401 Lisp_Object key_symbol;
401 Lisp_Object value = XCDR (pair); 402 if (is_plist)
403 {
404 key_symbol = XCAR (tail);
405 tail = XCDR (tail);
406 CHECK_CONS (tail);
407 value = XCAR (tail);
408 if (EQ (tail, li.tortoise)) circular_list (lisp);
409 }
410 else
411 {
412 Lisp_Object pair = XCAR (tail);
413 CHECK_CONS (pair);
414 key_symbol = XCAR (pair);
415 value = XCDR (pair);
416 }
402 CHECK_SYMBOL (key_symbol); 417 CHECK_SYMBOL (key_symbol);
403 Lisp_Object key = SYMBOL_NAME (key_symbol); 418 Lisp_Object key = SYMBOL_NAME (key_symbol);
404 /* We can't specify the length, so the string must be 419 /* We can't specify the length, so the string must be
405 null-terminated. */ 420 null-terminated. */
406 check_string_without_embedded_nulls (key); 421 check_string_without_embedded_nulls (key);
407 const char *key_str = SSDATA (key); 422 key_str = SSDATA (key);
423 /* In plists, ensure leading ":" in keys is stripped. It
424 will be reconstructed later in `json_to_lisp'.*/
425 if (is_plist && ':' == key_str[0] && key_str[1])
426 {
427 key_str = &key_str[1];
428 }
408 /* Only add element if key is not already present. */ 429 /* Only add element if key is not already present. */
409 if (json_object_get (*json, key_str) == NULL) 430 if (json_object_get (*json, key_str) == NULL)
410 { 431 {
@@ -423,7 +444,7 @@ lisp_to_json_toplevel_1 (Lisp_Object lisp, json_t **json)
423 444
424/* Convert LISP to a toplevel JSON object (array or object). Signal 445/* Convert LISP to a toplevel JSON object (array or object). Signal
425 an error of type `wrong-type-argument' if LISP is not a vector, 446 an error of type `wrong-type-argument' if LISP is not a vector,
426 hashtable, or alist. */ 447 hashtable, alist, or plist. */
427 448
428static json_t * 449static json_t *
429lisp_to_json_toplevel (Lisp_Object lisp) 450lisp_to_json_toplevel (Lisp_Object lisp)
@@ -470,20 +491,21 @@ lisp_to_json (Lisp_Object lisp)
470 return json; 491 return json;
471 } 492 }
472 493
473 /* LISP now must be a vector, hashtable, or alist. */ 494 /* LISP now must be a vector, hashtable, alist, or plist. */
474 return lisp_to_json_toplevel (lisp); 495 return lisp_to_json_toplevel (lisp);
475} 496}
476 497
477DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, 1, NULL, 498DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, 1, NULL,
478 doc: /* Return the JSON representation of OBJECT as a string. 499 doc: /* Return the JSON representation of OBJECT as a string.
479OBJECT must be a vector, hashtable, or alist, and its elements can 500OBJECT must be a vector, hashtable, alist, or plist and its elements
480recursively contain `:null', `:false', t, numbers, strings, or other 501can recursively contain `:null', `:false', t, numbers, strings, or
481vectors hashtables, and alist. `:null', `:false', and t will be 502other vectors hashtables, alists or plists. `:null', `:false', and t
482converted to JSON null, false, and true values, respectively. Vectors 503will be converted to JSON null, false, and true values, respectively.
483will be converted to JSON arrays, and hashtables and alists to JSON 504Vectors will be converted to JSON arrays, whereas hashtables, alists
484objects. Hashtable keys must be strings without embedded null 505and plists are converted to JSON objects. Hashtable keys must be
485characters and must be unique within each object. Alist keys must be 506strings without embedded null characters and must be unique within
486symbols; if a key is duplicate, the first instance is used. */) 507each object. Alist and plist keys must be symbols; if a key is
508duplicate, the first instance is used. */)
487 (Lisp_Object object) 509 (Lisp_Object object)
488{ 510{
489 ptrdiff_t count = SPECPDL_INDEX (); 511 ptrdiff_t count = SPECPDL_INDEX ();
@@ -605,6 +627,7 @@ OBJECT. */)
605enum json_object_type { 627enum json_object_type {
606 json_object_hashtable, 628 json_object_hashtable,
607 json_object_alist, 629 json_object_alist,
630 json_object_plist
608}; 631};
609 632
610/* Convert a JSON object to a Lisp object. */ 633/* Convert a JSON object to a Lisp object. */
@@ -692,6 +715,28 @@ json_to_lisp (json_t *json, enum json_object_type object_type)
692 result = Fnreverse (result); 715 result = Fnreverse (result);
693 break; 716 break;
694 } 717 }
718 case json_object_plist:
719 {
720 result = Qnil;
721 const char *key_str;
722 json_t *value;
723 json_object_foreach (json, key_str, value)
724 {
725 USE_SAFE_ALLOCA;
726 ptrdiff_t key_str_len = strlen (key_str);
727 char *keyword_key_str = SAFE_ALLOCA (1 + key_str_len + 1);
728 keyword_key_str[0] = ':';
729 strcpy (&keyword_key_str[1], key_str);
730 Lisp_Object key = intern_1 (keyword_key_str, key_str_len + 1);
731 /* Build the plist as value-key since we're going to
732 reverse it in the end.*/
733 result = Fcons (key, result);
734 result = Fcons (json_to_lisp (value, object_type), result);
735 SAFE_FREE ();
736 }
737 result = Fnreverse (result);
738 break;
739 }
695 default: 740 default:
696 /* Can't get here. */ 741 /* Can't get here. */
697 emacs_abort (); 742 emacs_abort ();
@@ -721,8 +766,10 @@ json_parse_object_type (ptrdiff_t nargs, Lisp_Object *args)
721 return json_object_hashtable; 766 return json_object_hashtable;
722 else if (EQ (value, Qalist)) 767 else if (EQ (value, Qalist))
723 return json_object_alist; 768 return json_object_alist;
769 else if (EQ (value, Qplist))
770 return json_object_plist;
724 else 771 else
725 wrong_choice (list2 (Qhash_table, Qalist), value); 772 wrong_choice (list3 (Qhash_table, Qalist, Qplist), value);
726 } 773 }
727 default: 774 default:
728 wrong_type_argument (Qplistp, Flist (nargs, args)); 775 wrong_type_argument (Qplistp, Flist (nargs, args));
@@ -733,14 +780,15 @@ DEFUN ("json-parse-string", Fjson_parse_string, Sjson_parse_string, 1, MANY,
733 NULL, 780 NULL,
734 doc: /* Parse the JSON STRING into a Lisp object. 781 doc: /* Parse the JSON STRING into a Lisp object.
735This is essentially the reverse operation of `json-serialize', which 782This is essentially the reverse operation of `json-serialize', which
736see. The returned object will be a vector, hashtable, or alist. Its 783see. The returned object will be a vector, hashtable, alist, or
737elements will be `:null', `:false', t, numbers, strings, or further 784plist. Its elements will be `:null', `:false', t, numbers, strings,
738vectors, hashtables, and alists. If there are duplicate keys in an 785or further vectors, hashtables, alists, or plists. If there are
739object, all but the last one are ignored. If STRING doesn't contain a 786duplicate keys in an object, all but the last one are ignored. If
740valid JSON object, an error of type `json-parse-error' is signaled. 787STRING doesn't contain a valid JSON object, an error of type
741The keyword argument `:object-type' specifies which Lisp type is used 788`json-parse-error' is signaled. The keyword argument `:object-type'
742to represent objects; it can be `hash-table' or `alist'. 789specifies which Lisp type is used to represent objects; it can be
743usage: (json-parse-string STRING &key (OBJECT-TYPE \\='hash-table)) */) 790`hash-table', `alist' or `plist'.
791usage: (json-parse-string STRING &key (OBJECT-TYPE \\='hash-table)) */)
744 (ptrdiff_t nargs, Lisp_Object *args) 792 (ptrdiff_t nargs, Lisp_Object *args)
745{ 793{
746 ptrdiff_t count = SPECPDL_INDEX (); 794 ptrdiff_t count = SPECPDL_INDEX ();
@@ -912,6 +960,7 @@ syms_of_json (void)
912 960
913 DEFSYM (QCobject_type, ":object-type"); 961 DEFSYM (QCobject_type, ":object-type");
914 DEFSYM (Qalist, "alist"); 962 DEFSYM (Qalist, "alist");
963 DEFSYM (Qplist, "plist");
915 964
916 defsubr (&Sjson_serialize); 965 defsubr (&Sjson_serialize);
917 defsubr (&Sjson_insert); 966 defsubr (&Sjson_insert);