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    ;   write(Stream, ', '),
  724        write_pairs_hor(T, Stream, State, Options)
  725    ).
  726
  727write_pairs_ver([], _, _, _).
  728write_pairs_ver([H|T], Stream, State, Options) :-
  729    indent(Stream, State),
  730    json_pair(H, Name, Value),
  731    json_write_string(Stream, Name),
  732    write(Stream, ':'),
  733    json_write_term(Value, Stream, State, Options),
  734    (   T == []
  735    ->  true
  736    ;   write(Stream, ','),
  737        write_pairs_ver(T, Stream, State, Options)
  738    ).
  739
  740
  741json_pair(Var, _, _) :-
  742    var(Var),
  743    !,
  744    instantiation_error(Var).
  745json_pair(Name=Value, Name, Value) :- !.
  746json_pair(Name-Value, Name, Value) :- !.
  747json_pair(NameValue, Name, Value) :-
  748    compound(NameValue),
  749    NameValue =.. [Name, Value],
  750    !.
  751json_pair(Pair, _, _) :-
  752    type_error(json_pair, Pair).
  753
  754
  755write_array_hor([], _, _, _).
  756write_array_hor([H|T], Stream, State, Options) :-
  757    json_write_term(H, Stream, State, Options),
  758    (   T == []
  759    ->  write(Stream, ' ')
  760    ;   write(Stream, ', '),
  761        write_array_hor(T, Stream, State, Options)
  762    ).
  763
  764write_array_ver([], _, _, _).
  765write_array_ver([H|T], Stream, State, Options) :-
  766    indent(Stream, State),
  767    json_write_term(H, Stream, State, Options),
  768    (   T == []
  769    ->  true
  770    ;   write(Stream, ','),
  771        write_array_ver(T, Stream, State, Options)
  772    ).
  773
  774
  775indent(Stream, State) :-
  776    json_write_state_indent(State, Indent),
  777    json_write_state_tab(State, Tab),
  778    json_write_indent(Stream, Indent, Tab).
  779
  780step_indent(State0, State) :-
  781    json_write_state_indent(State0, Indent),
  782    json_write_state_step(State0, Step),
  783    NewIndent is Indent+Step,
  784    set_indent_of_json_write_state(NewIndent, State0, State).
  785
  786space_if_not_at_left_margin(Stream, State) :-
  787    stream_pair(Stream, _, Write),
  788    line_position(Write, LinePos),
  789    (   LinePos == 0
  790    ;   json_write_state_indent(State, LinePos)
  791    ),
  792    !.
  793space_if_not_at_left_margin(Stream, _) :-
  794    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.
  804json_print_length(Var, _, _, _, _) :-
  805    var(Var),
  806    !,
  807    instantiation_error(Var).
  808json_print_length(json(Pairs), Options, Max, Len0, Len) :-
  809    !,
  810    Len1 is Len0 + 2,
  811    Len1 =< Max,
  812    must_be(list, Pairs),
  813    pairs_print_length(Pairs, Options, Max, Len1, Len).
  814json_print_length(Dict, Options, Max, Len0, Len) :-
  815    is_dict(Dict),
  816    !,
  817    dict_pairs(Dict, _Tag, Pairs),
  818    Len1 is Len0 + 2,
  819    Len1 =< Max,
  820    pairs_print_length(Pairs, Options, Max, Len1, Len).
  821json_print_length(Array, Options, Max, Len0, Len) :-
  822    is_list(Array),
  823    !,
  824    Len1 is Len0 + 2,
  825    Len1 =< Max,
  826    array_print_length(Array, Options, Max, Len1, Len).
  827json_print_length(Null, Options, Max, Len0, Len) :-
  828    json_options_null(Options, Null),
  829    !,
  830    Len is Len0 + 4,
  831    Len =< Max.
  832json_print_length(False, Options, Max, Len0, Len) :-
  833    json_options_false(Options, False),
  834    !,
  835    Len is Len0 + 5,
  836    Len =< Max.
  837json_print_length(True, Options, Max, Len0, Len) :-
  838    json_options_true(Options, True),
  839    !,
  840    Len is Len0 + 4,
  841    Len =< Max.
  842json_print_length(Number, _Options, Max, Len0, Len) :-
  843    number(Number),
  844    !,
  845    write_length(Number, AL, []),
  846    Len is Len0 + AL,
  847    Len =< Max.
  848json_print_length(@(Id), _Options, Max, Len0, Len) :-
  849    atom(Id),
  850    !,
  851    atom_length(Id, IdLen),
  852    Len is Len0+IdLen,
  853    Len =< Max.
  854json_print_length(String, _Options, Max, Len0, Len) :-
  855    string_len(String, Len0, Len),
  856    !,
  857    Len =< Max.
  858json_print_length(AnyTerm, _Options, Max, Len0, Len) :-
  859    write_length(AnyTerm, AL, []),          % will be serialized
  860    Len is Len0 + AL+2,
  861    Len =< Max.
  862
  863pairs_print_length([], _, _, Len, Len).
  864pairs_print_length([H|T], Options, Max, Len0, Len) :-
  865    pair_len(H, Options, Max, Len0, Len1),
  866    (   T == []
  867    ->  Len = Len1
  868    ;   Len2 is Len1 + 2,
  869        Len2 =< Max,
  870        pairs_print_length(T, Options, Max, Len2, Len)
  871    ).
  872
  873pair_len(Pair, Options, Max, Len0, Len) :-
  874    compound(Pair),
  875    pair_nv(Pair, Name, Value),
  876    !,
  877    string_len(Name, Len0, Len1),
  878    Len2 is Len1+2,
  879    Len2 =< Max,
  880    json_print_length(Value, Options, Max, Len2, Len).
  881pair_len(Pair, _Options, _Max, _Len0, _Len) :-
  882    type_error(pair, Pair).
  883
  884pair_nv(Name=Value, Name, Value) :- !.
  885pair_nv(Name-Value, Name, Value) :- !.
  886pair_nv(Term, Name, Value) :-
  887    compound_name_arguments(Term, Name, [Value]).
  888
  889array_print_length([], _, _, Len, Len).
  890array_print_length([H|T], Options, Max, Len0, Len) :-
  891    json_print_length(H, Options, Max, Len0, Len1),
  892    (   T == []
  893    ->  Len = Len1
  894    ;   Len2 is Len1+2,
  895        Len2 =< Max,
  896        array_print_length(T, Options, Max, Len2, Len)
  897    ).
  898
  899string_len(String, Len0, Len) :-
  900    atom(String),
  901    !,
  902    atom_length(String, AL),
  903    Len is Len0 + AL + 2.
  904string_len(String, Len0, Len) :-
  905    string(String),
  906    !,
  907    string_length(String, AL),
  908    Len is Len0 + AL + 2.
  909
  910
  911                 /*******************************
  912                 *             TEST             *
  913                 *******************************/
 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.
  922is_json_term(Term) :-
  923    default_json_options(Options),
  924    is_json_term2(Options, Term).
  925
  926is_json_term(Term, Options) :-
  927    make_json_options(Options, OptionTerm, _RestOptions),
  928    is_json_term2(OptionTerm, Term).
  929
  930is_json_term2(_, Var) :-
  931    var(Var), !, fail.
  932is_json_term2(Options, json(Pairs)) :-
  933    !,
  934    is_list(Pairs),
  935    maplist(is_json_pair(Options), Pairs).
  936is_json_term2(Options, List) :-
  937    is_list(List),
  938    !,
  939    maplist(is_json_term2(Options), List).
  940is_json_term2(_, Primitive) :-
  941    atomic(Primitive),
  942    !.           % atom, string or number
  943is_json_term2(Options, True) :-
  944    json_options_true(Options, True).
  945is_json_term2(Options, False) :-
  946    json_options_false(Options, False).
  947is_json_term2(Options, Null) :-
  948    json_options_null(Options, Null).
  949
  950is_json_pair(_, Var) :-
  951    var(Var), !, fail.
  952is_json_pair(Options, Name=Value) :-
  953    atom(Name),
  954    is_json_term2(Options, Value).
  955
  956                 /*******************************
  957                 *         DICT SUPPORT         *
  958                 *******************************/
 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.
  999json_read_dict(Stream, Dict) :-
 1000    json_read_dict(Stream, Dict, []).
 1001
 1002json_read_dict(Stream, Dict, Options) :-
 1003    make_json_dict_options(Options, OptionTerm, _RestOptions),
 1004    (   json_value_top(Stream, Term, OptionTerm)
 1005    ->  true
 1006    ;   syntax_error(illegal_json, Stream)
 1007    ),
 1008    term_to_dict(Term, Dict, OptionTerm).
 1009
 1010term_to_dict(json(Pairs), Dict, Options) :-
 1011    !,
 1012    (   json_options_tag(Options, TagName),
 1013        Tag \== '',
 1014        select(TagName = Tag0, Pairs, NVPairs),
 1015        to_atom(Tag0, Tag)
 1016    ->  json_dict_pairs(NVPairs, DictPairs, Options)
 1017    ;   json_options_default_tag(Options, DefTag),
 1018        (   var(DefTag)
 1019        ->  true
 1020        ;   Tag = DefTag
 1021        ),
 1022        json_dict_pairs(Pairs, DictPairs, Options)
 1023    ),
 1024    dict_create(Dict, Tag, DictPairs).
 1025term_to_dict(Value0, Value, _Options) :-
 1026    atomic(Value0), Value0 \== [],
 1027    !,
 1028    Value = Value0.
 1029term_to_dict(List0, List, Options) :-
 1030    is_list(List0),
 1031    !,
 1032    terms_to_dicts(List0, List, Options).
 1033term_to_dict(Special, Special, Options) :-
 1034    (   json_options_true(Options, Special)
 1035    ;   json_options_false(Options, Special)
 1036    ;   json_options_null(Options, Special)
 1037    ;   json_options_end_of_file(Options, Special)
 1038    ),
 1039    !.
 1040
 1041json_dict_pairs([], [], _).
 1042json_dict_pairs([Name=Value0|T0], [Name=Value|T], Options) :-
 1043    term_to_dict(Value0, Value, Options),
 1044    json_dict_pairs(T0, T, Options).
 1045
 1046terms_to_dicts([], [], _).
 1047terms_to_dicts([Value0|T0], [Value|T], Options) :-
 1048    term_to_dict(Value0, Value, Options),
 1049    terms_to_dicts(T0, T, Options).
 1050
 1051to_atom(Tag, Atom) :-
 1052    string(Tag),
 1053    !,
 1054    atom_string(Atom, Tag).
 1055to_atom(Atom, Atom) :-
 1056    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.
 1065json_write_dict(Stream, Dict) :-
 1066    json_write_dict(Stream, Dict, []).
 1067
 1068json_write_dict(Stream, Dict, Options) :-
 1069    make_json_write_state(Options, State, Options1),
 1070    make_json_dict_options(Options1, OptionTerm, _RestOptions),
 1071    json_write_term(Dict, Stream, State, OptionTerm).
 1072
 1073
 1074make_json_dict_options(Options, Record, RestOptions) :-
 1075    default_json_dict_options(Record0),
 1076    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.
 1089atom_json_dict(Atom, Term, Options) :-
 1090    ground(Atom),
 1091    !,
 1092    setup_call_cleanup(
 1093        open_string(Atom, In),
 1094        json_read_dict(In, Term, Options),
 1095        close(In)).
 1096atom_json_dict(Result, Term, Options) :-
 1097    select_option(as(Type), Options, Options1, atom),
 1098    (   type_term(Type, Result, Out)
 1099    ->  true
 1100    ;   must_be(oneof([atom,string,codes]), Type)
 1101    ),
 1102    with_output_to(Out,
 1103                   json_write_dict(current_output, Term, Options1)).
 1104
 1105
 1106                 /*******************************
 1107                 *           MESSAGES           *
 1108                 *******************************/
 1109
 1110:- multifile
 1111    prolog:error_message/3. 1112
 1113prolog:error_message(syntax_error(json(Id))) -->
 1114    [ 'JSON syntax error: ' ],
 1115    json_syntax_error(Id).
 1116
 1117json_syntax_error(illegal_comment) -->
 1118    [ 'Illegal comment' ].
 1119json_syntax_error(illegal_string_escape) -->
 1120    [ 'Illegal escape sequence in string' ].
 1121json_syntax_error(illegal_surrogate_pair) -->
 1122    [ 'Illegal escaped surrogate pair in string' ]