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

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. */
  138:- record json_options(
  139              null:ground = @(null),
  140              true:ground = @(true),
  141              false:ground = @(false),
  142              end_of_file:ground = error,
  143              value_string_as:oneof([atom,string]) = atom,
  144              tag:atom = '',
  145              default_tag:atom).  146
  147default_json_dict_options(
  148    json_options(null, true, false, error, string, '', _)).
  149
  150
  151                 /*******************************
  152                 *       MAP TO/FROM TEXT       *
  153                 *******************************/
 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.
  164atom_json_term(Atom, Term, Options) :-
  165    ground(Atom),
  166    !,
  167    setup_call_cleanup(
  168        ( atom_to_memory_file(Atom, MF),
  169          open_memory_file(MF, read, In, [free_on_close(true)])
  170        ),
  171        json_read(In, Term, Options),
  172        close(In)).
  173atom_json_term(Result, Term, Options) :-
  174    select_option(as(Type), Options, Options1, atom),
  175    (   type_term(Type, Result, Out)
  176    ->  true
  177    ;   must_be(oneof([atom,string,codes,chars]), Type)
  178    ),
  179    with_output_to(Out,
  180                   json_write(current_output, Term, Options1)).
  181
  182type_term(atom,   Result, atom(Result)).
  183type_term(string, Result, string(Result)).
  184type_term(codes,  Result, codes(Result)).
  185type_term(chars,  Result, chars(Result)).
  186
  187
  188                 /*******************************
  189                 *           READING            *
  190                 *******************************/
 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.
  265json_read(Stream, Term) :-
  266    default_json_options(Options),
  267    (   json_value_top(Stream, Term, Options)
  268    ->  true
  269    ;   syntax_error(illegal_json, Stream)
  270    ).
  271json_read(Stream, Term, Options) :-
  272    make_json_options(Options, OptionTerm, _RestOptions),
  273    (   json_value_top(Stream, Term, OptionTerm)
  274    ->  true
  275    ;   syntax_error(illegal_json, Stream)
  276    ).
  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    json_constant(ID, Constant, Options).
  326
  327json_pairs(0'}, _, [], _) :- !.
  328json_pairs(C0, Stream, [Pair|Tail], Options) :-
  329    json_pair(C0, Stream, Pair, C, Options),
  330    ws(C, Stream, Next),
  331    (   Next == 0',
  332    ->  ws(Stream, C2),
  333        json_pairs(C2, Stream, Tail, Options)
  334    ;   Next == 0'}
  335    ->  Tail = []
  336    ;   syntax_error(illegal_object, Stream)
  337    ).
  338
  339json_pair(C0, Stream, Name=Value, Next, Options) :-
  340    json_string_as_atom(C0, Stream, Name),
  341    ws(Stream, C),
  342    C == 0':,
  343    json_value(Stream, Value, Next, Options).
  344
  345
  346json_array(0'], _, [], _) :- !.
  347json_array(C0, Stream, [Value|Tail], Options) :-
  348    json_term(C0, Stream, Value, C, Options),
  349    ws(C, Stream, Next),
  350    (   Next == 0',
  351    ->  ws(Stream, C1),
  352        json_array(C1, Stream, Tail, Options)
  353    ;   Next == 0']
  354    ->  Tail = []
  355    ;   syntax_error(illegal_array, Stream)
  356    ).
  357
  358codes_to_type(atom, Codes, Atom) :-
  359    atom_codes(Atom, Codes).
  360codes_to_type(string, Codes, Atom) :-
  361    string_codes(Atom, Codes).
  362codes_to_type(codes, Codes, Codes).
  363
  364json_string_as_atom(0'", Stream, Atom) :-
  365    get_code(Stream, C1),
  366    json_string_codes(C1, Stream, Codes),
  367    atom_codes(Atom, Codes).
  368
  369json_string_codes(0'", _, []) :- !.
  370json_string_codes(0'\\, Stream, [H|T]) :-
  371    !,
  372    get_code(Stream, C0),
  373    (   escape(C0, Stream, H)
  374    ->  true
  375    ;   syntax_error(illegal_string_escape, Stream)
  376    ),
  377    get_code(Stream, C1),
  378    json_string_codes(C1, Stream, T).
  379json_string_codes(-1, Stream, _) :-
  380    !,
  381    syntax_error(eof_in_string, Stream).
  382json_string_codes(C, Stream, [C|T]) :-
  383    get_code(Stream, C1),
  384    json_string_codes(C1, Stream, T).
  385
  386escape(0'", _, 0'") :- !.
  387escape(0'\\, _, 0'\\) :- !.
  388escape(0'/, _, 0'/) :- !.
  389escape(0'b, _, 0'\b) :- !.
  390escape(0'f, _, 0'\f) :- !.
  391escape(0'n, _, 0'\n) :- !.
  392escape(0'r, _, 0'\r) :- !.
  393escape(0't, _, 0'\t) :- !.
  394escape(0'u, Stream, C) :-
  395    !,
  396    get_code(Stream, C1),
  397    get_code(Stream, C2),
  398    get_code(Stream, C3),
  399    get_code(Stream, C4),
  400    code_type(C1, xdigit(D1)),
  401    code_type(C2, xdigit(D2)),
  402    code_type(C3, xdigit(D3)),
  403    code_type(C4, xdigit(D4)),
  404    C is D1<<12+D2<<8+D3<<4+D4.
  405
  406json_read_constant(0't, Stream, true) :-
  407    !,
  408    must_see(`rue`, Stream, true).
  409json_read_constant(0'f, Stream, false) :-
  410    !,
  411    must_see(`alse`, Stream, false).
  412json_read_constant(0'n, Stream, null) :-
  413    !,
  414    must_see(`ull`, Stream, null).
  415
  416must_see([], _Stream, _).
  417must_see([H|T], Stream, Name) :-
  418    get_code(Stream, C),
  419    (   C == H
  420    ->  true
  421    ;   syntax_error(json_expected(Name), Stream)
  422    ),
  423    must_see(T, Stream, Name).
  424
  425json_constant(true, Constant, Options) :-
  426    !,
  427    json_options_true(Options, Constant).
  428json_constant(false, Constant, Options) :-
  429    !,
  430    json_options_false(Options, Constant).
  431json_constant(null, Constant, Options) :-
  432    !,
  433    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.
  441ws(Stream, Next) :-
  442    get_code(Stream, C0),
  443    json_skip_ws(Stream, C0, Next).
  444
  445ws(C0, Stream, Next) :-
  446    json_skip_ws(Stream, C0, Next).
  447
  448syntax_error(Message, Stream) :-
  449    stream_error_context(Stream, Context),
  450    throw(error(syntax_error(json(Message)), Context)).
  451
  452stream_error_context(Stream, stream(Stream, Line, LinePos, CharNo)) :-
  453    stream_pair(Stream, Read, _),
  454    character_count(Read, CharNo),
  455    line_position(Read, LinePos),
  456    line_count(Read, Line).
  457
  458
  459                 /*******************************
  460                 *          JSON OUTPUT         *
  461                 *******************************/
 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.
  468% 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.
  476% 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.

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.
  560:- record json_write_state(indent:nonneg = 0,
  561                       step:positive_integer = 2,
  562                       tab:positive_integer = 8,
  563                       width:nonneg = 72,
  564                       serialize_unknown:boolean = false
  565                      ).  566
  567json_write(Stream, Term) :-
  568    json_write(Stream, Term, []).
  569json_write(Stream, Term, Options) :-
  570    make_json_write_state(Options, State, Options1),
  571    make_json_options(Options1, OptionTerm, _RestOptions),
  572    json_write_term(Term, Stream, State, OptionTerm).
  573
  574json_write_term(Var, _, _, _) :-
  575    var(Var),
  576    !,
  577    instantiation_error(Var).
  578json_write_term(json(Pairs), Stream, State, Options) :-
  579    !,
  580    json_write_object(Pairs, Stream, State, Options).
  581:- if(current_predicate(is_dict/1)).  582json_write_term(Dict, Stream, State, Options) :-
  583    is_dict(Dict),
  584    !,
  585    dict_pairs(Dict, Tag, Pairs0),
  586    (   nonvar(Tag),
  587        json_options_tag(Options, Name),
  588        Name \== ''
  589    ->  Pairs = [Name-Tag|Pairs0]
  590    ;   Pairs = Pairs0
  591    ),
  592    json_write_object(Pairs, Stream, State, Options).
  593:- endif.  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    write(Stream, Number).
  621json_write_term(True, Stream, _State, Options) :-
  622    json_options_true(Options, True),
  623    !,
  624    write(Stream, true).
  625json_write_term(False, Stream, _State, Options) :-
  626    json_options_false(Options, False),
  627    !,
  628    write(Stream, false).
  629json_write_term(Null, Stream, _State, Options) :-
  630    json_options_null(Options, Null),
  631    !,
  632    write(Stream, null).
  633json_write_term(#(Text), Stream, _State, _Options) :-
  634    !,
  635    (   (   atom(Text)
  636        ;   string(Text)
  637        )
  638    ->  json_write_string(Stream, Text)
  639    ;   term_string(Text, String),
  640        json_write_string(Stream, String)
  641    ).
  642json_write_term(String, Stream, _State, _Options) :-
  643    atom(String),
  644    !,
  645    json_write_string(Stream, String).
  646json_write_term(String, Stream, _State, _Options) :-
  647    string(String),
  648    !,
  649    json_write_string(Stream, String).
  650json_write_term(AnyTerm, Stream, State, _Options) :-
  651    (   json_write_state_serialize_unknown(State, true)
  652    ->  term_string(AnyTerm, String),
  653        json_write_string(Stream, String)
  654    ;   type_error(json_term, AnyTerm)
  655    ).
  656
  657json_write_object(Pairs, Stream, State, Options) :-
  658    space_if_not_at_left_margin(Stream, State),
  659    write(Stream, '{'),
  660    (   json_write_state_width(State, Width),
  661        (   Width == 0
  662        ->  true
  663        ;   json_write_state_indent(State, Indent),
  664            json_print_length(json(Pairs), Options, Width, Indent, _)
  665        )
  666    ->  set_width_of_json_write_state(0, State, State2),
  667        write_pairs_hor(Pairs, Stream, State2, Options),
  668        write(Stream, '}')
  669    ;   step_indent(State, State2),
  670        write_pairs_ver(Pairs, Stream, State2, Options),
  671        indent(Stream, State),
  672        write(Stream, '}')
  673    ).
  674
  675
  676write_pairs_hor([], _, _, _).
  677write_pairs_hor([H|T], Stream, State, Options) :-
  678    json_pair(H, Name, Value),
  679    json_write_string(Stream, Name),
  680    write(Stream, ':'),
  681    json_write_term(Value, Stream, State, Options),
  682    (   T == []
  683    ->  true
  684    ;   write(Stream, ', '),
  685        write_pairs_hor(T, Stream, State, Options)
  686    ).
  687
  688write_pairs_ver([], _, _, _).
  689write_pairs_ver([H|T], Stream, State, Options) :-
  690    indent(Stream, State),
  691    json_pair(H, Name, Value),
  692    json_write_string(Stream, Name),
  693    write(Stream, ':'),
  694    json_write_term(Value, Stream, State, Options),
  695    (   T == []
  696    ->  true
  697    ;   write(Stream, ','),
  698        write_pairs_ver(T, Stream, State, Options)
  699    ).
  700
  701
  702json_pair(Var, _, _) :-
  703    var(Var),
  704    !,
  705    instantiation_error(Var).
  706json_pair(Name=Value, Name, Value) :- !.
  707json_pair(Name-Value, Name, Value) :- !.
  708json_pair(NameValue, Name, Value) :-
  709    compound(NameValue),
  710    NameValue =.. [Name, Value],
  711    !.
  712json_pair(Pair, _, _) :-
  713    type_error(json_pair, Pair).
  714
  715
  716write_array_hor([], _, _, _).
  717write_array_hor([H|T], Stream, State, Options) :-
  718    json_write_term(H, Stream, State, Options),
  719    (   T == []
  720    ->  write(Stream, ' ')
  721    ;   write(Stream, ', '),
  722        write_array_hor(T, Stream, State, Options)
  723    ).
  724
  725write_array_ver([], _, _, _).
  726write_array_ver([H|T], Stream, State, Options) :-
  727    indent(Stream, State),
  728    json_write_term(H, Stream, State, Options),
  729    (   T == []
  730    ->  true
  731    ;   write(Stream, ','),
  732        write_array_ver(T, Stream, State, Options)
  733    ).
  734
  735
  736indent(Stream, State) :-
  737    json_write_state_indent(State, Indent),
  738    json_write_state_tab(State, Tab),
  739    json_write_indent(Stream, Indent, Tab).
  740
  741step_indent(State0, State) :-
  742    json_write_state_indent(State0, Indent),
  743    json_write_state_step(State0, Step),
  744    NewIndent is Indent+Step,
  745    set_indent_of_json_write_state(NewIndent, State0, State).
  746
  747space_if_not_at_left_margin(Stream, State) :-
  748    stream_pair(Stream, _, Write),
  749    line_position(Write, LinePos),
  750    (   LinePos == 0
  751    ;   json_write_state_indent(State, LinePos)
  752    ),
  753    !.
  754space_if_not_at_left_margin(Stream, _) :-
  755    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.
  765json_print_length(Var, _, _, _, _) :-
  766    var(Var),
  767    !,
  768    instantiation_error(Var).
  769json_print_length(json(Pairs), Options, Max, Len0, Len) :-
  770    !,
  771    Len1 is Len0 + 2,
  772    Len1 =< Max,
  773    must_be(list, Pairs),
  774    pairs_print_length(Pairs, Options, Max, Len1, Len).
  775:- if(current_predicate(is_dict/1)).  776json_print_length(Dict, Options, Max, Len0, Len) :-
  777    is_dict(Dict),
  778    !,
  779    dict_pairs(Dict, _Tag, Pairs),
  780    Len1 is Len0 + 2,
  781    Len1 =< Max,
  782    pairs_print_length(Pairs, Options, Max, Len1, Len).
  783:- endif.  784json_print_length(Array, Options, Max, Len0, Len) :-
  785    is_list(Array),
  786    !,
  787    Len1 is Len0 + 2,
  788    Len1 =< Max,
  789    array_print_length(Array, Options, Max, Len1, Len).
  790json_print_length(Null, Options, Max, Len0, Len) :-
  791    json_options_null(Options, Null),
  792    !,
  793    Len is Len0 + 4,
  794    Len =< Max.
  795json_print_length(False, Options, Max, Len0, Len) :-
  796    json_options_false(Options, False),
  797    !,
  798    Len is Len0 + 5,
  799    Len =< Max.
  800json_print_length(True, Options, Max, Len0, Len) :-
  801    json_options_true(Options, True),
  802    !,
  803    Len is Len0 + 4,
  804    Len =< Max.
  805json_print_length(Number, _Options, Max, Len0, Len) :-
  806    number(Number),
  807    !,
  808    write_length(Number, AL, []),
  809    Len is Len0 + AL,
  810    Len =< Max.
  811json_print_length(@(Id), _Options, Max, Len0, Len) :-
  812    atom(Id),
  813    !,
  814    atom_length(Id, IdLen),
  815    Len is Len0+IdLen,
  816    Len =< Max.
  817json_print_length(String, _Options, Max, Len0, Len) :-
  818    string_len(String, Len0, Len),
  819    !,
  820    Len =< Max.
  821json_print_length(AnyTerm, _Options, Max, Len0, Len) :-
  822    write_length(AnyTerm, AL, []),          % will be serialized
  823    Len is Len0 + AL+2,
  824    Len =< Max.
  825
  826pairs_print_length([], _, _, Len, Len).
  827pairs_print_length([H|T], Options, Max, Len0, Len) :-
  828    pair_len(H, Options, Max, Len0, Len1),
  829    (   T == []
  830    ->  Len = Len1
  831    ;   Len2 is Len1 + 2,
  832        Len2 =< Max,
  833        pairs_print_length(T, Options, Max, Len2, Len)
  834    ).
  835
  836pair_len(Pair, Options, Max, Len0, Len) :-
  837    compound(Pair),
  838    pair_nv(Pair, Name, Value),
  839    !,
  840    string_len(Name, Len0, Len1),
  841    Len2 is Len1+2,
  842    Len2 =< Max,
  843    json_print_length(Value, Options, Max, Len2, Len).
  844pair_len(Pair, _Options, _Max, _Len0, _Len) :-
  845    type_error(pair, Pair).
  846
  847pair_nv(Name=Value, Name, Value) :- !.
  848pair_nv(Name-Value, Name, Value) :- !.
  849pair_nv(Term, Name, Value) :-
  850    compound_name_arguments(Term, Name, [Value]).
  851
  852array_print_length([], _, _, Len, Len).
  853array_print_length([H|T], Options, Max, Len0, Len) :-
  854    json_print_length(H, Options, Max, Len0, Len1),
  855    (   T == []
  856    ->  Len = Len1
  857    ;   Len2 is Len1+2,
  858        Len2 =< Max,
  859        array_print_length(T, Options, Max, Len2, Len)
  860    ).
  861
  862string_len(String, Len0, Len) :-
  863    atom(String),
  864    !,
  865    atom_length(String, AL),
  866    Len is Len0 + AL + 2.
  867string_len(String, Len0, Len) :-
  868    string(String),
  869    !,
  870    string_length(String, AL),
  871    Len is Len0 + AL + 2.
  872
  873
  874                 /*******************************
  875                 *             TEST             *
  876                 *******************************/
 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.
  885is_json_term(Term) :-
  886    default_json_options(Options),
  887    is_json_term2(Options, Term).
  888
  889is_json_term(Term, Options) :-
  890    make_json_options(Options, OptionTerm, _RestOptions),
  891    is_json_term2(OptionTerm, Term).
  892
  893is_json_term2(_, Var) :-
  894    var(Var), !, fail.
  895is_json_term2(Options, json(Pairs)) :-
  896    !,
  897    is_list(Pairs),
  898    maplist(is_json_pair(Options), Pairs).
  899is_json_term2(Options, List) :-
  900    is_list(List),
  901    !,
  902    maplist(is_json_term2(Options), List).
  903is_json_term2(_, Primitive) :-
  904    atomic(Primitive),
  905    !.           % atom, string or number
  906is_json_term2(Options, True) :-
  907    json_options_true(Options, True).
  908is_json_term2(Options, False) :-
  909    json_options_false(Options, False).
  910is_json_term2(Options, Null) :-
  911    json_options_null(Options, Null).
  912
  913is_json_pair(_, Var) :-
  914    var(Var), !, fail.
  915is_json_pair(Options, Name=Value) :-
  916    atom(Name),
  917    is_json_term2(Options, Value).
  918
  919:- if(current_predicate(is_dict/1)).  920
  921                 /*******************************
  922                 *         DICT SUPPORT         *
  923                 *******************************/
 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.
  964json_read_dict(Stream, Dict) :-
  965    json_read_dict(Stream, Dict, []).
  966
  967json_read_dict(Stream, Dict, Options) :-
  968    make_json_dict_options(Options, OptionTerm, _RestOptions),
  969    (   json_value_top(Stream, Term, OptionTerm)
  970    ->  true
  971    ;   syntax_error(illegal_json, Stream)
  972    ),
  973    term_to_dict(Term, Dict, OptionTerm).
  974
  975term_to_dict(json(Pairs), Dict, Options) :-
  976    !,
  977    (   json_options_tag(Options, TagName),
  978        Tag \== '',
  979        select(TagName = Tag0, Pairs, NVPairs),
  980        to_atom(Tag0, Tag)
  981    ->  json_dict_pairs(NVPairs, DictPairs, Options)
  982    ;   json_options_default_tag(Options, DefTag),
  983        (   var(DefTag)
  984        ->  true
  985        ;   Tag = DefTag
  986        ),
  987        json_dict_pairs(Pairs, DictPairs, Options)
  988    ),
  989    dict_create(Dict, Tag, DictPairs).
  990term_to_dict(Value0, Value, _Options) :-
  991    atomic(Value0), Value0 \== [],
  992    !,
  993    Value = Value0.
  994term_to_dict(List0, List, Options) :-
  995    is_list(List0),
  996    !,
  997    terms_to_dicts(List0, List, Options).
  998term_to_dict(Special, Special, Options) :-
  999    (   json_options_true(Options, Special)
 1000    ;   json_options_false(Options, Special)
 1001    ;   json_options_null(Options, Special)
 1002    ;   json_options_end_of_file(Options, Special)
 1003    ),
 1004    !.
 1005
 1006json_dict_pairs([], [], _).
 1007json_dict_pairs([Name=Value0|T0], [Name=Value|T], Options) :-
 1008    term_to_dict(Value0, Value, Options),
 1009    json_dict_pairs(T0, T, Options).
 1010
 1011terms_to_dicts([], [], _).
 1012terms_to_dicts([Value0|T0], [Value|T], Options) :-
 1013    term_to_dict(Value0, Value, Options),
 1014    terms_to_dicts(T0, T, Options).
 1015
 1016to_atom(Tag, Atom) :-
 1017    string(Tag),
 1018    !,
 1019    atom_string(Atom, Tag).
 1020to_atom(Atom, Atom) :-
 1021    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.
 1030json_write_dict(Stream, Dict) :-
 1031    json_write_dict(Stream, Dict, []).
 1032
 1033json_write_dict(Stream, Dict, Options) :-
 1034    make_json_write_state(Options, State, Options1),
 1035    make_json_dict_options(Options1, OptionTerm, _RestOptions),
 1036    json_write_term(Dict, Stream, State, OptionTerm).
 1037
 1038
 1039make_json_dict_options(Options, Record, RestOptions) :-
 1040    default_json_dict_options(Record0),
 1041    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.
 1054atom_json_dict(Atom, Term, Options) :-
 1055    ground(Atom),
 1056    !,
 1057    setup_call_cleanup(
 1058        ( text_memfile(Atom, MF),
 1059          open_memory_file(MF, read, In, [free_on_close(true)])
 1060        ),
 1061        json_read_dict(In, Term, Options),
 1062        close(In)).
 1063atom_json_dict(Result, Term, Options) :-
 1064    select_option(as(Type), Options, Options1, atom),
 1065    (   type_term(Type, Result, Out)
 1066    ->  true
 1067    ;   must_be(oneof([atom,string,codes]), Type)
 1068    ),
 1069    with_output_to(Out,
 1070                   json_write_dict(current_output, Term, Options1)).
 1071
 1072text_memfile(Atom, MF) :-
 1073    atom(Atom),
 1074    !,
 1075    atom_to_memory_file(Atom, MF).
 1076text_memfile(String, MF) :-
 1077    string(String),
 1078    !,
 1079    new_memory_file(MF),
 1080    insert_memory_file(MF, 0, String).
 1081
 1082:- endif. 1083
 1084                 /*******************************
 1085                 *           MESSAGES           *
 1086                 *******************************/
 1087
 1088:- multifile
 1089    prolog:error_message/3. 1090
 1091prolog:error_message(syntax_error(json(Id))) -->
 1092    [ 'JSON syntax error: ' ],
 1093    json_syntax_error(Id).
 1094
 1095json_syntax_error(illegal_comment) -->
 1096    [ 'Illegal comment' ].
 1097json_syntax_error(illegal_string_escape) -->
 1098    [ 'Illegal escape sequence in string' ]