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-2025, 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            json/4                      % {|json(Var,...)||JSON|}
   53          ]).   54:- use_module(library(record)).   55:- use_module(library(error)).   56:- use_module(library(option)).   57:- use_module(library(lists)).   58:- use_module(library(apply)).   59:- use_module(library(quasi_quotations)).   60
   61:- use_foreign_library(foreign(json)).   62
   63:- multifile
   64    json_write_hook/4,                  % +Term, +Stream, +State, +Options
   65    json_dict_pairs/2.                  % +Dict, -Pairs
   66
   67:- predicate_options(json_read/3, 3,
   68                     [ null(ground),
   69                       true(ground),
   70                       false(ground),
   71                       end_of_file(ground),
   72                       value_string_as(oneof([atom,string])),
   73                       qqdict(list(compound(=(atom,var))))
   74                     ]).   75:- predicate_options(json_write/3, 3,
   76                     [ indent(nonneg),
   77                       step(positive_integer),
   78                       tab(positive_integer),
   79                       width(nonneg),
   80                       null(ground),
   81                       true(ground),
   82                       false(ground),
   83                       serialize_unknown(boolean)
   84                     ]).   85:- predicate_options(json_read_dict/3, 3,
   86                     [ tag(atom),
   87                       default_tag(atom),
   88                       pass_to(json_read/3, 3)
   89                     ]).   90:- predicate_options(json_write_dict/3, 3,
   91                     [ tag(atom),
   92                       pass_to(json_write/3, 3)
   93                     ]).   94:- predicate_options(is_json_term/2, 2,
   95                     [ null(ground),
   96                       true(ground),
   97                       false(ground)
   98                     ]).   99:- predicate_options(atom_json_term/3, 3,
  100                     [ as(oneof([atom,string,codes])),
  101                       pass_to(json_read/3, 3),
  102                       pass_to(json_write/3, 3)
  103                     ]).

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):

This module provides the json Quasi Quotation syntax that allows for embedding JSON documents in Prolog.

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. */
  129:- record json_options(
  130              null:ground = @(null),
  131              true:ground = @(true),
  132              false:ground = @(false),
  133              end_of_file:ground = error,
  134              value_string_as:oneof([atom,string]) = atom,
  135              tag:atom = '',
  136              default_tag:(var;atom) = '#',
  137              qqdict:list(compound(atom=var))).  138
  139default_json_dict_options(
  140    json_options(null, true, false, error, string, '', #, _)).
  141
  142
  143                 /*******************************
  144                 *       MAP TO/FROM TEXT       *
  145                 *******************************/
 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.
  156atom_json_term(Atom, Term, Options) :-
  157    ground(Atom),
  158    !,
  159    setup_call_cleanup(
  160        open_string(Atom, In),
  161        json_read(In, Term, Options),
  162        close(In)).
  163atom_json_term(Result, Term, Options) :-
  164    select_option(as(Type), Options, Options1, atom),
  165    (   type_term(Type, Result, Out)
  166    ->  true
  167    ;   must_be(oneof([atom,string,codes,chars]), Type)
  168    ),
  169    with_output_to(Out,
  170                   json_write(current_output, Term, Options1)).
  171
  172type_term(atom,   Result, atom(Result)).
  173type_term(string, Result, string(Result)).
  174type_term(codes,  Result, codes(Result)).
  175type_term(chars,  Result, chars(Result)).
  176
  177
  178                 /*******************************
  179                 *           READING            *
  180                 *******************************/
 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.
  255json_read(Stream, Term) :-
  256    default_json_options(Options),
  257    (   json_value_top(Stream, Term, Options)
  258    ->  true
  259    ;   syntax_error(illegal_json, Stream)
  260    ).
  261json_read(Stream, Term, Options) :-
  262    make_json_options(Options, OptionTerm, _RestOptions),
  263    (   json_value_top(Stream, Term, OptionTerm)
  264    ->  true
  265    ;   syntax_error(illegal_json, Stream)
  266    ).
  267
  268json_value_top(Stream, Term, Options) :-
  269    stream_property(Stream, type(binary)),
  270    !,
  271    setup_call_cleanup(
  272        set_stream(Stream, encoding(utf8)),
  273        json_value_top_(Stream, Term, Options),
  274        set_stream(Stream, type(binary))).
  275json_value_top(Stream, Term, Options) :-
  276    json_value_top_(Stream, Term, Options).
  277
  278json_value_top_(Stream, Term, Options) :-
  279    get_code(Stream, C0),
  280    ws(C0, Stream, C1),
  281    (   C1 == -1
  282    ->  json_options_end_of_file(Options, Action),
  283        (   Action == error
  284        ->  syntax_error(unexpected_end_of_file, Stream)
  285        ;   Term = Action
  286        )
  287    ;   json_term_top(C1, Stream, Term, Options)
  288    ).
  289
  290json_value(Stream, Term, Next, Options) :-
  291    get_code(Stream, C0),
  292    ws(C0, Stream, C1),
  293    (   C1 == -1
  294    ->  syntax_error(unexpected_end_of_file, Stream)
  295    ;   json_term(C1, Stream, Term, Next, Options)
  296    ).
  297
  298json_term(C0, Stream, JSON, Next, Options) :-
  299    json_term_top(C0, Stream, JSON, Options),
  300    get_code(Stream, Next).
  301
  302json_term_top(0'{, Stream, json(Pairs), Options) :-
  303    !,
  304    ws(Stream, C),
  305    json_pairs(C, Stream, Pairs, Options).
  306json_term_top(0'[, Stream, Array, Options) :-
  307    !,
  308    ws(Stream, C),
  309    json_array(C, Stream, Array, Options).
  310json_term_top(0'", Stream, String, Options) :-
  311    !,
  312    get_code(Stream, C1),
  313    json_string_codes(C1, Stream, Codes),
  314    json_options_value_string_as(Options, Type),
  315    codes_to_type(Type, Codes, String).
  316json_term_top(0'-, Stream, Number, _Options) :-
  317    !,
  318    json_read_number(Stream, 0'-, Number).
  319json_term_top(D, Stream, Number, _Options) :-
  320    between(0'0, 0'9, D),
  321    !,
  322    json_read_number(Stream, D, Number).
  323json_term_top(C, Stream, Constant, Options) :-
  324    json_read_constant(C, Stream, ID),
  325    !,
  326    json_constant(ID, Constant, Options).
  327json_term_top(C, Stream, Var, Options) :-
  328    code_type(C, prolog_var_start),
  329    json_options_qqdict(Options, QQDict),
  330    nonvar(QQDict),
  331    !,
  332    json_read_var_cont(Stream, Codes),
  333    atom_codes(Name, [C | Codes]),
  334    (   memberchk(Name=Var, QQDict)
  335    ->  true
  336    ;   syntax_error(non_existing_var(Name, QQDict), Stream)
  337    ).
  338
  339json_read_var_cont(Stream, [C | L]) :-
  340    peek_code(Stream, C),
  341    code_type(C, prolog_identifier_continue),
  342    !,
  343    get_code(Stream, C),
  344    json_read_var_cont(Stream, L).
  345json_read_var_cont(_, []).
  346
  347
  348json_pairs(0'}, _, [], _) :- !.
  349json_pairs(C0, Stream, [Pair|Tail], Options) :-
  350    json_pair(C0, Stream, Pair, C, Options),
  351    ws(C, Stream, Next),
  352    (   Next == 0',
  353    ->  ws(Stream, C2),
  354        json_pairs(C2, Stream, Tail, Options)
  355    ;   Next == 0'}
  356    ->  Tail = []
  357    ;   syntax_error(illegal_object, Stream)
  358    ).
  359
  360json_pair(C0, Stream, Name=Value, Next, Options) :-
  361    json_string_as_atom(C0, Stream, Name),
  362    ws(Stream, C),
  363    C == 0':,
  364    json_value(Stream, Value, Next, Options).
  365
  366
  367json_array(0'], _, [], _) :- !.
  368json_array(C0, Stream, [Value|Tail], Options) :-
  369    json_term(C0, Stream, Value, C, Options),
  370    ws(C, Stream, Next),
  371    (   Next == 0',
  372    ->  ws(Stream, C1),
  373        json_array(C1, Stream, Tail, Options)
  374    ;   Next == 0']
  375    ->  Tail = []
  376    ;   syntax_error(illegal_array, Stream)
  377    ).
  378
  379codes_to_type(atom, Codes, Atom) :-
  380    atom_codes(Atom, Codes).
  381codes_to_type(string, Codes, Atom) :-
  382    string_codes(Atom, Codes).
  383codes_to_type(codes, Codes, Codes).
  384
  385json_string_as_atom(0'", Stream, Atom) :-
  386    get_code(Stream, C1),
  387    json_string_codes(C1, Stream, Codes),
  388    atom_codes(Atom, Codes).
  389
  390json_string_codes(0'", _, []) :- !.
  391json_string_codes(0'\\, Stream, [H|T]) :-
  392    !,
  393    get_code(Stream, C0),
  394    (   escape(C0, Stream, H)
  395    ->  true
  396    ;   syntax_error(illegal_string_escape, Stream)
  397    ),
  398    get_code(Stream, C1),
  399    json_string_codes(C1, Stream, T).
  400json_string_codes(-1, Stream, _) :-
  401    !,
  402    syntax_error(eof_in_string, Stream).
  403json_string_codes(C, Stream, [C|T]) :-
  404    get_code(Stream, C1),
  405    json_string_codes(C1, Stream, T).
  406
  407escape(0'", _, 0'") :- !.
  408escape(0'\\, _, 0'\\) :- !.
  409escape(0'/, _, 0'/) :- !.
  410escape(0'b, _, 0'\b) :- !.
  411escape(0'f, _, 0'\f) :- !.
  412escape(0'n, _, 0'\n) :- !.
  413escape(0'r, _, 0'\r) :- !.
  414escape(0't, _, 0'\t) :- !.
  415escape(0'u, Stream, C) :-
  416    get_XXXX(Stream, H),
  417    (   hi_surrogate(H)
  418    ->  get_surrogate_tail(Stream, H, C)
  419    ;   C = H
  420    ).
  421
  422get_XXXX(Stream, C) :-
  423    get_xdigit(Stream, D1),
  424    get_xdigit(Stream, D2),
  425    get_xdigit(Stream, D3),
  426    get_xdigit(Stream, D4),
  427    C is D1<<12+D2<<8+D3<<4+D4.
  428
  429get_xdigit(Stream, D) :-
  430    get_code(Stream, C),
  431    code_type(C, xdigit(D)),
  432    !.
  433get_xdigit(Stream, _) :-
  434    syntax_error(hexdigit_expected, Stream).
  435
  436get_surrogate_tail(Stream, Hi, Codepoint) :-
  437    (   get_code(Stream, 0'\\),
  438        get_code(Stream, 0'u),
  439        get_XXXX(Stream, Lo),
  440        surrogate([Hi, Lo], Codepoint)
  441    ->  true
  442    ;   syntax_error(illegal_surrogate_pair, Stream)
  443    ).
  444
  445
  446hi_surrogate(C) :-
  447    C >= 0xD800, C < 0xDC00.
  448
  449lo_surrogate(C) :-
  450    C >= 0xDC00, C < 0xE000.
  451
  452surrogate([Hi, Lo], Codepoint) :-
  453    hi_surrogate(Hi),
  454    lo_surrogate(Lo),
  455    Codepoint is (Hi - 0xD800) * 0x400 + (Lo - 0xDC00) + 0x10000.
  456
  457json_read_constant(0't, Stream, true) :-
  458    !,
  459    must_see(`rue`, Stream, true).
  460json_read_constant(0'f, Stream, false) :-
  461    !,
  462    must_see(`alse`, Stream, false).
  463json_read_constant(0'n, Stream, null) :-
  464    !,
  465    must_see(`ull`, Stream, null).
  466
  467must_see([], _Stream, _).
  468must_see([H|T], Stream, Name) :-
  469    get_code(Stream, C),
  470    (   C == H
  471    ->  true
  472    ;   syntax_error(json_expected(Name), Stream)
  473    ),
  474    must_see(T, Stream, Name).
  475
  476json_constant(true, Constant, Options) :-
  477    !,
  478    json_options_true(Options, Constant).
  479json_constant(false, Constant, Options) :-
  480    !,
  481    json_options_false(Options, Constant).
  482json_constant(null, Constant, Options) :-
  483    !,
  484    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.
  492ws(Stream, Next) :-
  493    get_code(Stream, C0),
  494    json_skip_ws(Stream, C0, Next).
  495
  496ws(C0, Stream, Next) :-
  497    json_skip_ws(Stream, C0, Next).
  498
  499syntax_error(Message, Stream) :-
  500    stream_error_context(Stream, Context),
  501    throw(error(syntax_error(json(Message)), Context)).
  502
  503stream_error_context(Stream, stream(Stream, Line, LinePos, CharNo)) :-
  504    stream_pair(Stream, Read, _),
  505    character_count(Read, CharNo),
  506    line_position(Read, LinePos),
  507    line_count(Read, Line).
  508
  509
  510                 /*******************************
  511                 *          JSON OUTPUT         *
  512                 *******************************/
 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.
  519% 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.
  527% 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 increment 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 representation 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 advised 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 advised 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.
  618:- record json_write_state(indent:nonneg = 0,
  619                           step:positive_integer = 2,
  620                           tab:positive_integer = 8,
  621                           width:nonneg = 72,
  622                           serialize_unknown:boolean = false
  623                          ).  624
  625json_write(Stream, Term) :-
  626    json_write(Stream, Term, []).
  627json_write(Stream, Term, Options) :-
  628    make_json_write_state(Options, State, Options1),
  629    make_json_options(Options1, OptionTerm, _RestOptions),
  630    json_write_term(Term, Stream, State, OptionTerm).
  631
  632json_write_term(Var, _, _, _) :-
  633    var(Var),
  634    !,
  635    instantiation_error(Var).
  636json_write_term(json(Pairs), Stream, State, Options) :-
  637    !,
  638    json_write_object(Pairs, Stream, State, Options).
  639json_write_term(Dict, Stream, State, Options) :-
  640    is_dict(Dict, Tag),
  641    !,
  642    json_pairs(Dict, Pairs0),
  643    (   nonvar(Tag),
  644        json_options_tag(Options, Name),
  645        Name \== ''
  646    ->  Pairs = [Name-Tag|Pairs0]
  647    ;   Pairs = Pairs0
  648    ),
  649    json_write_object(Pairs, Stream, State, Options).
  650json_write_term(List, Stream, State, Options) :-
  651    is_list(List),
  652    !,
  653    space_if_not_at_left_margin(Stream, State),
  654    write(Stream, '['),
  655    (   json_write_state_width(State, Width),
  656        (   Width == 0
  657        ->  true
  658        ;   json_write_state_indent(State, Indent),
  659            json_print_length(List, Options, Width, Indent, _)
  660        )
  661    ->  set_width_of_json_write_state(0, State, State2),
  662        write_array_hor(List, Stream, State2, Options),
  663        write(Stream, ']')
  664    ;   step_indent(State, State2),
  665        write_array_ver(List, Stream, State2, Options),
  666        indent(Stream, State),
  667        write(Stream, ']')
  668    ).
  669
  670json_write_term(Term, Stream, State, Options) :-
  671    json_write_hook(Term, Stream, State, Options),
  672    !.
  673json_write_term(Number, Stream, _State, _Options) :-
  674    number(Number),
  675    !,
  676    (   float(Number)
  677    ->  write(Stream, Number)
  678    ;   integer(Number)
  679    ->  write(Stream, Number)
  680    ;   Float is float(Number)              % rational number
  681    ->  write(Stream, Float)
  682    ).
  683json_write_term(True, Stream, _State, Options) :-
  684    json_options_true(Options, True),
  685    !,
  686    write(Stream, true).
  687json_write_term(False, Stream, _State, Options) :-
  688    json_options_false(Options, False),
  689    !,
  690    write(Stream, false).
  691json_write_term(Null, Stream, _State, Options) :-
  692    json_options_null(Options, Null),
  693    !,
  694    write(Stream, null).
  695json_write_term(#(Text), Stream, _State, _Options) :-
  696    !,
  697    (   (   atom(Text)
  698        ;   string(Text)
  699        )
  700    ->  json_write_string(Stream, Text)
  701    ;   term_string(Text, String),
  702        json_write_string(Stream, String)
  703    ).
  704json_write_term(String, Stream, _State, _Options) :-
  705    atom(String),
  706    !,
  707    json_write_string(Stream, String).
  708json_write_term(String, Stream, _State, _Options) :-
  709    string(String),
  710    !,
  711    json_write_string(Stream, String).
  712json_write_term(AnyTerm, Stream, State, _Options) :-
  713    (   json_write_state_serialize_unknown(State, true)
  714    ->  term_string(AnyTerm, String),
  715        json_write_string(Stream, String)
  716    ;   type_error(json_term, AnyTerm)
  717    ).
  718
  719json_pairs(Dict, Pairs) :-
  720    json_dict_pairs(Dict, Pairs),
  721    !.
  722json_pairs(Dict, Pairs) :-
  723    dict_pairs(Dict, _, Pairs).
  724
  725json_write_object(Pairs, Stream, State, Options) :-
  726    space_if_not_at_left_margin(Stream, State),
  727    write(Stream, '{'),
  728    (   json_write_state_width(State, Width),
  729        (   Width == 0
  730        ->  true
  731        ;   json_write_state_indent(State, Indent),
  732            json_print_length(json(Pairs), Options, Width, Indent, _)
  733        )
  734    ->  set_width_of_json_write_state(0, State, State2),
  735        write_pairs_hor(Pairs, Stream, State2, Options),
  736        write(Stream, '}')
  737    ;   step_indent(State, State2),
  738        write_pairs_ver(Pairs, Stream, State2, Options),
  739        indent(Stream, State),
  740        write(Stream, '}')
  741    ).
  742
  743
  744write_pairs_hor([], _, _, _).
  745write_pairs_hor([H|T], Stream, State, Options) :-
  746    json_pair(H, Name, Value),
  747    json_write_string(Stream, Name),
  748    write(Stream, ':'),
  749    json_write_term(Value, Stream, State, Options),
  750    (   T == []
  751    ->  true
  752    ;   (   json_write_state_width(State, 0)
  753        ->  write(Stream, ',')
  754        ;   write(Stream, ', ')
  755        ),
  756        write_pairs_hor(T, Stream, State, Options)
  757    ).
  758
  759write_pairs_ver([], _, _, _).
  760write_pairs_ver([H|T], Stream, State, Options) :-
  761    indent(Stream, State),
  762    json_pair(H, Name, Value),
  763    json_write_string(Stream, Name),
  764    write(Stream, ':'),
  765    json_write_term(Value, Stream, State, Options),
  766    (   T == []
  767    ->  true
  768    ;   write(Stream, ','),
  769        write_pairs_ver(T, Stream, State, Options)
  770    ).
  771
  772
  773json_pair(Var, _, _) :-
  774    var(Var),
  775    !,
  776    instantiation_error(Var).
  777json_pair(Name=Value, Name, Value) :- !.
  778json_pair(Name-Value, Name, Value) :- !.
  779json_pair(NameValue, Name, Value) :-
  780    compound(NameValue),
  781    NameValue =.. [Name, Value],
  782    !.
  783json_pair(Pair, _, _) :-
  784    type_error(json_pair, Pair).
  785
  786
  787write_array_hor([], _, _, _).
  788write_array_hor([H|T], Stream, State, Options) :-
  789    json_write_term(H, Stream, State, Options),
  790    (   T == []
  791    ->  write(Stream, ' ')
  792    ;   write(Stream, ', '),
  793        write_array_hor(T, Stream, State, Options)
  794    ).
  795
  796write_array_ver([], _, _, _).
  797write_array_ver([H|T], Stream, State, Options) :-
  798    indent(Stream, State),
  799    json_write_term(H, Stream, State, Options),
  800    (   T == []
  801    ->  true
  802    ;   write(Stream, ','),
  803        write_array_ver(T, Stream, State, Options)
  804    ).
  805
  806
  807indent(Stream, State) :-
  808    json_write_state_indent(State, Indent),
  809    json_write_state_tab(State, Tab),
  810    json_write_indent(Stream, Indent, Tab).
  811
  812step_indent(State0, State) :-
  813    json_write_state_indent(State0, Indent),
  814    json_write_state_step(State0, Step),
  815    NewIndent is Indent+Step,
  816    set_indent_of_json_write_state(NewIndent, State0, State).
  817
  818space_if_not_at_left_margin(Stream, State) :-
  819    stream_pair(Stream, _, Write),
  820    line_position(Write, LinePos),
  821    (   LinePos == 0
  822    ;   json_write_state_indent(State, LinePos)
  823    ),
  824    !.
  825space_if_not_at_left_margin(Stream, _) :-
  826    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.
  836json_print_length(Var, _, _, _, _) :-
  837    var(Var),
  838    !,
  839    instantiation_error(Var).
  840json_print_length(json(Pairs), Options, Max, Len0, Len) :-
  841    !,
  842    Len1 is Len0 + 2,
  843    Len1 =< Max,
  844    must_be(list, Pairs),
  845    pairs_print_length(Pairs, Options, Max, Len1, Len).
  846json_print_length(Dict, Options, Max, Len0, Len) :-
  847    is_dict(Dict),
  848    !,
  849    dict_pairs(Dict, _Tag, Pairs),
  850    Len1 is Len0 + 2,
  851    Len1 =< Max,
  852    pairs_print_length(Pairs, Options, Max, Len1, Len).
  853json_print_length(Array, Options, Max, Len0, Len) :-
  854    is_list(Array),
  855    !,
  856    Len1 is Len0 + 2,
  857    Len1 =< Max,
  858    array_print_length(Array, Options, Max, Len1, Len).
  859json_print_length(Null, Options, Max, Len0, Len) :-
  860    json_options_null(Options, Null),
  861    !,
  862    Len is Len0 + 4,
  863    Len =< Max.
  864json_print_length(False, Options, Max, Len0, Len) :-
  865    json_options_false(Options, False),
  866    !,
  867    Len is Len0 + 5,
  868    Len =< Max.
  869json_print_length(True, Options, Max, Len0, Len) :-
  870    json_options_true(Options, True),
  871    !,
  872    Len is Len0 + 4,
  873    Len =< Max.
  874json_print_length(Number, _Options, Max, Len0, Len) :-
  875    number(Number),
  876    !,
  877    write_length(Number, AL, []),
  878    Len is Len0 + AL,
  879    Len =< Max.
  880json_print_length(@(Id), _Options, Max, Len0, Len) :-
  881    atom(Id),
  882    !,
  883    atom_length(Id, IdLen),
  884    Len is Len0+IdLen,
  885    Len =< Max.
  886json_print_length(String, _Options, Max, Len0, Len) :-
  887    string_len(String, Len0, Len),
  888    !,
  889    Len =< Max.
  890json_print_length(AnyTerm, _Options, Max, Len0, Len) :-
  891    write_length(AnyTerm, AL, []),          % will be serialized
  892    Len is Len0 + AL+2,
  893    Len =< Max.
  894
  895pairs_print_length([], _, _, Len, Len).
  896pairs_print_length([H|T], Options, Max, Len0, Len) :-
  897    pair_len(H, Options, Max, Len0, Len1),
  898    (   T == []
  899    ->  Len = Len1
  900    ;   Len2 is Len1 + 2,
  901        Len2 =< Max,
  902        pairs_print_length(T, Options, Max, Len2, Len)
  903    ).
  904
  905pair_len(Pair, Options, Max, Len0, Len) :-
  906    compound(Pair),
  907    pair_nv(Pair, Name, Value),
  908    !,
  909    string_len(Name, Len0, Len1),
  910    Len2 is Len1+2,
  911    Len2 =< Max,
  912    json_print_length(Value, Options, Max, Len2, Len).
  913pair_len(Pair, _Options, _Max, _Len0, _Len) :-
  914    type_error(pair, Pair).
  915
  916pair_nv(Name=Value, Name, Value) :- !.
  917pair_nv(Name-Value, Name, Value) :- !.
  918pair_nv(Term, Name, Value) :-
  919    compound_name_arguments(Term, Name, [Value]).
  920
  921array_print_length([], _, _, Len, Len).
  922array_print_length([H|T], Options, Max, Len0, Len) :-
  923    json_print_length(H, Options, Max, Len0, Len1),
  924    (   T == []
  925    ->  Len = Len1
  926    ;   Len2 is Len1+2,
  927        Len2 =< Max,
  928        array_print_length(T, Options, Max, Len2, Len)
  929    ).
  930
  931string_len(String, Len0, Len) :-
  932    atom(String),
  933    !,
  934    atom_length(String, AL),
  935    Len is Len0 + AL + 2.
  936string_len(String, Len0, Len) :-
  937    string(String),
  938    !,
  939    string_length(String, AL),
  940    Len is Len0 + AL + 2.
  941
  942
  943                 /*******************************
  944                 *             TEST             *
  945                 *******************************/
 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.
  954is_json_term(Term) :-
  955    default_json_options(Options),
  956    is_json_term2(Options, Term).
  957
  958is_json_term(Term, Options) :-
  959    make_json_options(Options, OptionTerm, _RestOptions),
  960    is_json_term2(OptionTerm, Term).
  961
  962is_json_term2(_, Var) :-
  963    var(Var), !, fail.
  964is_json_term2(Options, json(Pairs)) :-
  965    !,
  966    is_list(Pairs),
  967    maplist(is_json_pair(Options), Pairs).
  968is_json_term2(Options, List) :-
  969    is_list(List),
  970    !,
  971    maplist(is_json_term2(Options), List).
  972is_json_term2(_, Primitive) :-
  973    atomic(Primitive),
  974    !.           % atom, string or number
  975is_json_term2(Options, True) :-
  976    json_options_true(Options, True).
  977is_json_term2(Options, False) :-
  978    json_options_false(Options, False).
  979is_json_term2(Options, Null) :-
  980    json_options_null(Options, Null).
  981
  982is_json_pair(_, Var) :-
  983    var(Var), !, fail.
  984is_json_pair(Options, Name=Value) :-
  985    atom(Name),
  986    is_json_term2(Options, Value).
  987
  988                 /*******************************
  989                 *         DICT SUPPORT         *
  990                 *******************************/
 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.
 1031json_read_dict(Stream, Dict) :-
 1032    json_read_dict(Stream, Dict, []).
 1033
 1034json_read_dict(Stream, Dict, Options) :-
 1035    make_json_dict_options(Options, OptionTerm, _RestOptions),
 1036    (   json_value_top(Stream, Term, OptionTerm)
 1037    ->  true
 1038    ;   syntax_error(illegal_json, Stream)
 1039    ),
 1040    term_to_dict(Term, Dict, OptionTerm).
 1041
 1042term_to_dict(Var, Var, _Options) :-
 1043    var(Var),
 1044    !.
 1045term_to_dict(json(Pairs), Dict, Options) :-
 1046    !,
 1047    (   json_options_tag(Options, TagName),
 1048        Tag \== '',
 1049        select(TagName = Tag0, Pairs, NVPairs),
 1050        to_atom(Tag0, Tag)
 1051    ->  json_dict_pairs(NVPairs, DictPairs, Options)
 1052    ;   json_options_default_tag(Options, DefTag),
 1053        (   var(DefTag)
 1054        ->  true
 1055        ;   Tag = DefTag
 1056        ),
 1057        json_dict_pairs(Pairs, DictPairs, Options)
 1058    ),
 1059    dict_create(Dict, Tag, DictPairs).
 1060term_to_dict(Value0, Value, _Options) :-
 1061    atomic(Value0), Value0 \== [],
 1062    !,
 1063    Value = Value0.
 1064term_to_dict(List0, List, Options) :-
 1065    is_list(List0),
 1066    !,
 1067    terms_to_dicts(List0, List, Options).
 1068term_to_dict(Special, Special, Options) :-
 1069    (   json_options_true(Options, Special)
 1070    ;   json_options_false(Options, Special)
 1071    ;   json_options_null(Options, Special)
 1072    ;   json_options_end_of_file(Options, Special)
 1073    ),
 1074    !.
 1075
 1076json_dict_pairs([], [], _).
 1077json_dict_pairs([Name=Value0|T0], [Name=Value|T], Options) :-
 1078    term_to_dict(Value0, Value, Options),
 1079    json_dict_pairs(T0, T, Options).
 1080
 1081terms_to_dicts([], [], _).
 1082terms_to_dicts([Value0|T0], [Value|T], Options) :-
 1083    term_to_dict(Value0, Value, Options),
 1084    terms_to_dicts(T0, T, Options).
 1085
 1086to_atom(Tag, Atom) :-
 1087    string(Tag),
 1088    !,
 1089    atom_string(Atom, Tag).
 1090to_atom(Atom, Atom) :-
 1091    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.
 1100json_write_dict(Stream, Dict) :-
 1101    json_write_dict(Stream, Dict, []).
 1102
 1103json_write_dict(Stream, Dict, Options) :-
 1104    make_json_write_state(Options, State, Options1),
 1105    make_json_dict_options(Options1, OptionTerm, _RestOptions),
 1106    json_write_term(Dict, Stream, State, OptionTerm).
 1107
 1108
 1109make_json_dict_options(Options, Record, RestOptions) :-
 1110    default_json_dict_options(Record0),
 1111    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 additional option
as(Type)
defines the output type, which is one of atom, string or codes.
 1124atom_json_dict(Atom, Term, Options) :-
 1125    ground(Atom),
 1126    !,
 1127    setup_call_cleanup(
 1128        open_string(Atom, In),
 1129        json_read_dict(In, Term, Options),
 1130        close(In)).
 1131atom_json_dict(Result, Term, Options) :-
 1132    select_option(as(Type), Options, Options1, atom),
 1133    (   type_term(Type, Result, Out)
 1134    ->  true
 1135    ;   must_be(oneof([atom,string,codes]), Type)
 1136    ),
 1137    with_output_to(Out,
 1138                   json_write_dict(current_output, Term, Options1)).
 1139
 1140                 /*******************************
 1141                 *      QUASI-QUOTATIONS        *
 1142                 *******************************/
 json(+Content, +Vars, +VarDict, -JSON) is det
The predicate json/4 implements JSON quasi quotations. These quotations produce a JSON dict that is suitable for json_write_dict/2. The quasi quoter only accepts valid, but possibly partial JSON documents. The quoter replaces content whose value is a Prolog variable that appears in the argument list of the json indicator. Notably, you can't use a Prolog variable in place of an object key. Here is an example.
  {|json(Name)||
      { "name": Name,
        "created": {
          "day":null,
          "month":"December",
          "year":2007
        },
        "confirmed":true,
        "members":[1,2,3]
      }
  |}.
 1168:- quasi_quotation_syntax(json). 1169
 1170json(Content, Vars, Dict, Result) :-
 1171    must_be(list, Dict),
 1172    include(qq_var(Vars), Dict, QQDict),
 1173    with_quasi_quotation_input(Content, Stream,
 1174                               json_read_dict(Stream, Result,
 1175                                              [ qqdict(QQDict)
 1176                                              ])).
 1177
 1178qq_var(Vars, _=Var) :-
 1179    member(V, Vars),
 1180    V == Var,
 1181    !.
 1182
 1183
 1184                 /*******************************
 1185                 *           MESSAGES           *
 1186                 *******************************/
 1187
 1188:- multifile
 1189    prolog:error_message/3. 1190
 1191prolog:error_message(syntax_error(json(Id))) -->
 1192    [ 'JSON syntax error: ' ],
 1193    json_syntax_error(Id).
 1194
 1195json_syntax_error(illegal_comment) -->
 1196    [ 'Illegal comment' ].
 1197json_syntax_error(illegal_string_escape) -->
 1198    [ 'Illegal escape sequence in string' ].
 1199json_syntax_error(illegal_surrogate_pair) -->
 1200    [ 'Illegal escaped surrogate pair in string' ].
 1201json_syntax_error(non_existing_var(Var, QQDict)) -->
 1202    { maplist(arg(1), QQDict, Vars),
 1203      Term =.. [json|Vars]
 1204    },
 1205    [ 'Variable ', ansi(code, '~w', [Var]),
 1206      ' is not defined in {|',ansi(code,'~w',Term),'|||}'
 1207    ].
 1208
 1209                /*******************************
 1210                *           SANDBOX            *
 1211                *******************************/
 1212
 1213:- multifile sandbox:safe_primitive/1. 1214
 1215sandbox:safe_primitive(json:json(_,_,_,_))