View source with formatted 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                     ]).  100
  101/** <module> Reading and writing JSON serialization
  102
  103This module supports reading and  writing   JSON  objects.  This library
  104supports two Prolog representations (the   _new_  representation is only
  105supported in SWI-Prolog version 7 and later):
  106
  107  - The *classical* representation is provided by json_read/3 and
  108    json_write/3.  This represents a JSON object as json(NameValueList),
  109    a JSON string as an atom and the JSON constants =null=, =true= and
  110    =false= as @(null), @(true) and @false.
  111
  112  - The *new* representation is provided by json_read_dict/3 and
  113    json_write_dict/3. This represents a JSON object as a dict, a JSON
  114    string as a Prolog string and the JSON constants using the Prolog
  115    atoms =null=, =true= and =false=.
  116
  117@author Jan Wielemaker
  118@see    http_json.pl links JSON to the HTTP client and server modules.
  119@see    json_convert.pl converts JSON Prolog terms to more comfortable
  120terms.
  121*/
  122
  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                 *******************************/
  139
  140%!  atom_json_term(?Atom, ?JSONTerm, +Options) is det.
  141%
  142%   Convert between textual  representation  and   a  JSON  term. In
  143%   _write_ mode (JSONTerm to Atom), the option
  144%
  145%       * as(Type)
  146%       defines the output type, which is one of =atom= (default),
  147%       =string=, =codes= or =chars=.
  148
  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                 *******************************/
  176
  177%!  json_read(+Stream, -Term) is det.
  178%!  json_read(+Stream, -Term, +Options) is det.
  179%
  180%   Read next JSON value from Stream into a Prolog term. The
  181%   canonical representation for Term is:
  182%
  183%     * A JSON object is mapped to a term json(NameValueList), where
  184%       NameValueList is a list of Name=Value. Name is an atom
  185%       created from the JSON string.
  186%
  187%     * A JSON array is mapped to a Prolog list of JSON values.
  188%
  189%     * A JSON string is mapped to a Prolog atom
  190%
  191%     * A JSON number is mapped to a Prolog number
  192%
  193%     * The JSON constants =true= and =false= are mapped -like JPL-
  194%       to @(true) and @(false).
  195%
  196%     * The JSON constant =null= is mapped to the Prolog term
  197%       @(null)
  198%
  199%   Here is a complete example in  JSON and its corresponding Prolog
  200%   term.
  201%
  202%     ```
  203%     { "name":"Demo term",
  204%       "created": {
  205%         "day":null,
  206%         "month":"December",
  207%         "year":2007
  208%       },
  209%       "confirmed":true,
  210%       "members":[1,2,3]
  211%     }
  212%     ```
  213%
  214%     ```
  215%     json([ name='Demo term',
  216%            created=json([day= @null, month='December', year=2007]),
  217%            confirmed= @true,
  218%            members=[1, 2, 3]
  219%          ])
  220%     ```
  221%
  222%   The following options are processed:
  223%
  224%     - null(+NullTerm)
  225%       Term used to represent JSON =null=.  Default @(null)
  226%     - true(+TrueTerm)
  227%       Term used to represent JSON =true=.  Default @(true)
  228%     - false(+FalseTerm)
  229%       Term used to represent JSON =false=.  Default @(false)
  230%     - end_of_file(+ErrorOrTerm)
  231%       If end of file is reached after skipping white space
  232%       but before any input is processed take the following
  233%       action (default `error`):
  234%         - If ErrorOrTerm == `error`, throw an unexpected
  235%           end of file syntax error
  236%         - Otherwise return ErrorOrTerm.
  237%       Returning an status term is required to process
  238%       [Concatenated
  239%       JSON](https://en.wikipedia.org/wiki/JSON_streaming#Concatenated_JSON).
  240%       Suggested values are `@(eof)` or `end_of_file`.
  241%     - value_string_as(+Type)
  242%       Prolog type used for strings used as value. Default is `atom`.
  243%       The alternative is `string`, producing a packed string object.
  244%       Please note that `codes` or `chars` would produce ambiguous
  245%       output and are therefore not supported.
  246%
  247%   @see    json_read_dict/3 to read a JSON term using the version 7
  248%           extended data types.
  249
  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).
  429
  430%!  ws(+Stream, -Next) is det.
  431%!  ws(+C0, +Stream, -Next)
  432%
  433%   Skip white space on the Stream, returning the first non-ws
  434%   character.  Also skips =|//|= ... comments.
  435
  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                 *******************************/
  457
  458%!  json_write_string(+Stream, +Text) is det.
  459%
  460%   Write a JSON string to  Stream.  Stream   must  be  opened  in a
  461%   Unicode capable encoding, typically UTF-8.
  462
  463% foreign json_write_string/2.
  464
  465%!  json_write_indent(+Stream, +Indent, +TabDistance) is det.
  466%
  467%   Newline and indent to  Indent.  A   Newline  is  only written if
  468%   line_position(Stream, Pos) is not 0. Then   it  writes Indent //
  469%   TabDistance tab characters and Indent mode TabDistance spaces.
  470
  471% foreign json_write_indent/3.
  472
  473%!  json_write(+Stream, +Term) is det.
  474%!  json_write(+Stream, +Term, +Options) is det.
  475%
  476%   Write a JSON term to Stream. The JSON   object is of the same format
  477%   as  produced  by  json_read/2,  though  we    allow  for  some  more
  478%   flexibility with regard to pairs  in   objects.  All  of Name=Value,
  479%   Name-Value and Name(Value) produce the same output.
  480%
  481%   Values can be of  the  form  #(Term),   which  causes  `Term`  to be
  482%   _stringified_ if it is not  an   atom  or string. Stringification is
  483%   based on term_string/2.
  484%
  485%   Rational numbers are emitted as  floating   point  numbers. The hook
  486%   json_write_hook/4  can  be  used   to    realize   domain   specific
  487%   alternatives.
  488%
  489%   The version 7 _dict_ type is supported   as well. Optionally, if the
  490%   dict has a _tag_, a  property  "type":"tag"   can  be  added  to the
  491%   object. This behaviour can be controlled using the =tag= option (see
  492%   below). For example:
  493%
  494%     ==
  495%     ?- json_write(current_output, point{x:1,y:2}).
  496%     {
  497%       "x":1,
  498%       "y":2
  499%     }
  500%     ==
  501%
  502%     ==
  503%     ?- json_write(current_output, point{x:1,y:2}, [tag(type)]).
  504%     {
  505%       "type":"point",
  506%       "x":1,
  507%       "y":2
  508%     }
  509%     ==
  510%
  511%   In addition to the options recognised by json_read/3, we process
  512%   the following options are recognised:
  513%
  514%       * width(+Width)
  515%       Width in which we try to format the result.  Too long lines
  516%       switch from _horizontal_ to _vertical_ layout for better
  517%       readability. If performance is critical and human
  518%       readability is not an issue use Width = 0, which causes a
  519%       single-line output.
  520%
  521%       * step(+Step)
  522%       Indentation increnment for next level.  Default is 2.
  523%
  524%       * tab(+TabDistance)
  525%       Distance between tab-stops.  If equal to Step, layout
  526%       is generated with one tab per level.
  527%
  528%       * serialize_unknown(+Boolean)
  529%       If =true= (default =false=), serialize unknown terms and
  530%       print them as a JSON string.  The default raises a type
  531%       error.  Note that this option only makes sense if you can
  532%       guarantee that the passed value is not an otherwise valid
  533%       Prolog reporesentation of a Prolog term.
  534%
  535%   If a string is  emitted,  the   sequence  =|</|=  is  emitted as
  536%   =|<\/|=. This is valid  JSON  syntax   which  ensures  that JSON
  537%   objects  can  be  safely  embedded  into  an  HTML  =|<script>|=
  538%   element.
  539
  540%!  json_write_hook(+Term, +Stream, +State, +Options) is semidet.
  541%
  542%   Hook that can be used to  emit   a  JSON  representation for Term to
  543%   Stream. If the  predicate  succeeds  it   __must__  have  written  a
  544%   __valid__ JSON data element and if it fails it may not have produced
  545%   any output. This facility may be used  to map arbitrary Prolog terms
  546%   to JSON. It was added to manage   the  precision with which floating
  547%   point numbers are emitted.
  548%
  549%   Note that this hook is shared by all   users  of this library. It is
  550%   generally  adviced  to  map  a  unique    compound   term  to  avoid
  551%   interference with normal output.
  552%
  553%   @arg State and Options are opaque handles to the current output
  554%   state and settings.  Future versions may provide documented access
  555%   to these terms.  Currently it is adviced to ignore these arguments.
  556
  557%!  json_dict_pairs(+Dict, -Pairs) is semidet.
  558%
  559%   This hook may be used to order the   keys of an object. If it fails,
  560%   dict_pairs/3 is used which produces an ordered list of keys.
  561
  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, ' ').
  768
  769
  770%!  json_print_length(+Value, +Options, +Max, +Len0, +Len) is semidet.
  771%
  772%   True if Len-Len0 is the print-length of Value on a single line
  773%   and Len-Len0 =< Max.
  774%
  775%   @tbd    Escape sequences in strings are not considered.
  776
  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                 *******************************/
  887
  888%!  is_json_term(@Term) is semidet.
  889%!  is_json_term(@Term, +Options) is semidet.
  890%
  891%   True if Term is  a  json  term.   Options  are  the  same as for
  892%   json_read/2, defining the Prolog  representation   for  the JSON
  893%   =true=, =false= and =null= constants.
  894
  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                 *******************************/
  932
  933%!  json_read_dict(+Stream, -Dict) is det.
  934%!  json_read_dict(+Stream, -Dict, +Options) is det.
  935%
  936%   Read  a  JSON  object,  returning  objects    as  a  dicts.  The
  937%   representation depends on the options, where the default is:
  938%
  939%     * String values are mapped to Prolog strings
  940%     * JSON =true=, =false= and =null= are represented using these
  941%       Prolog atoms.
  942%     * JSON objects are mapped to dicts.
  943%     * Optionally, a =type= field in an object assigns a tag for
  944%       the dict.
  945%
  946%   The predicate json_read_dict/3 processes  the   same  options as
  947%   json_read/3,  but  with  different  defaults.  In  addition,  it
  948%   processes the `tag` option. See   json_read/3  for details about
  949%   the shared options.
  950%
  951%     * tag(+Name)
  952%       When converting to/from a dict, map the indicated JSON
  953%       attribute to the dict _tag_. No mapping is performed if Name
  954%       is the empty atom ('', default). See json_read_dict/2 and
  955%       json_write_dict/2.
  956%     * default_tag(+Tag)
  957%       Provide the default tag if the above `tag` option does not
  958%       apply.
  959%     * null(+NullTerm)
  960%       Default the atom `null`.
  961%     * true(+TrueTerm)
  962%       Default the atom `true`.
  963%     * false(+FalseTerm)
  964%       Default the atom `false`
  965%     * end_of_file(+ErrorOrTerm)
  966%       Action on reading end-of-file. See json_read/3 for details.
  967%     * value_string_as(+Type)
  968%       Prolog type used for strings used as value.  Default
  969%       is `string`.  The alternative is `atom`, producing a
  970%       packed string object.
  971
  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).
 1030
 1031%!  json_write_dict(+Stream, +Dict) is det.
 1032%!  json_write_dict(+Stream, +Dict, +Options) is det.
 1033%
 1034%   Write a JSON term, represented using dicts.  This is the same as
 1035%   json_write/3, but assuming the default   representation  of JSON
 1036%   objects as dicts.
 1037
 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).
 1050
 1051%!  atom_json_dict(+Atom, -JSONDict, +Options) is det.
 1052%!  atom_json_dict(-Text, +JSONDict, +Options) is det.
 1053%
 1054%   Convert  between  textual  representation  and    a   JSON  term
 1055%   represented as a dict. Options are as for json_read/3.
 1056%   In _write_ mode, the addtional option
 1057%
 1058%       * as(Type)
 1059%       defines the output type, which is one of =atom=,
 1060%       =string= or =codes=.
 1061
 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' ]