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

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. */
  123:- record json_options(
  124              null:ground = @(null),
  125              true:ground = @(true),
  126              false:ground = @(false),
  127              end_of_file:ground = error,
  128              value_string_as:oneof([atom,string]) = atom,
  129              tag:atom = '',
  130              default_tag:atom).  131
  132default_json_dict_options(
  133    json_options(null, true, false, error, string, '', _)).
  134
  135
  136                 /*******************************
  137                 *       MAP TO/FROM TEXT       *
  138                 *******************************/
 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.
  149atom_json_term(Atom, Term, Options) :-
  150    ground(Atom),
  151    !,
  152    setup_call_cleanup(
  153        ( atom_to_memory_file(Atom, MF),
  154          open_memory_file(MF, read, In, [free_on_close(true)])
  155        ),
  156        json_read(In, Term, Options),
  157        close(In)).
  158atom_json_term(Result, Term, Options) :-
  159    select_option(as(Type), Options, Options1, atom),
  160    (   type_term(Type, Result, Out)
  161    ->  true
  162    ;   must_be(oneof([atom,string,codes,chars]), Type)
  163    ),
  164    with_output_to(Out,
  165                   json_write(current_output, Term, Options1)).
  166
  167type_term(atom,   Result, atom(Result)).
  168type_term(string, Result, string(Result)).
  169type_term(codes,  Result, codes(Result)).
  170type_term(chars,  Result, chars(Result)).
  171
  172
  173                 /*******************************
  174                 *           READING            *
  175                 *******************************/
 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.
  250json_read(Stream, Term) :-
  251    default_json_options(Options),
  252    (   json_value_top(Stream, Term, Options)
  253    ->  true
  254    ;   syntax_error(illegal_json, Stream)
  255    ).
  256json_read(Stream, Term, Options) :-
  257    make_json_options(Options, OptionTerm, _RestOptions),
  258    (   json_value_top(Stream, Term, OptionTerm)
  259    ->  true
  260    ;   syntax_error(illegal_json, Stream)
  261    ).
  262
  263json_value_top(Stream, Term, Options) :-
  264    stream_property(Stream, type(binary)),
  265    !,
  266    setup_call_cleanup(
  267        set_stream(Stream, encoding(utf8)),
  268        json_value_top_(Stream, Term, Options),
  269        set_stream(Stream, type(binary))).
  270json_value_top(Stream, Term, Options) :-
  271    json_value_top_(Stream, Term, Options).
  272
  273json_value_top_(Stream, Term, Options) :-
  274    get_code(Stream, C0),
  275    ws(C0, Stream, C1),
  276    (   C1 == -1
  277    ->  json_options_end_of_file(Options, Action),
  278        (   Action == error
  279        ->  syntax_error(unexpected_end_of_file, Stream)
  280        ;   Term = Action
  281        )
  282    ;   json_term_top(C1, Stream, Term, Options)
  283    ).
  284
  285json_value(Stream, Term, Next, Options) :-
  286    get_code(Stream, C0),
  287    ws(C0, Stream, C1),
  288    (   C1 == -1
  289    ->  syntax_error(unexpected_end_of_file, Stream)
  290    ;   json_term(C1, Stream, Term, Next, Options)
  291    ).
  292
  293json_term(C0, Stream, JSON, Next, Options) :-
  294    json_term_top(C0, Stream, JSON, Options),
  295    get_code(Stream, Next).
  296
  297json_term_top(0'{, Stream, json(Pairs), Options) :-
  298    !,
  299    ws(Stream, C),
  300    json_pairs(C, Stream, Pairs, Options).
  301json_term_top(0'[, Stream, Array, Options) :-
  302    !,
  303    ws(Stream, C),
  304    json_array(C, Stream, Array, Options).
  305json_term_top(0'", Stream, String, Options) :-
  306    !,
  307    get_code(Stream, C1),
  308    json_string_codes(C1, Stream, Codes),
  309    json_options_value_string_as(Options, Type),
  310    codes_to_type(Type, Codes, String).
  311json_term_top(0'-, Stream, Number, _Options) :-
  312    !,
  313    json_read_number(Stream, 0'-, Number).
  314json_term_top(D, Stream, Number, _Options) :-
  315    between(0'0, 0'9, D),
  316    !,
  317    json_read_number(Stream, D, Number).
  318json_term_top(C, Stream, Constant, Options) :-
  319    json_read_constant(C, Stream, ID),
  320    json_constant(ID, Constant, Options).
  321
  322json_pairs(0'}, _, [], _) :- !.
  323json_pairs(C0, Stream, [Pair|Tail], Options) :-
  324    json_pair(C0, Stream, Pair, C, Options),
  325    ws(C, Stream, Next),
  326    (   Next == 0',
  327    ->  ws(Stream, C2),
  328        json_pairs(C2, Stream, Tail, Options)
  329    ;   Next == 0'}
  330    ->  Tail = []
  331    ;   syntax_error(illegal_object, Stream)
  332    ).
  333
  334json_pair(C0, Stream, Name=Value, Next, Options) :-
  335    json_string_as_atom(C0, Stream, Name),
  336    ws(Stream, C),
  337    C == 0':,
  338    json_value(Stream, Value, Next, Options).
  339
  340
  341json_array(0'], _, [], _) :- !.
  342json_array(C0, Stream, [Value|Tail], Options) :-
  343    json_term(C0, Stream, Value, C, Options),
  344    ws(C, Stream, Next),
  345    (   Next == 0',
  346    ->  ws(Stream, C1),
  347        json_array(C1, Stream, Tail, Options)
  348    ;   Next == 0']
  349    ->  Tail = []
  350    ;   syntax_error(illegal_array, Stream)
  351    ).
  352
  353codes_to_type(atom, Codes, Atom) :-
  354    atom_codes(Atom, Codes).
  355codes_to_type(string, Codes, Atom) :-
  356    string_codes(Atom, Codes).
  357codes_to_type(codes, Codes, Codes).
  358
  359json_string_as_atom(0'", Stream, Atom) :-
  360    get_code(Stream, C1),
  361    json_string_codes(C1, Stream, Codes),
  362    atom_codes(Atom, Codes).
  363
  364json_string_codes(0'", _, []) :- !.
  365json_string_codes(0'\\, Stream, [H|T]) :-
  366    !,
  367    get_code(Stream, C0),
  368    (   escape(C0, Stream, H)
  369    ->  true
  370    ;   syntax_error(illegal_string_escape, Stream)
  371    ),
  372    get_code(Stream, C1),
  373    json_string_codes(C1, Stream, T).
  374json_string_codes(-1, Stream, _) :-
  375    !,
  376    syntax_error(eof_in_string, Stream).
  377json_string_codes(C, Stream, [C|T]) :-
  378    get_code(Stream, C1),
  379    json_string_codes(C1, Stream, T).
  380
  381escape(0'", _, 0'") :- !.
  382escape(0'\\, _, 0'\\) :- !.
  383escape(0'/, _, 0'/) :- !.
  384escape(0'b, _, 0'\b) :- !.
  385escape(0'f, _, 0'\f) :- !.
  386escape(0'n, _, 0'\n) :- !.
  387escape(0'r, _, 0'\r) :- !.
  388escape(0't, _, 0'\t) :- !.
  389escape(0'u, Stream, C) :-
  390    !,
  391    get_code(Stream, C1),
  392    get_code(Stream, C2),
  393    get_code(Stream, C3),
  394    get_code(Stream, C4),
  395    code_type(C1, xdigit(D1)),
  396    code_type(C2, xdigit(D2)),
  397    code_type(C3, xdigit(D3)),
  398    code_type(C4, xdigit(D4)),
  399    C is D1<<12+D2<<8+D3<<4+D4.
  400
  401json_read_constant(0't, Stream, true) :-
  402    !,
  403    must_see(`rue`, Stream, true).
  404json_read_constant(0'f, Stream, false) :-
  405    !,
  406    must_see(`alse`, Stream, false).
  407json_read_constant(0'n, Stream, null) :-
  408    !,
  409    must_see(`ull`, Stream, null).
  410
  411must_see([], _Stream, _).
  412must_see([H|T], Stream, Name) :-
  413    get_code(Stream, C),
  414    (   C == H
  415    ->  true
  416    ;   syntax_error(json_expected(Name), Stream)
  417    ),
  418    must_see(T, Stream, Name).
  419
  420json_constant(true, Constant, Options) :-
  421    !,
  422    json_options_true(Options, Constant).
  423json_constant(false, Constant, Options) :-
  424    !,
  425    json_options_false(Options, Constant).
  426json_constant(null, Constant, Options) :-
  427    !,
  428    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.
  436ws(Stream, Next) :-
  437    get_code(Stream, C0),
  438    json_skip_ws(Stream, C0, Next).
  439
  440ws(C0, Stream, Next) :-
  441    json_skip_ws(Stream, C0, Next).
  442
  443syntax_error(Message, Stream) :-
  444    stream_error_context(Stream, Context),
  445    throw(error(syntax_error(json(Message)), Context)).
  446
  447stream_error_context(Stream, stream(Stream, Line, LinePos, CharNo)) :-
  448    stream_pair(Stream, Read, _),
  449    character_count(Read, CharNo),
  450    line_position(Read, LinePos),
  451    line_count(Read, Line).
  452
  453
  454                 /*******************************
  455                 *          JSON OUTPUT         *
  456                 *******************************/
 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.
  463% 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.
  471% 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.
  562:- record json_write_state(indent:nonneg = 0,
  563                           step:positive_integer = 2,
  564                           tab:positive_integer = 8,
  565                           width:nonneg = 72,
  566                           serialize_unknown:boolean = false
  567                          ).  568
  569json_write(Stream, Term) :-
  570    json_write(Stream, Term, []).
  571json_write(Stream, Term, Options) :-
  572    make_json_write_state(Options, State, Options1),
  573    make_json_options(Options1, OptionTerm, _RestOptions),
  574    json_write_term(Term, Stream, State, OptionTerm).
  575
  576json_write_term(Var, _, _, _) :-
  577    var(Var),
  578    !,
  579    instantiation_error(Var).
  580json_write_term(json(Pairs), Stream, State, Options) :-
  581    !,
  582    json_write_object(Pairs, Stream, State, Options).
  583json_write_term(Dict, Stream, State, Options) :-
  584    is_dict(Dict, Tag),
  585    !,
  586    json_pairs(Dict, Pairs0),
  587    (   nonvar(Tag),
  588        json_options_tag(Options, Name),
  589        Name \== ''
  590    ->  Pairs = [Name-Tag|Pairs0]
  591    ;   Pairs = Pairs0
  592    ),
  593    json_write_object(Pairs, Stream, State, Options).
  594json_write_term(List, Stream, State, Options) :-
  595    is_list(List),
  596    !,
  597    space_if_not_at_left_margin(Stream, State),
  598    write(Stream, '['),
  599    (   json_write_state_width(State, Width),
  600        (   Width == 0
  601        ->  true
  602        ;   json_write_state_indent(State, Indent),
  603            json_print_length(List, Options, Width, Indent, _)
  604        )
  605    ->  set_width_of_json_write_state(0, State, State2),
  606        write_array_hor(List, Stream, State2, Options),
  607        write(Stream, ']')
  608    ;   step_indent(State, State2),
  609        write_array_ver(List, Stream, State2, Options),
  610        indent(Stream, State),
  611        write(Stream, ']')
  612    ).
  613
  614json_write_term(Term, Stream, State, Options) :-
  615    json_write_hook(Term, Stream, State, Options),
  616    !.
  617json_write_term(Number, Stream, _State, _Options) :-
  618    number(Number),
  619    !,
  620    (   float(Number)
  621    ->  write(Stream, Number)
  622    ;   integer(Number)
  623    ->  write(Stream, Number)
  624    ;   Float is float(Number)              % rational number
  625    ->  write(Stream, Float)
  626    ).
  627json_write_term(True, Stream, _State, Options) :-
  628    json_options_true(Options, True),
  629    !,
  630    write(Stream, true).
  631json_write_term(False, Stream, _State, Options) :-
  632    json_options_false(Options, False),
  633    !,
  634    write(Stream, false).
  635json_write_term(Null, Stream, _State, Options) :-
  636    json_options_null(Options, Null),
  637    !,
  638    write(Stream, null).
  639json_write_term(#(Text), Stream, _State, _Options) :-
  640    !,
  641    (   (   atom(Text)
  642        ;   string(Text)
  643        )
  644    ->  json_write_string(Stream, Text)
  645    ;   term_string(Text, String),
  646        json_write_string(Stream, String)
  647    ).
  648json_write_term(String, Stream, _State, _Options) :-
  649    atom(String),
  650    !,
  651    json_write_string(Stream, String).
  652json_write_term(String, Stream, _State, _Options) :-
  653    string(String),
  654    !,
  655    json_write_string(Stream, String).
  656json_write_term(AnyTerm, Stream, State, _Options) :-
  657    (   json_write_state_serialize_unknown(State, true)
  658    ->  term_string(AnyTerm, String),
  659        json_write_string(Stream, String)
  660    ;   type_error(json_term, AnyTerm)
  661    ).
  662
  663json_pairs(Dict, Pairs) :-
  664    json_dict_pairs(Dict, Pairs),
  665    !.
  666json_pairs(Dict, Pairs) :-
  667    dict_pairs(Dict, _, Pairs).
  668
  669json_write_object(Pairs, Stream, State, Options) :-
  670    space_if_not_at_left_margin(Stream, State),
  671    write(Stream, '{'),
  672    (   json_write_state_width(State, Width),
  673        (   Width == 0
  674        ->  true
  675        ;   json_write_state_indent(State, Indent),
  676            json_print_length(json(Pairs), Options, Width, Indent, _)
  677        )
  678    ->  set_width_of_json_write_state(0, State, State2),
  679        write_pairs_hor(Pairs, Stream, State2, Options),
  680        write(Stream, '}')
  681    ;   step_indent(State, State2),
  682        write_pairs_ver(Pairs, Stream, State2, Options),
  683        indent(Stream, State),
  684        write(Stream, '}')
  685    ).
  686
  687
  688write_pairs_hor([], _, _, _).
  689write_pairs_hor([H|T], Stream, State, Options) :-
  690    json_pair(H, Name, Value),
  691    json_write_string(Stream, Name),
  692    write(Stream, ':'),
  693    json_write_term(Value, Stream, State, Options),
  694    (   T == []
  695    ->  true
  696    ;   write(Stream, ', '),
  697        write_pairs_hor(T, Stream, State, Options)
  698    ).
  699
  700write_pairs_ver([], _, _, _).
  701write_pairs_ver([H|T], Stream, State, Options) :-
  702    indent(Stream, State),
  703    json_pair(H, Name, Value),
  704    json_write_string(Stream, Name),
  705    write(Stream, ':'),
  706    json_write_term(Value, Stream, State, Options),
  707    (   T == []
  708    ->  true
  709    ;   write(Stream, ','),
  710        write_pairs_ver(T, Stream, State, Options)
  711    ).
  712
  713
  714json_pair(Var, _, _) :-
  715    var(Var),
  716    !,
  717    instantiation_error(Var).
  718json_pair(Name=Value, Name, Value) :- !.
  719json_pair(Name-Value, Name, Value) :- !.
  720json_pair(NameValue, Name, Value) :-
  721    compound(NameValue),
  722    NameValue =.. [Name, Value],
  723    !.
  724json_pair(Pair, _, _) :-
  725    type_error(json_pair, Pair).
  726
  727
  728write_array_hor([], _, _, _).
  729write_array_hor([H|T], Stream, State, Options) :-
  730    json_write_term(H, Stream, State, Options),
  731    (   T == []
  732    ->  write(Stream, ' ')
  733    ;   write(Stream, ', '),
  734        write_array_hor(T, Stream, State, Options)
  735    ).
  736
  737write_array_ver([], _, _, _).
  738write_array_ver([H|T], Stream, State, Options) :-
  739    indent(Stream, State),
  740    json_write_term(H, Stream, State, Options),
  741    (   T == []
  742    ->  true
  743    ;   write(Stream, ','),
  744        write_array_ver(T, Stream, State, Options)
  745    ).
  746
  747
  748indent(Stream, State) :-
  749    json_write_state_indent(State, Indent),
  750    json_write_state_tab(State, Tab),
  751    json_write_indent(Stream, Indent, Tab).
  752
  753step_indent(State0, State) :-
  754    json_write_state_indent(State0, Indent),
  755    json_write_state_step(State0, Step),
  756    NewIndent is Indent+Step,
  757    set_indent_of_json_write_state(NewIndent, State0, State).
  758
  759space_if_not_at_left_margin(Stream, State) :-
  760    stream_pair(Stream, _, Write),
  761    line_position(Write, LinePos),
  762    (   LinePos == 0
  763    ;   json_write_state_indent(State, LinePos)
  764    ),
  765    !.
  766space_if_not_at_left_margin(Stream, _) :-
  767    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.
  777json_print_length(Var, _, _, _, _) :-
  778    var(Var),
  779    !,
  780    instantiation_error(Var).
  781json_print_length(json(Pairs), Options, Max, Len0, Len) :-
  782    !,
  783    Len1 is Len0 + 2,
  784    Len1 =< Max,
  785    must_be(list, Pairs),
  786    pairs_print_length(Pairs, Options, Max, Len1, Len).
  787json_print_length(Dict, Options, Max, Len0, Len) :-
  788    is_dict(Dict),
  789    !,
  790    dict_pairs(Dict, _Tag, Pairs),
  791    Len1 is Len0 + 2,
  792    Len1 =< Max,
  793    pairs_print_length(Pairs, Options, Max, Len1, Len).
  794json_print_length(Array, Options, Max, Len0, Len) :-
  795    is_list(Array),
  796    !,
  797    Len1 is Len0 + 2,
  798    Len1 =< Max,
  799    array_print_length(Array, Options, Max, Len1, Len).
  800json_print_length(Null, Options, Max, Len0, Len) :-
  801    json_options_null(Options, Null),
  802    !,
  803    Len is Len0 + 4,
  804    Len =< Max.
  805json_print_length(False, Options, Max, Len0, Len) :-
  806    json_options_false(Options, False),
  807    !,
  808    Len is Len0 + 5,
  809    Len =< Max.
  810json_print_length(True, Options, Max, Len0, Len) :-
  811    json_options_true(Options, True),
  812    !,
  813    Len is Len0 + 4,
  814    Len =< Max.
  815json_print_length(Number, _Options, Max, Len0, Len) :-
  816    number(Number),
  817    !,
  818    write_length(Number, AL, []),
  819    Len is Len0 + AL,
  820    Len =< Max.
  821json_print_length(@(Id), _Options, Max, Len0, Len) :-
  822    atom(Id),
  823    !,
  824    atom_length(Id, IdLen),
  825    Len is Len0+IdLen,
  826    Len =< Max.
  827json_print_length(String, _Options, Max, Len0, Len) :-
  828    string_len(String, Len0, Len),
  829    !,
  830    Len =< Max.
  831json_print_length(AnyTerm, _Options, Max, Len0, Len) :-
  832    write_length(AnyTerm, AL, []),          % will be serialized
  833    Len is Len0 + AL+2,
  834    Len =< Max.
  835
  836pairs_print_length([], _, _, Len, Len).
  837pairs_print_length([H|T], Options, Max, Len0, Len) :-
  838    pair_len(H, Options, Max, Len0, Len1),
  839    (   T == []
  840    ->  Len = Len1
  841    ;   Len2 is Len1 + 2,
  842        Len2 =< Max,
  843        pairs_print_length(T, Options, Max, Len2, Len)
  844    ).
  845
  846pair_len(Pair, Options, Max, Len0, Len) :-
  847    compound(Pair),
  848    pair_nv(Pair, Name, Value),
  849    !,
  850    string_len(Name, Len0, Len1),
  851    Len2 is Len1+2,
  852    Len2 =< Max,
  853    json_print_length(Value, Options, Max, Len2, Len).
  854pair_len(Pair, _Options, _Max, _Len0, _Len) :-
  855    type_error(pair, Pair).
  856
  857pair_nv(Name=Value, Name, Value) :- !.
  858pair_nv(Name-Value, Name, Value) :- !.
  859pair_nv(Term, Name, Value) :-
  860    compound_name_arguments(Term, Name, [Value]).
  861
  862array_print_length([], _, _, Len, Len).
  863array_print_length([H|T], Options, Max, Len0, Len) :-
  864    json_print_length(H, Options, Max, Len0, Len1),
  865    (   T == []
  866    ->  Len = Len1
  867    ;   Len2 is Len1+2,
  868        Len2 =< Max,
  869        array_print_length(T, Options, Max, Len2, Len)
  870    ).
  871
  872string_len(String, Len0, Len) :-
  873    atom(String),
  874    !,
  875    atom_length(String, AL),
  876    Len is Len0 + AL + 2.
  877string_len(String, Len0, Len) :-
  878    string(String),
  879    !,
  880    string_length(String, AL),
  881    Len is Len0 + AL + 2.
  882
  883
  884                 /*******************************
  885                 *             TEST             *
  886                 *******************************/
 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.
  895is_json_term(Term) :-
  896    default_json_options(Options),
  897    is_json_term2(Options, Term).
  898
  899is_json_term(Term, Options) :-
  900    make_json_options(Options, OptionTerm, _RestOptions),
  901    is_json_term2(OptionTerm, Term).
  902
  903is_json_term2(_, Var) :-
  904    var(Var), !, fail.
  905is_json_term2(Options, json(Pairs)) :-
  906    !,
  907    is_list(Pairs),
  908    maplist(is_json_pair(Options), Pairs).
  909is_json_term2(Options, List) :-
  910    is_list(List),
  911    !,
  912    maplist(is_json_term2(Options), List).
  913is_json_term2(_, Primitive) :-
  914    atomic(Primitive),
  915    !.           % atom, string or number
  916is_json_term2(Options, True) :-
  917    json_options_true(Options, True).
  918is_json_term2(Options, False) :-
  919    json_options_false(Options, False).
  920is_json_term2(Options, Null) :-
  921    json_options_null(Options, Null).
  922
  923is_json_pair(_, Var) :-
  924    var(Var), !, fail.
  925is_json_pair(Options, Name=Value) :-
  926    atom(Name),
  927    is_json_term2(Options, Value).
  928
  929                 /*******************************
  930                 *         DICT SUPPORT         *
  931                 *******************************/
 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.
  972json_read_dict(Stream, Dict) :-
  973    json_read_dict(Stream, Dict, []).
  974
  975json_read_dict(Stream, Dict, Options) :-
  976    make_json_dict_options(Options, OptionTerm, _RestOptions),
  977    (   json_value_top(Stream, Term, OptionTerm)
  978    ->  true
  979    ;   syntax_error(illegal_json, Stream)
  980    ),
  981    term_to_dict(Term, Dict, OptionTerm).
  982
  983term_to_dict(json(Pairs), Dict, Options) :-
  984    !,
  985    (   json_options_tag(Options, TagName),
  986        Tag \== '',
  987        select(TagName = Tag0, Pairs, NVPairs),
  988        to_atom(Tag0, Tag)
  989    ->  json_dict_pairs(NVPairs, DictPairs, Options)
  990    ;   json_options_default_tag(Options, DefTag),
  991        (   var(DefTag)
  992        ->  true
  993        ;   Tag = DefTag
  994        ),
  995        json_dict_pairs(Pairs, DictPairs, Options)
  996    ),
  997    dict_create(Dict, Tag, DictPairs).
  998term_to_dict(Value0, Value, _Options) :-
  999    atomic(Value0), Value0 \== [],
 1000    !,
 1001    Value = Value0.
 1002term_to_dict(List0, List, Options) :-
 1003    is_list(List0),
 1004    !,
 1005    terms_to_dicts(List0, List, Options).
 1006term_to_dict(Special, Special, Options) :-
 1007    (   json_options_true(Options, Special)
 1008    ;   json_options_false(Options, Special)
 1009    ;   json_options_null(Options, Special)
 1010    ;   json_options_end_of_file(Options, Special)
 1011    ),
 1012    !.
 1013
 1014json_dict_pairs([], [], _).
 1015json_dict_pairs([Name=Value0|T0], [Name=Value|T], Options) :-
 1016    term_to_dict(Value0, Value, Options),
 1017    json_dict_pairs(T0, T, Options).
 1018
 1019terms_to_dicts([], [], _).
 1020terms_to_dicts([Value0|T0], [Value|T], Options) :-
 1021    term_to_dict(Value0, Value, Options),
 1022    terms_to_dicts(T0, T, Options).
 1023
 1024to_atom(Tag, Atom) :-
 1025    string(Tag),
 1026    !,
 1027    atom_string(Atom, Tag).
 1028to_atom(Atom, Atom) :-
 1029    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.
 1038json_write_dict(Stream, Dict) :-
 1039    json_write_dict(Stream, Dict, []).
 1040
 1041json_write_dict(Stream, Dict, Options) :-
 1042    make_json_write_state(Options, State, Options1),
 1043    make_json_dict_options(Options1, OptionTerm, _RestOptions),
 1044    json_write_term(Dict, Stream, State, OptionTerm).
 1045
 1046
 1047make_json_dict_options(Options, Record, RestOptions) :-
 1048    default_json_dict_options(Record0),
 1049    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.
 1062atom_json_dict(Atom, Term, Options) :-
 1063    ground(Atom),
 1064    !,
 1065    setup_call_cleanup(
 1066        open_string(Atom, In),
 1067        json_read_dict(In, Term, Options),
 1068        close(In)).
 1069atom_json_dict(Result, Term, Options) :-
 1070    select_option(as(Type), Options, Options1, atom),
 1071    (   type_term(Type, Result, Out)
 1072    ->  true
 1073    ;   must_be(oneof([atom,string,codes]), Type)
 1074    ),
 1075    with_output_to(Out,
 1076                   json_write_dict(current_output, Term, Options1)).
 1077
 1078
 1079                 /*******************************
 1080                 *           MESSAGES           *
 1081                 *******************************/
 1082
 1083:- multifile
 1084    prolog:error_message/3. 1085
 1086prolog:error_message(syntax_error(json(Id))) -->
 1087    [ 'JSON syntax error: ' ],
 1088    json_syntax_error(Id).
 1089
 1090json_syntax_error(illegal_comment) -->
 1091    [ 'Illegal comment' ].
 1092json_syntax_error(illegal_string_escape) -->
 1093    [ 'Illegal escape sequence in string' ]