unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#32793: 27.0.50; json-parse-string doesn't have the equivalent of json.el's json-array-type
@ 2018-09-21 12:56 Xu Chunyang
  2019-04-11 16:26 ` Dmitry Gutov
  0 siblings, 1 reply; 10+ messages in thread
From: Xu Chunyang @ 2018-09-21 12:56 UTC (permalink / raw)
  To: 32793

Hello,

We can parse JSON Array as Lisp List with json.el, e.g.,

(let ((json-array-type 'list))
  (json-read-from-string "[1,2,3]"))
;; => (1 2 3)

but the new json-parse-string doesn't have the equivalent, thus porting
existing code to using json-parse-string might be difficult.





^ permalink raw reply	[flat|nested] 10+ messages in thread

* bug#32793: 27.0.50; json-parse-string doesn't have the equivalent of json.el's json-array-type
  2018-09-21 12:56 bug#32793: 27.0.50; json-parse-string doesn't have the equivalent of json.el's json-array-type Xu Chunyang
@ 2019-04-11 16:26 ` Dmitry Gutov
  2019-04-11 16:46   ` Eli Zaretskii
  0 siblings, 1 reply; 10+ messages in thread
From: Dmitry Gutov @ 2019-04-11 16:26 UTC (permalink / raw)
  To: Xu Chunyang, 32793

On 21.09.2018 15:56, Xu Chunyang wrote:

> We can parse JSON Array as Lisp List with json.el, e.g.,
> 
> (let ((json-array-type 'list))
>    (json-read-from-string "[1,2,3]"))
> ;; => (1 2 3)
> 
> but the new json-parse-string doesn't have the equivalent, thus porting
> existing code to using json-parse-string might be difficult.

I also stumbled on this problem when trying to port existing code to 
native JSON.

It's kind of surprising, considering lists are more common in Elisp, and 
there's no JSON data structure that would correspond to them (so I can't 
simply return a different format from the backing server process, I'll 
have to convert).





^ permalink raw reply	[flat|nested] 10+ messages in thread

* bug#32793: 27.0.50; json-parse-string doesn't have the equivalent of json.el's json-array-type
  2019-04-11 16:26 ` Dmitry Gutov
@ 2019-04-11 16:46   ` Eli Zaretskii
  2019-04-12  0:34     ` Dmitry Gutov
  0 siblings, 1 reply; 10+ messages in thread
From: Eli Zaretskii @ 2019-04-11 16:46 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: mail, 32793

> From: Dmitry Gutov <dgutov@yandex.ru>
> Date: Thu, 11 Apr 2019 19:26:23 +0300
> 
> On 21.09.2018 15:56, Xu Chunyang wrote:
> 
> > We can parse JSON Array as Lisp List with json.el, e.g.,
> > 
> > (let ((json-array-type 'list))
> >    (json-read-from-string "[1,2,3]"))
> > ;; => (1 2 3)
> > 
> > but the new json-parse-string doesn't have the equivalent, thus porting
> > existing code to using json-parse-string might be difficult.
> 
> I also stumbled on this problem when trying to port existing code to 
> native JSON.

Sounds like a simple extension of the existing code, so patches are
welcome to implement this feature.





^ permalink raw reply	[flat|nested] 10+ messages in thread

* bug#32793: 27.0.50; json-parse-string doesn't have the equivalent of json.el's json-array-type
  2019-04-11 16:46   ` Eli Zaretskii
@ 2019-04-12  0:34     ` Dmitry Gutov
  2019-04-12  9:22       ` Eli Zaretskii
  0 siblings, 1 reply; 10+ messages in thread
From: Dmitry Gutov @ 2019-04-12  0:34 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: mail, 32793

[-- Attachment #1: Type: text/plain, Size: 198 bytes --]

On 11.04.2019 19:46, Eli Zaretskii wrote:

> Sounds like a simple extension of the existing code, so patches are
> welcome to implement this feature.

Very well, patch attached.

What do you think?

[-- Attachment #2: json-array-type.diff --]
[-- Type: text/x-patch, Size: 6072 bytes --]

diff --git a/src/json.c b/src/json.c
index 5e1439f881..c6c5a1a9b1 100644
--- a/src/json.c
+++ b/src/json.c
@@ -337,8 +337,14 @@ enum json_object_type {
   json_object_plist
 };
 
+enum json_array_type {
+  json_array_array,
+  json_array_list
+};
+
 struct json_configuration {
   enum json_object_type object_type;
+  enum json_array_type array_type;
   Lisp_Object null_object;
   Lisp_Object false_object;
 };
@@ -521,7 +527,7 @@ static void
 json_parse_args (ptrdiff_t nargs,
                  Lisp_Object *args,
                  struct json_configuration *conf,
-                 bool configure_object_type)
+                 bool configure_types)
 {
   if ((nargs % 2) != 0)
     wrong_type_argument (Qplistp, Flist (nargs, args));
@@ -531,7 +537,7 @@ json_parse_args (ptrdiff_t nargs,
   for (ptrdiff_t i = nargs; i > 0; i -= 2) {
     Lisp_Object key = args[i - 2];
     Lisp_Object value = args[i - 1];
-    if (configure_object_type && EQ (key, QCobject_type))
+    if (configure_types && EQ (key, QCobject_type))
       {
         if (EQ (value, Qhash_table))
           conf->object_type = json_object_hashtable;
@@ -542,12 +548,22 @@ json_parse_args (ptrdiff_t nargs,
         else
           wrong_choice (list3 (Qhash_table, Qalist, Qplist), value);
       }
+    else if (configure_types && EQ (key, QCarray_type))
+      {
+        if (EQ (value, Qarray))
+          conf->array_type = json_array_array;
+        else if (EQ (value, Qlist))
+          conf->array_type = json_array_list;
+        else
+          wrong_choice (list2 (Qarray, Qlist), value);
+      }
     else if (EQ (key, QCnull_object))
       conf->null_object = value;
     else if (EQ (key, QCfalse_object))
       conf->false_object = value;
-    else if (configure_object_type)
-      wrong_choice (list3 (QCobject_type,
+    else if (configure_types)
+      wrong_choice (list4 (QCobject_type,
+                           QCarray_type,
                            QCnull_object,
                            QCfalse_object),
                     value);
@@ -604,7 +620,8 @@ usage: (json-serialize OBJECT &rest ARGS)  */)
     }
 #endif
 
-  struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+  struct json_configuration conf =
+    {json_object_hashtable, json_array_array, QCnull, QCfalse};
   json_parse_args (nargs - 1, args + 1, &conf, false);
 
   json_t *json = lisp_to_json_toplevel (args[0], &conf);
@@ -701,7 +718,8 @@ usage: (json-insert OBJECT &rest ARGS)  */)
     }
 #endif
 
-  struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+  struct json_configuration conf =
+    {json_object_hashtable, json_array_array, QCnull, QCfalse};
   json_parse_args (nargs - 1, args + 1, &conf, false);
 
   json_t *json = lisp_to_json (args[0], &conf);
@@ -817,10 +835,30 @@ json_to_lisp (json_t *json, struct json_configuration *conf)
         size_t size = json_array_size (json);
         if (PTRDIFF_MAX < size)
           overflow_error ();
-        Lisp_Object result = make_vector (size, Qunbound);
-        for (ptrdiff_t i = 0; i < size; ++i)
-          ASET (result, i,
-                json_to_lisp (json_array_get (json, i), conf));
+        Lisp_Object result;
+        switch (conf->array_type)
+          {
+          case json_array_array:
+            {
+              result = make_vector (size, Qunbound);
+              for (ptrdiff_t i = 0; i < size; ++i)
+                ASET (result, i,
+                      json_to_lisp (json_array_get (json, i), conf));
+              break;
+            }
+          case json_array_list:
+            {
+              result = Qnil;
+              for (ptrdiff_t i = 0; i < size; ++i)
+                result = Fcons (json_to_lisp (json_array_get (json, i), conf),
+                                result);
+              result = Fnreverse (result);
+              break;
+            }
+          default:
+            /* Can't get here.  */
+            emacs_abort ();
+          }
         --lisp_eval_depth;
         return result;
       }
@@ -946,7 +984,8 @@ usage: (json-parse-string STRING &rest ARGS) */)
   Lisp_Object string = args[0];
   Lisp_Object encoded = json_encode (string);
   check_string_without_embedded_nuls (encoded);
-  struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+  struct json_configuration conf =
+    {json_object_hashtable, json_array_array, QCnull, QCfalse};
   json_parse_args (nargs - 1, args + 1, &conf, true);
 
   json_error_t error;
@@ -1016,7 +1055,8 @@ usage: (json-parse-buffer &rest args) */)
     }
 #endif
 
-  struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+  struct json_configuration conf =
+    {json_object_hashtable, json_array_array, QCnull, QCfalse};
   json_parse_args (nargs, args, &conf, true);
 
   ptrdiff_t point = PT_BYTE;
@@ -1095,10 +1135,12 @@ syms_of_json (void)
   Fput (Qjson_parse_string, Qside_effect_free, Qt);
 
   DEFSYM (QCobject_type, ":object-type");
+  DEFSYM (QCarray_type, ":array-type");
   DEFSYM (QCnull_object, ":null-object");
   DEFSYM (QCfalse_object, ":false-object");
   DEFSYM (Qalist, "alist");
   DEFSYM (Qplist, "plist");
+  DEFSYM (Qarray, "array");
 
   defsubr (&Sjson_serialize);
   defsubr (&Sjson_insert);
diff --git a/test/src/json-tests.el b/test/src/json-tests.el
index 04f91f4abb..542eec11bf 100644
--- a/test/src/json-tests.el
+++ b/test/src/json-tests.el
@@ -117,6 +117,14 @@ 'json-tests--error
     (should (equal (json-parse-string input :object-type 'plist)
                    '(:abc [9 :false] :def :null)))))
 
+(ert-deftest json-parse-string/array ()
+  (skip-unless (fboundp 'json-parse-string))
+  (let ((input "[\"a\", 1, [\"b\", 2]]"))
+    (should (equal (json-parse-string input)
+                   ["a" 1 ["b" 2]]))
+    (should (equal (json-parse-string input :array-type 'list)
+                   '("a" 1 ("b" 2))))))
+
 (ert-deftest json-parse-string/string ()
   (skip-unless (fboundp 'json-parse-string))
   (should-error (json-parse-string "[\"formfeed\f\"]") :type 'json-parse-error)

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* bug#32793: 27.0.50; json-parse-string doesn't have the equivalent of json.el's json-array-type
  2019-04-12  0:34     ` Dmitry Gutov
@ 2019-04-12  9:22       ` Eli Zaretskii
  2019-04-12 15:02         ` Dmitry Gutov
  0 siblings, 1 reply; 10+ messages in thread
From: Eli Zaretskii @ 2019-04-12  9:22 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: mail, 32793

> Cc: mail@xuchunyang.me, 32793@debbugs.gnu.org
> From: Dmitry Gutov <dgutov@yandex.ru>
> Date: Fri, 12 Apr 2019 03:34:05 +0300
> 
> > Sounds like a simple extension of the existing code, so patches are
> > welcome to implement this feature.
> 
> Very well, patch attached.
> 
> What do you think?

Thanks, some comments below.

> +enum json_array_type {
> +  json_array_array,
> +  json_array_list
> +};
> +
>  struct json_configuration {
>    enum json_object_type object_type;
> +  enum json_array_type array_type;
>    Lisp_Object null_object;
>    Lisp_Object false_object;
>  };

Can't say I like this conversion of Lisp symbols into C enumerations.
I'd rather we used symbols (Qarray, Qlist, etc.), but I can understand
why you did this as json.c already did for the other keyword values.

> @@ -521,7 +527,7 @@ static void
>  json_parse_args (ptrdiff_t nargs,
>                   Lisp_Object *args,
>                   struct json_configuration *conf,
> -                 bool configure_object_type)
> +                 bool configure_types)

If we are renaming this argument, let's do a better job: I think its
name should have been parse_object_types.

Come to think of this: why do we need this boolean at all?  The
callers which don't want :object-type parsed will ignore the result
anyway, so it sounds like something we could just toss.

> +          case json_array_list:
> +            {
> +              result = Qnil;
> +              for (ptrdiff_t i = 0; i < size; ++i)
> +                result = Fcons (json_to_lisp (json_array_get (json, i), conf),
> +                                result);
> +              result = Fnreverse (result);

If you cons the list back to front, you can avoid the Fnreverse call,
which will make this faster.

Also, please insert a call to rarely_quit into the loop, as JSON
vectors could be quite large, AFAIU.

Finally, this needs documentation update: the doc strings of
json-parse-string and json-parse-buffer, NEWS, and the ELisp manual.

Thanks again for working on this.





^ permalink raw reply	[flat|nested] 10+ messages in thread

* bug#32793: 27.0.50; json-parse-string doesn't have the equivalent of json.el's json-array-type
  2019-04-12  9:22       ` Eli Zaretskii
@ 2019-04-12 15:02         ` Dmitry Gutov
  2019-04-12 15:26           ` Eli Zaretskii
  0 siblings, 1 reply; 10+ messages in thread
From: Dmitry Gutov @ 2019-04-12 15:02 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: mail, 32793

[-- Attachment #1: Type: text/plain, Size: 2416 bytes --]

On 12.04.2019 12:22, Eli Zaretskii wrote:

> Can't say I like this conversion of Lisp symbols into C enumerations.
> I'd rather we used symbols (Qarray, Qlist, etc.), but I can understand
> why you did this as json.c already did for the other keyword values.

I vaguely suspect it might help with performance. *shrug* Or not.

>> @@ -521,7 +527,7 @@ static void
>>   json_parse_args (ptrdiff_t nargs,
>>                    Lisp_Object *args,
>>                    struct json_configuration *conf,
>> -                 bool configure_object_type)
>> +                 bool configure_types)
> 
> If we are renaming this argument, let's do a better job: I think its
> name should have been parse_object_types.

OK.

> Come to think of this: why do we need this boolean at all?  The
> callers which don't want :object-type parsed will ignore the result
> anyway, so it sounds like something we could just toss.

I'd rather we didn't accept argument we cannot handle, to avoid false 
expectations.

For example with this patch we can parse a JSON array into a Lisp list.

But there's no way to serialize a list back into a JSON array, yet.

I looked at implementing it, just for completeness (it's not necessary 
for my use case, for now). But there's an ambiguity between lists and 
alists which is not trivial to solve (i.e. when object-type is alist and 
array-type is list, how do we determinine, quickly and reliably, that a 
given list is actually an alist?). So maybe leave it for the future.

>> +          case json_array_list:
>> +            {
>> +              result = Qnil;
>> +              for (ptrdiff_t i = 0; i < size; ++i)
>> +                result = Fcons (json_to_lisp (json_array_get (json, i), conf),
>> +                                result);
>> +              result = Fnreverse (result);
> 
> If you cons the list back to front, you can avoid the Fnreverse call,
> which will make this faster.

Done. No real performance impact that I can see, but it didn't hurt either.

> Also, please insert a call to rarely_quit into the loop, as JSON
> vectors could be quite large, AFAIU.

Also done. In the json_array_array case as well, where it was missing, 
it seems.

> Finally, this needs documentation update: the doc strings of
> json-parse-string and json-parse-buffer, NEWS, and the ELisp manual.

json-parse-buffer and NEWS don't need updating, I think.

Otherwise, done, see the new patch.

[-- Attachment #2: json-array-type.diff --]
[-- Type: text/x-patch, Size: 7292 bytes --]

diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index 1ef836b8f9..b46ee64786 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -5167,6 +5167,11 @@ Parsing JSON
 keys; @code{alist} to use alists with symbols as keys; or @code{plist}
 to use plists with keyword symbols as keys.
 
+@item :array-type
+The value decides which Lisp object to use for representing a JSON
+array.  It can be either @code{array}, the default, to use Lisp
+arrays; or @code{list} to use lists.
+
 @item :null-object
 The value decides which Lisp object to use to represent the JSON
 keyword @code{null}.  It defaults to the symbol @code{:null}.
diff --git a/src/json.c b/src/json.c
index 5e1439f881..1f45fe527f 100644
--- a/src/json.c
+++ b/src/json.c
@@ -337,8 +337,14 @@ enum json_object_type {
   json_object_plist
 };
 
+enum json_array_type {
+  json_array_array,
+  json_array_list
+};
+
 struct json_configuration {
   enum json_object_type object_type;
+  enum json_array_type array_type;
   Lisp_Object null_object;
   Lisp_Object false_object;
 };
@@ -521,7 +527,7 @@ static void
 json_parse_args (ptrdiff_t nargs,
                  Lisp_Object *args,
                  struct json_configuration *conf,
-                 bool configure_object_type)
+                 bool parse_object_types)
 {
   if ((nargs % 2) != 0)
     wrong_type_argument (Qplistp, Flist (nargs, args));
@@ -531,7 +537,7 @@ json_parse_args (ptrdiff_t nargs,
   for (ptrdiff_t i = nargs; i > 0; i -= 2) {
     Lisp_Object key = args[i - 2];
     Lisp_Object value = args[i - 1];
-    if (configure_object_type && EQ (key, QCobject_type))
+    if (parse_object_types && EQ (key, QCobject_type))
       {
         if (EQ (value, Qhash_table))
           conf->object_type = json_object_hashtable;
@@ -542,12 +548,22 @@ json_parse_args (ptrdiff_t nargs,
         else
           wrong_choice (list3 (Qhash_table, Qalist, Qplist), value);
       }
+    else if (parse_object_types && EQ (key, QCarray_type))
+      {
+        if (EQ (value, Qarray))
+          conf->array_type = json_array_array;
+        else if (EQ (value, Qlist))
+          conf->array_type = json_array_list;
+        else
+          wrong_choice (list2 (Qarray, Qlist), value);
+      }
     else if (EQ (key, QCnull_object))
       conf->null_object = value;
     else if (EQ (key, QCfalse_object))
       conf->false_object = value;
-    else if (configure_object_type)
-      wrong_choice (list3 (QCobject_type,
+    else if (parse_object_types)
+      wrong_choice (list4 (QCobject_type,
+                           QCarray_type,
                            QCnull_object,
                            QCfalse_object),
                     value);
@@ -604,7 +620,8 @@ usage: (json-serialize OBJECT &rest ARGS)  */)
     }
 #endif
 
-  struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+  struct json_configuration conf =
+    {json_object_hashtable, json_array_array, QCnull, QCfalse};
   json_parse_args (nargs - 1, args + 1, &conf, false);
 
   json_t *json = lisp_to_json_toplevel (args[0], &conf);
@@ -701,7 +718,8 @@ usage: (json-insert OBJECT &rest ARGS)  */)
     }
 #endif
 
-  struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+  struct json_configuration conf =
+    {json_object_hashtable, json_array_array, QCnull, QCfalse};
   json_parse_args (nargs - 1, args + 1, &conf, false);
 
   json_t *json = lisp_to_json (args[0], &conf);
@@ -817,10 +835,35 @@ json_to_lisp (json_t *json, struct json_configuration *conf)
         size_t size = json_array_size (json);
         if (PTRDIFF_MAX < size)
           overflow_error ();
-        Lisp_Object result = make_vector (size, Qunbound);
-        for (ptrdiff_t i = 0; i < size; ++i)
-          ASET (result, i,
-                json_to_lisp (json_array_get (json, i), conf));
+        Lisp_Object result;
+        switch (conf->array_type)
+          {
+          case json_array_array:
+            {
+              result = make_vector (size, Qunbound);
+              for (ptrdiff_t i = 0; i < size; ++i)
+                {
+                  rarely_quit (i);
+                  ASET (result, i,
+                        json_to_lisp (json_array_get (json, i), conf));
+                }
+              break;
+            }
+          case json_array_list:
+            {
+              result = Qnil;
+              for (ptrdiff_t i = size - 1; i >= 0; --i)
+                {
+                  rarely_quit (i);
+                  result = Fcons (json_to_lisp (json_array_get (json, i), conf),
+                                  result);
+                }
+              break;
+            }
+          default:
+            /* Can't get here.  */
+            emacs_abort ();
+          }
         --lisp_eval_depth;
         return result;
       }
@@ -918,6 +961,9 @@ a list of keyword/argument pairs:
 The keyword argument `:object-type' specifies which Lisp type is used
 to represent objects; it can be `hash-table', `alist' or `plist'.
 
+The keyword argument `:array-type' specifies which Lisp type is used
+to represent arrays; it can be `array' or `list'.
+
 The keyword argument `:null-object' specifies which object to use
 to represent a JSON null value.  It defaults to `:null'.
 
@@ -946,7 +992,8 @@ usage: (json-parse-string STRING &rest ARGS) */)
   Lisp_Object string = args[0];
   Lisp_Object encoded = json_encode (string);
   check_string_without_embedded_nuls (encoded);
-  struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+  struct json_configuration conf =
+    {json_object_hashtable, json_array_array, QCnull, QCfalse};
   json_parse_args (nargs - 1, args + 1, &conf, true);
 
   json_error_t error;
@@ -1016,7 +1063,8 @@ usage: (json-parse-buffer &rest args) */)
     }
 #endif
 
-  struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+  struct json_configuration conf =
+    {json_object_hashtable, json_array_array, QCnull, QCfalse};
   json_parse_args (nargs, args, &conf, true);
 
   ptrdiff_t point = PT_BYTE;
@@ -1095,10 +1143,12 @@ syms_of_json (void)
   Fput (Qjson_parse_string, Qside_effect_free, Qt);
 
   DEFSYM (QCobject_type, ":object-type");
+  DEFSYM (QCarray_type, ":array-type");
   DEFSYM (QCnull_object, ":null-object");
   DEFSYM (QCfalse_object, ":false-object");
   DEFSYM (Qalist, "alist");
   DEFSYM (Qplist, "plist");
+  DEFSYM (Qarray, "array");
 
   defsubr (&Sjson_serialize);
   defsubr (&Sjson_insert);
diff --git a/test/src/json-tests.el b/test/src/json-tests.el
index 04f91f4abb..542eec11bf 100644
--- a/test/src/json-tests.el
+++ b/test/src/json-tests.el
@@ -117,6 +117,14 @@ 'json-tests--error
     (should (equal (json-parse-string input :object-type 'plist)
                    '(:abc [9 :false] :def :null)))))
 
+(ert-deftest json-parse-string/array ()
+  (skip-unless (fboundp 'json-parse-string))
+  (let ((input "[\"a\", 1, [\"b\", 2]]"))
+    (should (equal (json-parse-string input)
+                   ["a" 1 ["b" 2]]))
+    (should (equal (json-parse-string input :array-type 'list)
+                   '("a" 1 ("b" 2))))))
+
 (ert-deftest json-parse-string/string ()
   (skip-unless (fboundp 'json-parse-string))
   (should-error (json-parse-string "[\"formfeed\f\"]") :type 'json-parse-error)

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* bug#32793: 27.0.50; json-parse-string doesn't have the equivalent of json.el's json-array-type
  2019-04-12 15:02         ` Dmitry Gutov
@ 2019-04-12 15:26           ` Eli Zaretskii
  2019-04-12 15:45             ` Dmitry Gutov
  0 siblings, 1 reply; 10+ messages in thread
From: Eli Zaretskii @ 2019-04-12 15:26 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: mail, 32793

> Cc: mail@xuchunyang.me, 32793@debbugs.gnu.org
> From: Dmitry Gutov <dgutov@yandex.ru>
> Date: Fri, 12 Apr 2019 18:02:22 +0300
> 
> > Come to think of this: why do we need this boolean at all?  The
> > callers which don't want :object-type parsed will ignore the result
> > anyway, so it sounds like something we could just toss.
> 
> I'd rather we didn't accept argument we cannot handle, to avoid false 
> expectations.
> 
> For example with this patch we can parse a JSON array into a Lisp list.
> 
> But there's no way to serialize a list back into a JSON array, yet.

This argument is meaningless for serializing, I think.  But OK.

> >> +              for (ptrdiff_t i = 0; i < size; ++i)
> >> +                result = Fcons (json_to_lisp (json_array_get (json, i), conf),
> >> +                                result);
> >> +              result = Fnreverse (result);
> > 
> > If you cons the list back to front, you can avoid the Fnreverse call,
> > which will make this faster.
> 
> Done. No real performance impact that I can see, but it didn't hurt either.

You just didn't try an array big enough for this to matter.

> > Also, please insert a call to rarely_quit into the loop, as JSON
> > vectors could be quite large, AFAIU.
> 
> Also done. In the json_array_array case as well, where it was missing, 
> it seems.

I don't mind, although in that case the loop just assigns value to a
vector that was already consed.

> json-parse-buffer and NEWS don't need updating, I think.

They don't?  Why not?

> Otherwise, done, see the new patch.

LGTM, with the above question, and this one gotcha:

> @@ -918,6 +961,9 @@ a list of keyword/argument pairs:
>  The keyword argument `:object-type' specifies which Lisp type is used
>  to represent objects; it can be `hash-table', `alist' or `plist'.
>  
> +The keyword argument `:array-type' specifies which Lisp type is used
> +to represent arrays; it can be `array' or `list'.

Please say here that 'array' is the default.

Thanks.





^ permalink raw reply	[flat|nested] 10+ messages in thread

* bug#32793: 27.0.50; json-parse-string doesn't have the equivalent of json.el's json-array-type
  2019-04-12 15:26           ` Eli Zaretskii
@ 2019-04-12 15:45             ` Dmitry Gutov
  2019-04-12 17:42               ` Eli Zaretskii
  0 siblings, 1 reply; 10+ messages in thread
From: Dmitry Gutov @ 2019-04-12 15:45 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: mail, 32793

[-- Attachment #1: Type: text/plain, Size: 1348 bytes --]

On 12.04.2019 18:26, Eli Zaretskii wrote:

> This argument is meaningless for serializing, I think.  But OK.

I wouldn't be so sure. For instance, one could attempt to resolve the 
alist-list conflict using a combination of :object-type and :array-type 
parameters. This isn't going to work, at least not yet. It's better to 
fail earlier.

> You just didn't try an array big enough for this to matter.

My array was big enough for the switch from json.el to json.c to make a 
real difference.

Anyway, it depends on how "big" the elements inside the array are as 
well. In my case they're fairly complex too.

> I don't mind, although in that case the loop just assigns value to a
> vector that was already consed.

It also calls

   json_to_lisp (json_array_get (json, i), conf)

in that loop.

>> json-parse-buffer and NEWS don't need updating, I think.
> 
> They don't?  Why not?

There was no json.c in Emacs 26. NEWS describes the changes from the 
previous release, and the current entry only lists the public functions.

json-parse-buffer doesn't enumerate the keyword arguments either.

>> +The keyword argument `:array-type' specifies which Lisp type is used
>> +to represent arrays; it can be `array' or `list'.
> 
> Please say here that 'array' is the default.

OK. I've had to update the preceding paragraph as well.

Good to install?

[-- Attachment #2: json-array-type.diff --]
[-- Type: text/x-patch, Size: 7488 bytes --]

diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index 1ef836b8f9..b46ee64786 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -5167,6 +5167,11 @@ Parsing JSON
 keys; @code{alist} to use alists with symbols as keys; or @code{plist}
 to use plists with keyword symbols as keys.
 
+@item :array-type
+The value decides which Lisp object to use for representing a JSON
+array.  It can be either @code{array}, the default, to use Lisp
+arrays; or @code{list} to use lists.
+
 @item :null-object
 The value decides which Lisp object to use to represent the JSON
 keyword @code{null}.  It defaults to the symbol @code{:null}.
diff --git a/src/json.c b/src/json.c
index 5e1439f881..eb323b498c 100644
--- a/src/json.c
+++ b/src/json.c
@@ -337,8 +337,14 @@ enum json_object_type {
   json_object_plist
 };
 
+enum json_array_type {
+  json_array_array,
+  json_array_list
+};
+
 struct json_configuration {
   enum json_object_type object_type;
+  enum json_array_type array_type;
   Lisp_Object null_object;
   Lisp_Object false_object;
 };
@@ -521,7 +527,7 @@ static void
 json_parse_args (ptrdiff_t nargs,
                  Lisp_Object *args,
                  struct json_configuration *conf,
-                 bool configure_object_type)
+                 bool parse_object_types)
 {
   if ((nargs % 2) != 0)
     wrong_type_argument (Qplistp, Flist (nargs, args));
@@ -531,7 +537,7 @@ json_parse_args (ptrdiff_t nargs,
   for (ptrdiff_t i = nargs; i > 0; i -= 2) {
     Lisp_Object key = args[i - 2];
     Lisp_Object value = args[i - 1];
-    if (configure_object_type && EQ (key, QCobject_type))
+    if (parse_object_types && EQ (key, QCobject_type))
       {
         if (EQ (value, Qhash_table))
           conf->object_type = json_object_hashtable;
@@ -542,12 +548,22 @@ json_parse_args (ptrdiff_t nargs,
         else
           wrong_choice (list3 (Qhash_table, Qalist, Qplist), value);
       }
+    else if (parse_object_types && EQ (key, QCarray_type))
+      {
+        if (EQ (value, Qarray))
+          conf->array_type = json_array_array;
+        else if (EQ (value, Qlist))
+          conf->array_type = json_array_list;
+        else
+          wrong_choice (list2 (Qarray, Qlist), value);
+      }
     else if (EQ (key, QCnull_object))
       conf->null_object = value;
     else if (EQ (key, QCfalse_object))
       conf->false_object = value;
-    else if (configure_object_type)
-      wrong_choice (list3 (QCobject_type,
+    else if (parse_object_types)
+      wrong_choice (list4 (QCobject_type,
+                           QCarray_type,
                            QCnull_object,
                            QCfalse_object),
                     value);
@@ -604,7 +620,8 @@ usage: (json-serialize OBJECT &rest ARGS)  */)
     }
 #endif
 
-  struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+  struct json_configuration conf =
+    {json_object_hashtable, json_array_array, QCnull, QCfalse};
   json_parse_args (nargs - 1, args + 1, &conf, false);
 
   json_t *json = lisp_to_json_toplevel (args[0], &conf);
@@ -701,7 +718,8 @@ usage: (json-insert OBJECT &rest ARGS)  */)
     }
 #endif
 
-  struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+  struct json_configuration conf =
+    {json_object_hashtable, json_array_array, QCnull, QCfalse};
   json_parse_args (nargs - 1, args + 1, &conf, false);
 
   json_t *json = lisp_to_json (args[0], &conf);
@@ -817,10 +835,35 @@ json_to_lisp (json_t *json, struct json_configuration *conf)
         size_t size = json_array_size (json);
         if (PTRDIFF_MAX < size)
           overflow_error ();
-        Lisp_Object result = make_vector (size, Qunbound);
-        for (ptrdiff_t i = 0; i < size; ++i)
-          ASET (result, i,
-                json_to_lisp (json_array_get (json, i), conf));
+        Lisp_Object result;
+        switch (conf->array_type)
+          {
+          case json_array_array:
+            {
+              result = make_vector (size, Qunbound);
+              for (ptrdiff_t i = 0; i < size; ++i)
+                {
+                  rarely_quit (i);
+                  ASET (result, i,
+                        json_to_lisp (json_array_get (json, i), conf));
+                }
+              break;
+            }
+          case json_array_list:
+            {
+              result = Qnil;
+              for (ptrdiff_t i = size - 1; i >= 0; --i)
+                {
+                  rarely_quit (i);
+                  result = Fcons (json_to_lisp (json_array_get (json, i), conf),
+                                  result);
+                }
+              break;
+            }
+          default:
+            /* Can't get here.  */
+            emacs_abort ();
+          }
         --lisp_eval_depth;
         return result;
       }
@@ -916,7 +959,12 @@ error of type `json-parse-error' is signaled.  The arguments ARGS are
 a list of keyword/argument pairs:
 
 The keyword argument `:object-type' specifies which Lisp type is used
-to represent objects; it can be `hash-table', `alist' or `plist'.
+to represent objects; it can be `hash-table', `alist' or `plist'.  It
+defaults to `hash-table'.
+
+The keyword argument `:array-type' specifies which Lisp type is used
+to represent arrays; it can be `array' or `list'.  It defaults to
+`array'.
 
 The keyword argument `:null-object' specifies which object to use
 to represent a JSON null value.  It defaults to `:null'.
@@ -946,7 +994,8 @@ usage: (json-parse-string STRING &rest ARGS) */)
   Lisp_Object string = args[0];
   Lisp_Object encoded = json_encode (string);
   check_string_without_embedded_nuls (encoded);
-  struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+  struct json_configuration conf =
+    {json_object_hashtable, json_array_array, QCnull, QCfalse};
   json_parse_args (nargs - 1, args + 1, &conf, true);
 
   json_error_t error;
@@ -1016,7 +1065,8 @@ usage: (json-parse-buffer &rest args) */)
     }
 #endif
 
-  struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+  struct json_configuration conf =
+    {json_object_hashtable, json_array_array, QCnull, QCfalse};
   json_parse_args (nargs, args, &conf, true);
 
   ptrdiff_t point = PT_BYTE;
@@ -1095,10 +1145,12 @@ syms_of_json (void)
   Fput (Qjson_parse_string, Qside_effect_free, Qt);
 
   DEFSYM (QCobject_type, ":object-type");
+  DEFSYM (QCarray_type, ":array-type");
   DEFSYM (QCnull_object, ":null-object");
   DEFSYM (QCfalse_object, ":false-object");
   DEFSYM (Qalist, "alist");
   DEFSYM (Qplist, "plist");
+  DEFSYM (Qarray, "array");
 
   defsubr (&Sjson_serialize);
   defsubr (&Sjson_insert);
diff --git a/test/src/json-tests.el b/test/src/json-tests.el
index 04f91f4abb..542eec11bf 100644
--- a/test/src/json-tests.el
+++ b/test/src/json-tests.el
@@ -117,6 +117,14 @@ 'json-tests--error
     (should (equal (json-parse-string input :object-type 'plist)
                    '(:abc [9 :false] :def :null)))))
 
+(ert-deftest json-parse-string/array ()
+  (skip-unless (fboundp 'json-parse-string))
+  (let ((input "[\"a\", 1, [\"b\", 2]]"))
+    (should (equal (json-parse-string input)
+                   ["a" 1 ["b" 2]]))
+    (should (equal (json-parse-string input :array-type 'list)
+                   '("a" 1 ("b" 2))))))
+
 (ert-deftest json-parse-string/string ()
   (skip-unless (fboundp 'json-parse-string))
   (should-error (json-parse-string "[\"formfeed\f\"]") :type 'json-parse-error)

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* bug#32793: 27.0.50; json-parse-string doesn't have the equivalent of json.el's json-array-type
  2019-04-12 15:45             ` Dmitry Gutov
@ 2019-04-12 17:42               ` Eli Zaretskii
  2019-04-12 22:36                 ` Dmitry Gutov
  0 siblings, 1 reply; 10+ messages in thread
From: Eli Zaretskii @ 2019-04-12 17:42 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: mail, 32793

> Cc: mail@xuchunyang.me, 32793@debbugs.gnu.org
> From: Dmitry Gutov <dgutov@yandex.ru>
> Date: Fri, 12 Apr 2019 18:45:46 +0300
> 
> Good to install?

Yes, thanks.





^ permalink raw reply	[flat|nested] 10+ messages in thread

* bug#32793: 27.0.50; json-parse-string doesn't have the equivalent of json.el's json-array-type
  2019-04-12 17:42               ` Eli Zaretskii
@ 2019-04-12 22:36                 ` Dmitry Gutov
  0 siblings, 0 replies; 10+ messages in thread
From: Dmitry Gutov @ 2019-04-12 22:36 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: mail, 32793-done

On 12.04.2019 20:42, Eli Zaretskii wrote:

>> Good to install?
> 
> Yes, thanks.

Thank you.

Pushed to master, closing.





^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2019-04-12 22:36 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-21 12:56 bug#32793: 27.0.50; json-parse-string doesn't have the equivalent of json.el's json-array-type Xu Chunyang
2019-04-11 16:26 ` Dmitry Gutov
2019-04-11 16:46   ` Eli Zaretskii
2019-04-12  0:34     ` Dmitry Gutov
2019-04-12  9:22       ` Eli Zaretskii
2019-04-12 15:02         ` Dmitry Gutov
2019-04-12 15:26           ` Eli Zaretskii
2019-04-12 15:45             ` Dmitry Gutov
2019-04-12 17:42               ` Eli Zaretskii
2019-04-12 22:36                 ` Dmitry Gutov

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).