@itemize
@item
-JSON has a couple of keywords: @code{null}, @code{false}, and
-@code{true}. These are represented in Lisp using the keywords
-@code{:null}, @code{:false}, and @code{t}, respectively.
+JSON uses three keywords: @code{true}, @code{null}, @code{false}.
+@code{true} is represented by the symbol @code{t}. By default, the
+remaining two are represented, respectively, by the symbols
+@code{:null} and @code{:false}.
@item
JSON only has floating-point numbers. They can represent both Lisp
type. Likewise, the parsing functions will only return vectors,
hashtables, alists, and plists.
- The parsing functions accept keyword arguments. Currently only one
-keyword argument, @code{:object-type}, is recognized; its value
-decides which Lisp object to use for representing the key-value
-mappings of a JSON object. It can be either @code{hash-table}, the
-default, to make hashtables with strings as keys, @code{alist} to use
-alists with symbols as keys or @code{plist} to use plists with keyword
-symbols as keys.
-
@defun json-serialize object
This function returns a new Lisp string which contains the JSON
representation of @var{object}.
current buffer before point.
@end defun
-@defun json-parse-string string &key (object-type @code{hash-table})
+@defun json-parse-string string &rest args
This function parses the JSON value in @var{string}, which must be a
-Lisp string.
+Lisp string. The argument @var{args} is a list of keyword/argument
+pairs. The following keywords are accepted:
+
+@itemize
+
+@item @code{:object-type}
+The value decides which Lisp object to use for representing the
+key-value mappings of a JSON object. It can be either
+@code{hash-table}, the default, to make hashtables with strings as
+keys; @code{alist} to use alists with symbols as keys; or @code{plist}
+to use plists with keyword symbols as keys.
+
+@item @code{:null-object}
+The value decides which Lisp object to use to represent the JSON
+keyword @code{null}. It defaults to the lisp symbol @code{:null}.
+
+@item @code{:false-object}
+The value decides which Lisp object to use to represent the JSON
+keyword @code{false}. It defaults to the lisp symbol @code{:false}.
+
+@end itemize
+
@end defun
-@defun json-parse-buffer &key (object-type @code{hash-table})
+@defun json-parse-buffer &rest args
This function reads the next JSON value from the current buffer,
starting at point. It moves point to the position immediately after
the value if a value could be read and converted to Lisp; otherwise it
-doesn't move point.
+doesn't move point. @var{args} is interpreted as in
+@code{json-parse-string}.
@end defun
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
-your option) any later version.
+nyour option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
strings without embedded null characters and must be unique within
each object. Alist and plist keys must be symbols; if a key is
duplicate, the first instance is used. */)
- (Lisp_Object object)
+ (Lisp_Object object)
{
ptrdiff_t count = SPECPDL_INDEX ();
DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, 1, NULL,
doc: /* Insert the JSON representation of OBJECT before point.
-This is the same as (insert (json-serialize OBJECT)), but potentially
-faster. See the function `json-serialize' for allowed values of
-OBJECT. */)
- (Lisp_Object object)
+ This is the same as (insert (json-serialize OBJECT)), but potentially
+ faster. See the function `json-serialize' for allowed values of
+ OBJECT. */)
+ (Lisp_Object object)
{
ptrdiff_t count = SPECPDL_INDEX ();
}
enum json_object_type {
- json_object_hashtable,
- json_object_alist,
- json_object_plist
+ json_object_hashtable,
+ json_object_alist,
+ json_object_plist
+};
+
+struct json_configuration {
+ enum json_object_type object_type;
+ Lisp_Object null_object;
+ Lisp_Object false_object;
};
/* Convert a JSON object to a Lisp object. */
static _GL_ARG_NONNULL ((1)) Lisp_Object
-json_to_lisp (json_t *json, enum json_object_type object_type)
+json_to_lisp (json_t *json, struct json_configuration *conf)
{
switch (json_typeof (json))
{
case JSON_NULL:
- return QCnull;
+ return conf->null_object;
case JSON_FALSE:
- return QCfalse;
+ return conf->false_object;
case JSON_TRUE:
return Qt;
case JSON_INTEGER:
otherwise. This loses precision for integers with large
magnitude; however, such integers tend to be nonportable
anyway because many JSON implementations use only 64-bit
- floating-point numbers with 53 mantissa bits. See
- https://tools.ietf.org/html/rfc7159#section-6 for some
- discussion. */
+ floating-point numbers with 53 mantissa bits. See
+ https://tools.ietf.org/html/rfc7159#section-6 for some
+ discussion. */
return make_fixnum_or_float (json_integer_value (json));
case JSON_REAL:
return make_float (json_real_value (json));
Lisp_Object result = Fmake_vector (make_natnum (size), Qunbound);
for (ptrdiff_t i = 0; i < size; ++i)
ASET (result, i,
- json_to_lisp (json_array_get (json, i), object_type));
+ json_to_lisp (json_array_get (json, i), conf));
--lisp_eval_depth;
return result;
}
if (++lisp_eval_depth > max_lisp_eval_depth)
xsignal0 (Qjson_object_too_deep);
Lisp_Object result;
- switch (object_type)
+ switch (conf->object_type)
{
case json_object_hashtable:
{
/* Keys in JSON objects are unique, so the key can't
be present yet. */
eassert (i < 0);
- hash_put (h, key, json_to_lisp (value, object_type), hash);
+ hash_put (h, key, json_to_lisp (value, conf), hash);
}
break;
}
{
Lisp_Object key = Fintern (json_build_string (key_str), Qnil);
result
- = Fcons (Fcons (key, json_to_lisp (value, object_type)),
+ = Fcons (Fcons (key, json_to_lisp (value, conf)),
result);
}
result = Fnreverse (result);
/* Build the plist as value-key since we're going to
reverse it in the end.*/
result = Fcons (key, result);
- result = Fcons (json_to_lisp (value, object_type), result);
+ result = Fcons (json_to_lisp (value, conf), result);
SAFE_FREE ();
}
result = Fnreverse (result);
emacs_abort ();
}
-static enum json_object_type
-json_parse_object_type (ptrdiff_t nargs, Lisp_Object *args)
-{
- switch (nargs)
- {
- case 0:
- return json_object_hashtable;
- case 2:
+static void
+json_parse_args (ptrdiff_t nargs,
+ Lisp_Object *args,
+ struct json_configuration *conf)
+{
+ if ((nargs % 2) != 0)
+ wrong_type_argument (Qplistp, Flist (nargs, args));
+
+ /* Start from the back so keyword values appearing
+ first take precedence. */
+ for (ptrdiff_t i = nargs; i > 0; i -= 2) {
+ Lisp_Object key = args[i - 2];
+ Lisp_Object value = args[i - 1];
+ if (EQ (key, QCobject_type))
{
- Lisp_Object key = args[0];
- Lisp_Object value = args[1];
- if (!EQ (key, QCobject_type))
- wrong_choice (list1 (QCobject_type), key);
if (EQ (value, Qhash_table))
- return json_object_hashtable;
+ conf->object_type = json_object_hashtable;
else if (EQ (value, Qalist))
- return json_object_alist;
+ conf->object_type = json_object_alist;
else if (EQ (value, Qplist))
- return json_object_plist;
+ conf->object_type = json_object_plist;
else
wrong_choice (list3 (Qhash_table, Qalist, Qplist), value);
}
- default:
- wrong_type_argument (Qplistp, Flist (nargs, args));
- }
+ else if (EQ (key, QCnull_object))
+ conf->null_object = value;
+ else if (EQ (key, QCfalse_object))
+ conf->false_object = value;
+ else
+ wrong_choice (list3 (QCobject_type,
+ QCnull_object,
+ QCfalse_object),
+ value);
+ }
}
DEFUN ("json-parse-string", Fjson_parse_string, Sjson_parse_string, 1, MANY,
NULL,
doc: /* Parse the JSON STRING into a Lisp object.
+
This is essentially the reverse operation of `json-serialize', which
see. The returned object will be a vector, hashtable, alist, or
-plist. Its elements will be `:null', `:false', t, numbers, strings,
-or further vectors, hashtables, alists, or plists. If there are
-duplicate keys in an object, all but the last one are ignored. If
-STRING doesn't contain a valid JSON object, an error of type
-`json-parse-error' is signaled. The keyword argument `:object-type'
-specifies which Lisp type is used to represent objects; it can be
-`hash-table', `alist' or `plist'.
-usage: (json-parse-string STRING &key (OBJECT-TYPE \\='hash-table)) */)
- (ptrdiff_t nargs, Lisp_Object *args)
+plist. Its elements will be the JSON null value, the JSON false
+value, t, numbers, strings, or further vectors, hashtables, alists, or
+plists. If there are duplicate keys in an object, all but the last
+one are ignored. If STRING doesn't contain a valid JSON object, an
+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'.
+
+The keyword argument `:null-object' specifies which object to use
+to represent a JSON null value. It defaults to `:null'.
+
+The keyword argument `:false-object' specifies which object to use to
+represent a JSON false value. It defaults to `:false'.
+
+usage: (json-parse-string STRING &rest args) */)
+ (ptrdiff_t nargs, Lisp_Object *args)
{
ptrdiff_t count = SPECPDL_INDEX ();
Lisp_Object string = args[0];
Lisp_Object encoded = json_encode (string);
check_string_without_embedded_nulls (encoded);
- enum json_object_type object_type
- = json_parse_object_type (nargs - 1, args + 1);
+ struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+ json_parse_args (nargs - 1, args + 1, &conf);
json_error_t error;
json_t *object = json_loads (SSDATA (encoded), 0, &error);
if (object != NULL)
record_unwind_protect_ptr (json_release_object, object);
- return unbind_to (count, json_to_lisp (object, object_type));
+ return unbind_to (count, json_to_lisp (object, &conf));
}
struct json_read_buffer_data
This is similar to `json-parse-string', which see. Move point after
the end of the object if parsing was successful. On error, point is
not moved.
-usage: (json-parse-buffer &key (OBJECT-TYPE \\='hash-table)) */)
- (ptrdiff_t nargs, Lisp_Object *args)
+usage: (json-parse-buffer &rest args) */)
+ (ptrdiff_t nargs, Lisp_Object *args)
{
ptrdiff_t count = SPECPDL_INDEX ();
}
#endif
- enum json_object_type object_type = json_parse_object_type (nargs, args);
+ struct json_configuration conf = {json_object_hashtable, QCnull, QCfalse};
+ json_parse_args (nargs, args, &conf);
ptrdiff_t point = PT_BYTE;
struct json_read_buffer_data data = {.point = point};
record_unwind_protect_ptr (json_release_object, object);
/* Convert and then move point only if everything succeeded. */
- Lisp_Object lisp = json_to_lisp (object, object_type);
+ Lisp_Object lisp = json_to_lisp (object, &conf);
/* Adjust point by how much we just read. */
point += error.position;
Fput (Qjson_parse_string, Qside_effect_free, Qt);
DEFSYM (QCobject_type, ":object-type");
+ DEFSYM (QCnull_object, ":null-object");
+ DEFSYM (QCfalse_object, ":false-object");
DEFSYM (Qalist, "alist");
DEFSYM (Qplist, "plist");