View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2007-2023, University of Amsterdam
    7                              VU University Amsterdam
    8                              CWI, Amsterdam
    9                              SWI-Prolog Solutions b.v.
   10    All rights reserved.
   11
   12    Redistribution and use in source and binary forms, with or without
   13    modification, are permitted provided that the following conditions
   14    are met:
   15
   16    1. Redistributions of source code must retain the above copyright
   17       notice, this list of conditions and the following disclaimer.
   18
   19    2. Redistributions in binary form must reproduce the above copyright
   20       notice, this list of conditions and the following disclaimer in
   21       the documentation and/or other materials provided with the
   22       distribution.
   23
   24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   25    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   26    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   27    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   28    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   29    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   30    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   31    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   32    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   33    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   34    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   35    POSSIBILITY OF SUCH DAMAGE.
   36*/
   37
   38:- module(json,
   39          [ json_read/2,                % +Stream, -JSONTerm
   40            json_read/3,                % +Stream, -JSONTerm, +Options
   41            atom_json_term/3,           % ?Atom, ?JSONTerm, +Options
   42            json_write/2,               % +Stream, +Term
   43            json_write/3,               % +Stream, +Term, +Options
   44            is_json_term/1,             % @Term
   45            is_json_term/2,             % @Term, +Options
   46                                        % Version 7 dict support
   47            json_read_dict/2,           % +Stream, -Dict
   48            json_read_dict/3,           % +Stream, -Dict, +Options
   49            json_write_dict/2,          % +Stream, +Dict
   50            json_write_dict/3,          % +Stream, +Dict, +Options
   51            atom_json_dict/3            % ?Atom, ?JSONDict, +Options
   52          ]).   53:- use_module(library(record)).   54:- use_module(library(error)).   55:- use_module(library(option)).   56:- use_module(library(lists)).   57
   58:- use_foreign_library(foreign(json)).   59
   60:- multifile
   61    json_write_hook/4,                  % +Term, +Stream, +State, +Options
   62    json_dict_pairs/2.                  % +Dict, -Pairs
   63
   64:- predicate_options(json_read/3, 3,
   65                     [ null(ground),
   66                       true(ground),
   67                       false(ground),
   68                       value_string_as(oneof([atom,string]))
   69                     ]).   70:- predicate_options(json_write/3, 3,
   71                     [ indent(nonneg),
   72                       step(positive_integer),
   73                       tab(positive_integer),
   74                       width(nonneg),
   75                       null(ground),
   76                       true(ground),
   77                       false(ground),
   78                       serialize_unknown(boolean)
   79                     ]).   80:- predicate_options(json_read_dict/3, 3,
   81                     [ tag(atom),
   82                       default_tag(atom),
   83                       pass_to(json_read/3, 3)
   84                     ]).   85:- predicate_options(json_write_dict/3, 3,
   86                     [ tag(atom),
   87                       pass_to(json_write/3, 3)
   88                     ]).   89:- predicate_options(is_json_term/2, 2,
   90                     [ null(ground),
   91                       true(ground),
   92                       false(ground)
   93                     ]).   94:- predicate_options(atom_json_term/3, 3,
   95                     [ as(oneof([atom,string,codes])),
   96                       pass_to(json_read/3, 3),
   97                       pass_to(json_write/3, 3)
   98                     ]).

Reading and writing JSON serialization

This module supports reading and writing JSON objects. This library supports two Prolog representations (the new representation is only supported in SWI-Prolog version 7 and later):

author
- Jan Wielemaker
See also
- http_json.pl links JSON to the HTTP client and server modules.
- json_convert.pl converts JSON Prolog terms to more comfortable terms. */
  122:- record json_options(
  123              null:ground = @(null),
  124              true:ground = @(true),
  125              false:ground = @(false),
  126              end_of_file:ground = error,
  127              value_string_as:oneof([atom,string]) = atom,
  128              tag:atom = '',
  129              default_tag:atom).  130
  131default_json_dict_options(
  132    json_options(null, true, false, error, string, '', _)).
  133
  134
  135                 /*******************************
  136                 *       MAP TO/FROM TEXT       *
  137                 *******************************/
 atom_json_term(?Atom, ?JSONTerm, +Options) is det
Convert between textual representation and a JSON term. In write mode (JSONTerm to Atom), the option
as(Type)
defines the output type, which is one of atom (default), string, codes or chars.
  148atom_json_term(Atom, Term, Options) :-
  149    ground(Atom),
  150    !,
  151    setup_call_cleanup(
  152        open_string(Atom, In),
  153        json_read(In, Term, Options),
  154        close(In)).
  155atom_json_term(Result, Term, Options) :-
  156    select_option(as(Type), Options, Options1, atom),
  157    (   type_term(Type, Result, Out)
  158    ->  true
  159    ;   must_be(oneof([atom,string,codes,chars]), Type)
  160    ),
  161    with_output_to(Out,
  162                   json_write(current_output, Term, Options1)).
  163
  164type_term(atom,   Result, atom(Result)).
  165type_term(string, Result, string(Result)).
  166type_term(codes,  Result, codes(Result)).
  167type_term(chars,  Result, chars(Result)).
  168
  169
  170                 /*******************************
  171                 *           READING            *
  172                 *******************************/
 json_read(+Stream, -Term) is det
 json_read(+Stream, -Term, +Options) is det
Read next JSON value from Stream into a Prolog term. The canonical representation for Term is:

Here is a complete example in JSON and its corresponding Prolog term.

{ "name":"Demo term",
  "created": {
    "day":null,
    "month":"December",
    "year":2007
  },
  "confirmed":true,
  "members":[1,2,3]
}
json([ name='Demo term',
       created=json([day= @null, month='December', year=2007]),
       confirmed= @true,
       members=[1, 2, 3]
     ])

The following options are processed:

null(+NullTerm)
Term used to represent JSON null. Default @(null)
true(+TrueTerm)
Term used to represent JSON true. Default @(true)
false(+FalseTerm)
Term used to represent JSON false. Default @(false)
end_of_file(+ErrorOrTerm)
If end of file is reached after skipping white space but before any input is processed take the following action (default error):
  • If ErrorOrTerm == error, throw an unexpected end of file syntax error
  • Otherwise return ErrorOrTerm.

Returning an status term is required to process Concatenated JSON. Suggested values are @(eof) or end_of_file.

value_string_as(+Type)
Prolog type used for strings used as value. Default is atom. The alternative is string, producing a packed string object. Please note that codes or chars would produce ambiguous output and are therefore not supported.
See also
- json_read_dict/3 to read a JSON term using the version 7 extended data types.
  247json_read(Stream, Term) :-
  248    default_json_options(Options),
  249    (   json_value_top(Stream, Term, Options)
  250    ->  true
  251    ;   syntax_error(illegal_json, Stream)
  252    ).
  253json_read(Stream, Term, Options) :-
  254    make_json_options(Options, OptionTerm, _RestOptions),
  255    (   json_value_top(Stream, Term, OptionTerm)
  256    ->  true
  257    ;   syntax_error(illegal_json, Stream)
  258    ).
  259
  260json_value_top(Stream, Term, Options) :-
  261    stream_property(Stream, type(binary)),
  262    !,
  263    setup_call_cleanup(
  264        set_stream(Stream, encoding(utf8)),
  265        json_value_top_(Stream, Term, Options),
  266        set_stream(Stream, type(binary))).
  267json_value_top(Stream, Term, Options) :-
  268    json_value_top_(Stream, Term, Options).
  269
  270json_value_top_(Stream, Term, Options) :-
  271    get_code(Stream, C0),
  272    ws(C0, Stream, C1),
  273    (   C1 == -1
  274    ->  json_options_end_of_file(Options, Action),
  275        (   Action == error
  276        ->  syntax_error(unexpected_end_of_file, Stream)
  277        ;   Term = Action
  278        )
  279    ;   json_term_top(C1, Stream, Term, Options)
  280    ).
  281
  282json_value(Stream, Term, Next, Options) :-
  283    get_code(Stream, C0),
  284    ws(C0, Stream, C1),
  285    (   C1 == -1
  286    ->  syntax_error(unexpected_end_of_file, Stream)
  287    ;   json_term(C1, Stream, Term, Next, Options)
  288    ).
  289
  290json_term(C0, Stream, JSON, Next, Options) :-
  291    json_term_top(C0, Stream, JSON, Options),
  292    get_code(Stream, Next).
  293
  294json_term_top(0'{, Stream, json(Pairs), Options) :-
  295    !,
  296    ws(Stream, C),
  297    json_pairs(C, Stream, Pairs, Options).
  298json_term_top(0'[, Stream, Array, Options) :-
  299    !,
  300    ws(Stream, C),
  301    json_array(C, Stream, Array, Options).
  302json_term_top(0'", Stream, String, Options) :-
  303    !,
  304    get_code(Stream, C1),
  305    json_string_codes(C1, Stream, Codes),
  306    json_options_value_string_as(Options, Type),
  307    codes_to_type(Type, Codes, String).
  308json_term_top(0'-, Stream, Number, _Options) :-
  309    !,
  310    json_read_number(Stream, 0'-, Number).
  311json_term_top(D, Stream, Number, _Options) :-
  312    between(0'0, 0'9, D),
  313    !,
  314    json_read_number(Stream, D, Number).
  315json_term_top(C, Stream, Constant, Options) :-
  316    json_read_constant(C, Stream, ID),
  317    json_constant(ID, Constant, Options).
  318
  319json_pairs(0'}, _, [], _) :- !.
  320json_pairs(C0, Stream, [Pair|Tail], Options) :-
  321    json_pair(C0, Stream, Pair, C, Options),
  322    ws(C, Stream, Next),
  323    (   Next == 0',
  324    ->  ws(Stream, C2),
  325        json_pairs(C2, Stream, Tail, Options)
  326    ;   Next == 0'}
  327    ->  Tail = []
  328    ;   syntax_error(illegal_object, Stream)
  329    ).
  330
  331json_pair(C0, Stream, Name=Value, Next, Options) :-
  332    json_string_as_atom(C0, Stream, Name),
  333    ws(Stream, C),
  334    C == 0':,
  335    json_value(Stream, Value, Next, Options).
  336
  337
  338json_array(0'], _, [], _) :- !.
  339json_array(C0, Stream, [Value|Tail], Options) :-
  340    json_term(C0, Stream, Value, C, Options),
  341    ws(C, Stream, Next),
  342    (   Next == 0',
  343    ->  ws(Stream, C1),
  344        json_array(C1, Stream, Tail, Options)
  345    ;   Next == 0']
  346    ->  Tail = []
  347    ;   syntax_error(illegal_array, Stream)
  348    ).
  349
  350codes_to_type(atom, Codes, Atom) :-
  351    atom_codes(Atom, Codes).
  352codes_to_type(string, Codes, Atom) :-
  353    string_codes(Atom, Codes).
  354codes_to_type(codes, Codes, Codes).
  355
  356json_string_as_atom(0'", Stream, Atom) :-
  357    get_code(Stream, C1),
  358    json_string_codes(C1, Stream, Codes),
  359    atom_codes(Atom, Codes).
  360
  361json_string_codes(0'", _, []) :- !.
  362json_string_codes(0'\\, Stream, [H|T]) :-
  363    !,
  364    get_code(Stream, C0),
  365    (   escape(C0, Stream, H)
  366    ->  true
  367    ;   syntax_error(illegal_string_escape, Stream)
  368    ),
  369    get_code(Stream, C1),
  370    json_string_codes(C1, Stream, T).
  371json_string_codes(-1, Stream, _) :-
  372    !,
  373    syntax_error(eof_in_string, Stream).
  374json_string_codes(C, Stream, [C|T]) :-
  375    get_code(Stream, C1),
  376    json_string_codes(C1, Stream, T).
  377
  378escape(0'", _, 0'") :- !.
  379escape(0'\\, _, 0'\\) :- !.
  380escape(0'/, _, 0'/) :- !.
  381escape(0'b, _, 0'\b) :- !.
  382escape(0'f, _, 0'\f) :- !.
  383escape(0'n, _, 0'\n) :- !.
  384escape(0'r, _, 0'\r) :- !.
  385escape(0't, _, 0'\t) :- !.
  386escape(0'u, Stream, C) :-
  387    get_XXXX(Stream, H),
  388    (   hi_surrogate(H)
  389    ->  get_surrogate_tail(Stream, H, C)
  390    ;   C = H
  391    ).
  392
  393get_XXXX(Stream, C) :-
  394    get_xdigit(Stream, D1),
  395    get_xdigit(Stream, D2),
  396    get_xdigit(Stream, D3),
  397    get_xdigit(Stream, D4),
  398    C is D1<<12+D2<<8+D3<<4+D4.
  399
  400get_xdigit(Stream, D) :-
  401    get_code(Stream, C),
  402    code_type(C, xdigit(D)),
  403    !.
  404get_xdigit(Stream, _) :-
  405    syntax_error(hexdigit_expected, Stream).
  406
  407get_surrogate_tail(Stream, Hi, Codepoint) :-
  408    (   get_code(Stream, 0'\\),
  409        get_code(Stream, 0'u),
  410        get_XXXX(Stream, Lo),
  411        surrogate([Hi, Lo], Codepoint)
  412    ->  true
  413    ;   syntax_error(illegal_surrogate_pair, Stream)
  414    ).
  415
  416
  417hi_surrogate(C) :-
  418    C >= 0xD800, C < 0xDC00.
  419
  420lo_surrogate(C) :-
  421    C >= 0xDC00, C < 0xE000.
  422
  423surrogate([Hi, Lo], Codepoint) :-
  424    hi_surrogate(Hi),
  425    lo_surrogate(Lo),
  426    Codepoint is (Hi - 0xD800) * 0x400 + (Lo - 0xDC00) + 0x10000.
  427
  428json_read_constant(0't, Stream, true) :-
  429    !,
  430    must_see(`rue`, Stream, true).
  431json_read_constant(0'f, Stream, false) :-
  432    !,
  433    must_see(`alse`, Stream, false).
  434json_read_constant(0'n, Stream, null) :-
  435    !,
  436    must_see(`ull`, Stream, null).
  437
  438must_see([], _Stream, _).
  439must_see([H|T], Stream, Name) :-
  440    get_code(Stream, C),
  441    (   C == H
  442    ->  true
  443    ;   syntax_error(json_expected(Name), Stream)
  444    ),
  445    must_see(T, Stream, Name).
  446
  447json_constant(true, Constant, Options) :-
  448    !,
  449    json_options_true(Options, Constant).
  450json_constant(false, Constant, Options) :-
  451    !,
  452    json_options_false(Options, Constant).
  453json_constant(null, Constant, Options) :-
  454    !,
  455    json_options_null(Options, Constant).
 ws(+Stream, -Next) is det
 ws(+C0, +Stream, -Next)
Skip white space on the Stream, returning the first non-ws character. Also skips // ... comments.
  463ws(Stream, Next) :-
  464    get_code(Stream, C0),
  465    json_skip_ws(Stream, C0, Next).
  466
  467ws(C0, Stream, Next) :-
  468    json_skip_ws(Stream, C0, Next).
  469
  470syntax_error(Message, Stream) :-
  471    stream_error_context(Stream, Context),
  472    throw(error(syntax_error(json(Message)), Context)).
  473
  474stream_error_context(Stream, stream(Stream, Line, LinePos, CharNo)) :-
  475    stream_pair(Stream, Read, _),
  476    character_count(Read, CharNo),
  477    line_position(Read, LinePos),
  478    line_count(Read, Line).
  479
  480
  481                 /*******************************
  482                 *          JSON OUTPUT         *
  483                 *******************************/
 json_write_string(+Stream, +Text) is det
Write a JSON string to Stream. Stream must be opened in a Unicode capable encoding, typically UTF-8.
  490% foreign json_write_string/2.
 json_write_indent(+Stream, +Indent, +TabDistance) is det
Newline and indent to Indent. A Newline is only written if line_position(Stream, Pos) is not 0. Then it writes Indent // TabDistance tab characters and Indent mode TabDistance spaces.
  498% foreign json_write_indent/3.
 json_write(+Stream, +Term) is det
 json_write(+Stream, +Term, +Options) is det
Write a JSON term to Stream. The JSON object is of the same format as produced by json_read/2, though we allow for some more flexibility with regard to pairs in objects. All of Name=Value, Name-Value and Name(Value) produce the same output.

Values can be of the form #(Term), which causes Term to be stringified if it is not an atom or string. Stringification is based on term_string/2.

Rational numbers are emitted as floating point numbers. The hook json_write_hook/4 can be used to realize domain specific alternatives.

The version 7 dict type is supported as well. Optionally, if the dict has a tag, a property "type":"tag" can be added to the object. This behaviour can be controlled using the tag option (see below). For example:

?- json_write(current_output, point{x:1,y:2}).
{
  "x":1,
  "y":2
}
?- json_write(current_output, point{x:1,y:2}, [tag(type)]).
{
  "type":"point",
  "x":1,
  "y":2
}

In addition to the options recognised by json_read/3, we process the following options are recognised:

width(+Width)
Width in which we try to format the result. Too long lines switch from horizontal to vertical layout for better readability. If performance is critical and human readability is not an issue use Width = 0, which causes a single-line output.
step(+Step)
Indentation increnment for next level. Default is 2.
tab(+TabDistance)
Distance between tab-stops. If equal to Step, layout is generated with one tab per level.
serialize_unknown(+Boolean)
If true (default false), serialize unknown terms and print them as a JSON string. The default raises a type error. Note that this option only makes sense if you can guarantee that the passed value is not an otherwise valid Prolog reporesentation of a Prolog term.

If a string is emitted, the sequence </ is emitted as <\/. This is valid JSON syntax which ensures that JSON objects can be safely embedded into an HTML <script> element.

 json_write_hook(+Term, +Stream, +State, +Options) is semidet
Hook that can be used to emit a JSON representation for Term to Stream. If the predicate succeeds it must have written a valid JSON data element and if it fails it may not have produced any output. This facility may be used to map arbitrary Prolog terms to JSON. It was added to manage the precision with which floating point numbers are emitted.

Note that this hook is shared by all users of this library. It is generally adviced to map a unique compound term to avoid interference with normal output.

Arguments:
State- and Options are opaque handles to the current output state and settings. Future versions may provide documented access to these terms. Currently it is adviced to ignore these arguments.
 json_dict_pairs(+Dict, -Pairs) is semidet
This hook may be used to order the keys of an object. If it fails, dict_pairs/3 is used which produces an ordered list of keys.
  589:- record json_write_state(indent:nonneg = 0,
  590                           step:positive_integer = 2,
  591                           tab:positive_integer = 8,
  592                           width:nonneg = 72,
  593                           serialize_unknown:boolean = false
  594                          ).  595
  596json_write(Stream, Term) :-
  597    json_write(Stream, Term, []).
  598json_write(Stream, Term, Options) :-
  599    make_json_write_state(Options, State, Options1),
  600    make_json_options(Options1, OptionTerm, _RestOptions),
  601    json_write_term(Term, Stream, State, OptionTerm).
  602
  603json_write_term(Var, _, _, _) :-
  604    var(Var),
  605    !,
  606    instantiation_error(Var).
  607json_write_term(json(Pairs), Stream, State, Options) :-
  608    !,
  609    json_write_object(Pairs, Stream, State, Options).
  610json_write_term(Dict, Stream, State, Options) :-
  611    is_dict(Dict, Tag),
  612    !,
  613    json_pairs(Dict, Pairs0),
  614    (   nonvar(Tag),
  615        json_options_tag(Options, Name),
  616        Name \== ''
  617    ->  Pairs = [Name-Tag|Pairs0]
  618    ;   Pairs = Pairs0
  619    ),
  620    json_write_object(Pairs, Stream, State, Options).
  621json_write_term(List, Stream, State, Options) :-
  622    is_list(List),
  623    !,
  624    space_if_not_at_left_margin(Stream, State),
  625    write(Stream, '['),
  626    (   json_write_state_width(State, Width),
  627        (   Width == 0
  628        ->  true
  629        ;   json_write_state_indent(State, Indent),
  630            json_print_length(List, Options, Width, Indent, _)
  631        )
  632    ->  set_width_of_json_write_state(0, State, State2),
  633        write_array_hor(List, Stream, State2, Options),
  634        write(Stream, ']')
  635    ;   step_indent(State, State2),
  636        write_array_ver(List, Stream, State2, Options),
  637        indent(Stream, State),
  638        write(Stream, ']')
  639    ).
  640
  641json_write_term(Term, Stream, State, Options) :-
  642    json_write_hook(Term, Stream, State, Options),
  643    !.
  644json_write_term(Number, Stream, _State, _Options) :-
  645    number(Number),
  646    !,
  647    (   float(Number)
  648    ->  write(Stream, Number)
  649    ;   integer(Number)
  650    ->  write(Stream, Number)
  651    ;   Float is float(Number)              % rational number
  652    ->  write(Stream, Float)
  653    ).
  654json_write_term(True, Stream, _State, Options) :-
  655    json_options_true(Options, True),
  656    !,
  657    write(Stream, true).
  658json_write_term(False, Stream, _State, Options) :-
  659    json_options_false(Options, False),
  660    !,
  661    write(Stream, false).
  662json_write_term(Null, Stream, _State, Options) :-
  663    json_options_null(Options, Null),
  664    !,
  665    write(Stream, null).
  666json_write_term(#(Text), Stream, _State, _Options) :-
  667    !,
  668    (   (   atom(Text)
  669        ;   string(Text)
  670        )
  671    ->  json_write_string(Stream, Text)
  672    ;   term_string(Text, String),
  673        json_write_string(Stream, String)
  674    ).
  675json_write_term(String, Stream, _State, _Options) :-
  676    atom(String),
  677    !,
  678    json_write_string(Stream, String).
  679json_write_term(String, Stream, _State, _Options) :-
  680    string(String),
  681    !,
  682    json_write_string(Stream, String).
  683json_write_term(AnyTerm, Stream, State, _Options) :-
  684    (   json_write_state_serialize_unknown(State, true)
  685    ->  term_string(AnyTerm, String),
  686        json_write_string(Stream, String)
  687    ;   type_error(json_term, AnyTerm)
  688    ).
  689
  690json_pairs(Dict, Pairs) :-
  691    json_dict_pairs(Dict, Pairs),
  692    !.
  693json_pairs(Dict, Pairs) :-
  694    dict_pairs(Dict, _, Pairs).
  695
  696json_write_object(Pairs, Stream, State, Options) :-
  697    space_if_not_at_left_margin(Stream, State),
  698    write(Stream, '{'),
  699    (   json_write_state_width(State, Width),
  700        (   Width == 0
  701        ->  true
  702        ;   json_write_state_indent(State, Indent),
  703            json_print_length(json(Pairs), Options, Width, Indent, _)
  704        )
  705    ->  set_width_of_json_write_state(0, State, State2),
  706        write_pairs_hor(Pairs, Stream, State2, Options),
  707        write(Stream, '}')
  708    ;   step_indent(State, State2),
  709        write_pairs_ver(Pairs, Stream, State2, Options),
  710        indent(Stream, State),
  711        write(Stream, '}')
  712    ).
  713
  714
  715write_pairs_hor([], _, _, _).
  716write_pairs_hor([H|T], Stream, State, Options) :-
  717    json_pair(H, Name, Value),
  718    json_write_string(Stream, Name),
  719    write(Stream, ':'),
  720    json_write_term(Value, Stream, State, Options),
  721    (   T == []
  722    ->  true
  723    ;   (   json_write_state_width(State, 0)
  724        ->  write(Stream, ',')
  725        ;   write(Stream, ', ')
  726        ),
  727        write_pairs_hor(T, Stream, State, Options)
  728    ).
  729
  730write_pairs_ver([], _, _, _).
  731write_pairs_ver([H|T], Stream, State, Options) :-
  732    indent(Stream, State),
  733    json_pair(H, Name, Value),
  734    json_write_string(Stream, Name),
  735    write(Stream, ':'),
  736    json_write_term(Value, Stream, State, Options),
  737    (   T == []
  738    ->  true
  739    ;   write(Stream, ','),
  740        write_pairs_ver(T, Stream, State, Options)
  741    ).
  742
  743
  744json_pair(Var, _, _) :-
  745    var(Var),
  746    !,
  747    instantiation_error(Var).
  748json_pair(Name=Value, Name, Value) :- !.
  749json_pair(Name-Value, Name, Value) :- !.
  750json_pair(NameValue, Name, Value) :-
  751    compound(NameValue),
  752    NameValue =.. [Name, Value],
  753    !.
  754json_pair(Pair, _, _) :-
  755    type_error(json_pair, Pair).
  756
  757
  758write_array_hor([], _, _, _).
  759write_array_hor([H|T], Stream, State, Options) :-
  760    json_write_term(H, Stream, State, Options),
  761    (   T == []
  762    ->  write(Stream, ' ')
  763    ;   write(Stream, ', '),
  764        write_array_hor(T, Stream, State, Options)
  765    ).
  766
  767write_array_ver([], _, _, _).
  768write_array_ver([H|T], Stream, State, Options) :-
  769    indent(Stream, State),
  770    json_write_term(H, Stream, State, Options),
  771    (   T == []
  772    ->  true
  773    ;   write(Stream, ','),
  774        write_array_ver(T, Stream, State, Options)
  775    ).
  776
  777
  778indent(Stream, State) :-
  779    json_write_state_indent(State, Indent),
  780    json_write_state_tab(State, Tab),
  781    json_write_indent(Stream, Indent, Tab).
  782
  783step_indent(State0, State) :-
  784    json_write_state_indent(State0, Indent),
  785    json_write_state_step(State0, Step),
  786    NewIndent is Indent+Step,
  787    set_indent_of_json_write_state(NewIndent, State0, State).
  788
  789space_if_not_at_left_margin(Stream, State) :-
  790    stream_pair(Stream, _, Write),
  791    line_position(Write, LinePos),
  792    (   LinePos == 0
  793    ;   json_write_state_indent(State, LinePos)
  794    ),
  795    !.
  796space_if_not_at_left_margin(Stream, _) :-
  797    put_char(Stream, ' ').
 json_print_length(+Value, +Options, +Max, +Len0, +Len) is semidet
True if Len-Len0 is the print-length of Value on a single line and Len-Len0 =< Max.
To be done
- Escape sequences in strings are not considered.
  807json_print_length(Var, _, _, _, _) :-
  808    var(Var),
  809    !,
  810    instantiation_error(Var).
  811json_print_length(json(Pairs), Options, Max, Len0, Len) :-
  812    !,
  813    Len1 is Len0 + 2,
  814    Len1 =< Max,
  815    must_be(list, Pairs),
  816    pairs_print_length(Pairs, Options, Max, Len1, Len).
  817json_print_length(Dict, Options, Max, Len0, Len) :-
  818    is_dict(Dict),
  819    !,
  820    dict_pairs(Dict, _Tag, Pairs),
  821    Len1 is Len0 + 2,
  822    Len1 =< Max,
  823    pairs_print_length(Pairs, Options, Max, Len1, Len).
  824json_print_length(Array, Options, Max, Len0, Len) :-
  825    is_list(Array),
  826    !,
  827    Len1 is Len0 + 2,
  828    Len1 =< Max,
  829    array_print_length(Array, Options, Max, Len1, Len).
  830json_print_length(Null, Options, Max, Len0, Len) :-
  831    json_options_null(Options, Null),
  832    !,
  833    Len is Len0 + 4,
  834    Len =< Max.
  835json_print_length(False, Options, Max, Len0, Len) :-
  836    json_options_false(Options, False),
  837    !,
  838    Len is Len0 + 5,
  839    Len =< Max.
  840json_print_length(True, Options, Max, Len0, Len) :-
  841    json_options_true(Options, True),
  842    !,
  843    Len is Len0 + 4,
  844    Len =< Max.
  845json_print_length(Number, _Options, Max, Len0, Len) :-
  846    number(Number),
  847    !,
  848    write_length(Number, AL, []),
  849    Len is Len0 + AL,
  850    Len =< Max.
  851json_print_length(@(Id), _Options, Max, Len0, Len) :-
  852    atom(Id),
  853    !,
  854    atom_length(Id, IdLen),
  855    Len is Len0+IdLen,
  856    Len =< Max.
  857json_print_length(String, _Options, Max, Len0, Len) :-
  858    string_len(String, Len0, Len),
  859    !,
  860    Len =< Max.
  861json_print_length(AnyTerm, _Options, Max, Len0, Len) :-
  862    write_length(AnyTerm, AL, []),          % will be serialized
  863    Len is Len0 + AL+2,
  864    Len =< Max.
  865
  866pairs_print_length([], _, _, Len, Len).
  867pairs_print_length([H|T], Options, Max, Len0, Len) :-
  868    pair_len(H, Options, Max, Len0, Len1),
  869    (   T == []
  870    ->  Len = Len1
  871    ;   Len2 is Len1 + 2,
  872        Len2 =< Max,
  873        pairs_print_length(T, Options, Max, Len2, Len)
  874    ).
  875
  876pair_len(Pair, Options, Max, Len0, Len) :-
  877    compound(Pair),
  878    pair_nv(Pair, Name, Value),
  879    !,
  880    string_len(Name, Len0, Len1),
  881    Len2 is Len1+2,
  882    Len2 =< Max,
  883    json_print_length(Value, Options, Max, Len2, Len).
  884pair_len(Pair, _Options, _Max, _Len0, _Len) :-
  885    type_error(pair, Pair).
  886
  887pair_nv(Name=Value, Name, Value) :- !.
  888pair_nv(Name-Value, Name, Value) :- !.
  889pair_nv(Term, Name, Value) :-
  890    compound_name_arguments(Term, Name, [Value]).
  891
  892array_print_length([], _, _, Len, Len).
  893array_print_length([H|T], Options, Max, Len0, Len) :-
  894    json_print_length(H, Options, Max, Len0, Len1),
  895    (   T == []
  896    ->  Len = Len1
  897    ;   Len2 is Len1+2,
  898        Len2 =< Max,
  899        array_print_length(T, Options, Max, Len2, Len)
  900    ).
  901
  902string_len(String, Len0, Len) :-
  903    atom(String),
  904    !,
  905    atom_length(String, AL),
  906    Len is Len0 + AL + 2.
  907string_len(String, Len0, Len) :-
  908    string(String),
  909    !,
  910    string_length(String, AL),
  911    Len is Len0 + AL + 2.
  912
  913
  914                 /*******************************
  915                 *             TEST             *
  916                 *******************************/
 is_json_term(@Term) is semidet
 is_json_term(@Term, +Options) is semidet
True if Term is a json term. Options are the same as for json_read/2, defining the Prolog representation for the JSON true, false and null constants.
  925is_json_term(Term) :-
  926    default_json_options(Options),
  927    is_json_term2(Options, Term).
  928
  929is_json_term(Term, Options) :-
  930    make_json_options(Options, OptionTerm, _RestOptions),
  931    is_json_term2(OptionTerm, Term).
  932
  933is_json_term2(_, Var) :-
  934    var(Var), !, fail.
  935is_json_term2(Options, json(Pairs)) :-
  936    !,
  937    is_list(Pairs),
  938    maplist(is_json_pair(Options), Pairs).
  939is_json_term2(Options, List) :-
  940    is_list(List),
  941    !,
  942    maplist(is_json_term2(Options), List).
  943is_json_term2(_, Primitive) :-
  944    atomic(Primitive),
  945    !.           % atom, string or number
  946is_json_term2(Options, True) :-
  947    json_options_true(Options, True).
  948is_json_term2(Options, False) :-
  949    json_options_false(Options, False).
  950is_json_term2(Options, Null) :-
  951    json_options_null(Options, Null).
  952
  953is_json_pair(_, Var) :-
  954    var(Var), !, fail.
  955is_json_pair(Options, Name=Value) :-
  956    atom(Name),
  957    is_json_term2(Options, Value).
  958
  959                 /*******************************
  960                 *         DICT SUPPORT         *
  961                 *******************************/
 json_read_dict(+Stream, -Dict) is det
 json_read_dict(+Stream, -Dict, +Options) is det
Read a JSON object, returning objects as a dicts. The representation depends on the options, where the default is:

The predicate json_read_dict/3 processes the same options as json_read/3, but with different defaults. In addition, it processes the tag option. See json_read/3 for details about the shared options.

tag(+Name)
When converting to/from a dict, map the indicated JSON attribute to the dict tag. No mapping is performed if Name is the empty atom ('', default). See json_read_dict/2 and json_write_dict/2.
default_tag(+Tag)
Provide the default tag if the above tag option does not apply.
null(+NullTerm)
Default the atom null.
true(+TrueTerm)
Default the atom true.
false(+FalseTerm)
Default the atom false
end_of_file(+ErrorOrTerm)
Action on reading end-of-file. See json_read/3 for details.
value_string_as(+Type)
Prolog type used for strings used as value. Default is string. The alternative is atom, producing a packed string object.
 1002json_read_dict(Stream, Dict) :-
 1003    json_read_dict(Stream, Dict, []).
 1004
 1005json_read_dict(Stream, Dict, Options) :-
 1006    make_json_dict_options(Options, OptionTerm, _RestOptions),
 1007    (   json_value_top(Stream, Term, OptionTerm)
 1008    ->  true
 1009    ;   syntax_error(illegal_json, Stream)
 1010    ),
 1011    term_to_dict(Term, Dict, OptionTerm).
 1012
 1013term_to_dict(json(Pairs), Dict, Options) :-
 1014    !,
 1015    (   json_options_tag(Options, TagName),
 1016        Tag \== '',
 1017        select(TagName = Tag0, Pairs, NVPairs),
 1018        to_atom(Tag0, Tag)
 1019    ->  json_dict_pairs(NVPairs, DictPairs, Options)
 1020    ;   json_options_default_tag(Options, DefTag),
 1021        (   var(DefTag)
 1022        ->  true
 1023        ;   Tag = DefTag
 1024        ),
 1025        json_dict_pairs(Pairs, DictPairs, Options)
 1026    ),
 1027    dict_create(Dict, Tag, DictPairs).
 1028term_to_dict(Value0, Value, _Options) :-
 1029    atomic(Value0), Value0 \== [],
 1030    !,
 1031    Value = Value0.
 1032term_to_dict(List0, List, Options) :-
 1033    is_list(List0),
 1034    !,
 1035    terms_to_dicts(List0, List, Options).
 1036term_to_dict(Special, Special, Options) :-
 1037    (   json_options_true(Options, Special)
 1038    ;   json_options_false(Options, Special)
 1039    ;   json_options_null(Options, Special)
 1040    ;   json_options_end_of_file(Options, Special)
 1041    ),
 1042    !.
 1043
 1044json_dict_pairs([], [], _).
 1045json_dict_pairs([Name=Value0|T0], [Name=Value|T], Options) :-
 1046    term_to_dict(Value0, Value, Options),
 1047    json_dict_pairs(T0, T, Options).
 1048
 1049terms_to_dicts([], [], _).
 1050terms_to_dicts([Value0|T0], [Value|T], Options) :-
 1051    term_to_dict(Value0, Value, Options),
 1052    terms_to_dicts(T0, T, Options).
 1053
 1054to_atom(Tag, Atom) :-
 1055    string(Tag),
 1056    !,
 1057    atom_string(Atom, Tag).
 1058to_atom(Atom, Atom) :-
 1059    atom(Atom).
 json_write_dict(+Stream, +Dict) is det
 json_write_dict(+Stream, +Dict, +Options) is det
Write a JSON term, represented using dicts. This is the same as json_write/3, but assuming the default representation of JSON objects as dicts.
 1068json_write_dict(Stream, Dict) :-
 1069    json_write_dict(Stream, Dict, []).
 1070
 1071json_write_dict(Stream, Dict, Options) :-
 1072    make_json_write_state(Options, State, Options1),
 1073    make_json_dict_options(Options1, OptionTerm, _RestOptions),
 1074    json_write_term(Dict, Stream, State, OptionTerm).
 1075
 1076
 1077make_json_dict_options(Options, Record, RestOptions) :-
 1078    default_json_dict_options(Record0),
 1079    set_json_options_fields(Options, Record0, Record, RestOptions).
 atom_json_dict(+Atom, -JSONDict, +Options) is det
atom_json_dict(-Text, +JSONDict, +Options) is det
Convert between textual representation and a JSON term represented as a dict. Options are as for json_read/3. In write mode, the addtional option
as(Type)
defines the output type, which is one of atom, string or codes.
 1092atom_json_dict(Atom, Term, Options) :-
 1093    ground(Atom),
 1094    !,
 1095    setup_call_cleanup(
 1096        open_string(Atom, In),
 1097        json_read_dict(In, Term, Options),
 1098        close(In)).
 1099atom_json_dict(Result, Term, Options) :-
 1100    select_option(as(Type), Options, Options1, atom),
 1101    (   type_term(Type, Result, Out)
 1102    ->  true
 1103    ;   must_be(oneof([atom,string,codes]), Type)
 1104    ),
 1105    with_output_to(Out,
 1106                   json_write_dict(current_output, Term, Options1)).
 1107
 1108
 1109                 /*******************************
 1110                 *           MESSAGES           *
 1111                 *******************************/
 1112
 1113:- multifile
 1114    prolog:error_message/3. 1115
 1116prolog:error_message(syntax_error(json(Id))) -->
 1117    [ 'JSON syntax error: ' ],
 1118    json_syntax_error(Id).
 1119
 1120json_syntax_error(illegal_comment) -->
 1121    [ 'Illegal comment' ].
 1122json_syntax_error(illegal_string_escape) -->
 1123    [ 'Illegal escape sequence in string' ].
 1124json_syntax_error(illegal_surrogate_pair) -->
 1125    [ 'Illegal escaped surrogate pair in string' ]