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-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                     ]).  115
  116/** <module> Reading and writing JSON serialization
  117
  118This module supports reading and  writing   JSON  objects.  This library
  119supports two Prolog representations (the   _new_  representation is only
  120supported in SWI-Prolog version 7 and later):
  121
  122  - The *classical* representation is provided by json_read/3 and
  123    json_write/3.  This represents a JSON object as json(NameValueList),
  124    a JSON string as an atom and the JSON constants =null=, =true= and
  125    =false= as @(null), @(true) and @false.
  126
  127  - The *new* representation is provided by json_read_dict/3 and
  128    json_write_dict/3. This represents a JSON object as a dict, a JSON
  129    string as a Prolog string and the JSON constants using the Prolog
  130    atoms =null=, =true= and =false=.
  131
  132@author Jan Wielemaker
  133@see    http_json.pl links JSON to the HTTP client and server modules.
  134@see    json_convert.pl converts JSON Prolog terms to more comfortable
  135terms.
  136*/
  137
  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                 *******************************/
  154
  155%!  atom_json_term(?Atom, ?JSONTerm, +Options) is det.
  156%
  157%   Convert between textual  representation  and   a  JSON  term. In
  158%   _write_ mode (JSONTerm to Atom), the option
  159%
  160%       * as(Type)
  161%       defines the output type, which is one of =atom= (default),
  162%       =string=, =codes= or =chars=.
  163
  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                 *******************************/
  191
  192%!  json_read(+Stream, -Term) is det.
  193%!  json_read(+Stream, -Term, +Options) is det.
  194%
  195%   Read next JSON value from Stream into a Prolog term. The
  196%   canonical representation for Term is:
  197%
  198%     * A JSON object is mapped to a term json(NameValueList), where
  199%       NameValueList is a list of Name=Value. Name is an atom
  200%       created from the JSON string.
  201%
  202%     * A JSON array is mapped to a Prolog list of JSON values.
  203%
  204%     * A JSON string is mapped to a Prolog atom
  205%
  206%     * A JSON number is mapped to a Prolog number
  207%
  208%     * The JSON constants =true= and =false= are mapped -like JPL-
  209%       to @(true) and @(false).
  210%
  211%     * The JSON constant =null= is mapped to the Prolog term
  212%       @(null)
  213%
  214%   Here is a complete example in  JSON and its corresponding Prolog
  215%   term.
  216%
  217%     ```
  218%     { "name":"Demo term",
  219%       "created": {
  220%         "day":null,
  221%         "month":"December",
  222%         "year":2007
  223%       },
  224%       "confirmed":true,
  225%       "members":[1,2,3]
  226%     }
  227%     ```
  228%
  229%     ```
  230%     json([ name='Demo term',
  231%            created=json([day= @null, month='December', year=2007]),
  232%            confirmed= @true,
  233%            members=[1, 2, 3]
  234%          ])
  235%     ```
  236%
  237%   The following options are processed:
  238%
  239%     - null(+NullTerm)
  240%       Term used to represent JSON =null=.  Default @(null)
  241%     - true(+TrueTerm)
  242%       Term used to represent JSON =true=.  Default @(true)
  243%     - false(+FalseTerm)
  244%       Term used to represent JSON =false=.  Default @(false)
  245%     - end_of_file(+ErrorOrTerm)
  246%       If end of file is reached after skipping white space
  247%       but before any input is processed take the following
  248%       action (default `error`):
  249%         - If ErrorOrTerm == `error`, throw an unexpected
  250%           end of file syntax error
  251%         - Otherwise return ErrorOrTerm.
  252%       Returning an status term is required to process
  253%       [Concatenated
  254%       JSON](https://en.wikipedia.org/wiki/JSON_streaming#Concatenated_JSON).
  255%       Suggested values are `@(eof)` or `end_of_file`.
  256%     - value_string_as(+Type)
  257%       Prolog type used for strings used as value. Default is `atom`.
  258%       The alternative is `string`, producing a packed string object.
  259%       Please note that `codes` or `chars` would produce ambiguous
  260%       output and are therefore not supported.
  261%
  262%   @see    json_read_dict/3 to read a JSON term using the version 7
  263%           extended data types.
  264
  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).
  434
  435%!  ws(+Stream, -Next) is det.
  436%!  ws(+C0, +Stream, -Next)
  437%
  438%   Skip white space on the Stream, returning the first non-ws
  439%   character.  Also skips =|//|= ... comments.
  440
  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                 *******************************/
  462
  463%!  json_write_string(+Stream, +Text) is det.
  464%
  465%   Write a JSON string to  Stream.  Stream   must  be  opened  in a
  466%   Unicode capable encoding, typically UTF-8.
  467
  468% foreign json_write_string/2.
  469
  470%!  json_write_indent(+Stream, +Indent, +TabDistance) is det.
  471%
  472%   Newline and indent to  Indent.  A   Newline  is  only written if
  473%   line_position(Stream, Pos) is not 0. Then   it  writes Indent //
  474%   TabDistance tab characters and Indent mode TabDistance spaces.
  475
  476% foreign json_write_indent/3.
  477
  478%!  json_write(+Stream, +Term) is det.
  479%!  json_write(+Stream, +Term, +Options) is det.
  480%
  481%   Write a JSON term to Stream.  The   JSON  object  is of the same
  482%   format as produced by json_read/2, though we allow for some more
  483%   flexibility with regard to pairs in  objects. All of Name=Value,
  484%   Name-Value and Name(Value) produce the  same output.
  485%
  486%   Values can be of the form  #(Term),   which  causes `Term` to be
  487%   _stringified_ if it is not an atom or string. Stringification is
  488%   based on term_string/2.
  489%
  490%   The version 7 _dict_ type is supported as well.  Optionally,  if
  491%   the dict has a _tag_,  a property  "type":"tag"  can be added to
  492%   the object.  This behaviour can be controlled using the  =tag=
  493%   option (see below). For example:
  494%
  495%     ==
  496%     ?- json_write(current_output, point{x:1,y:2}).
  497%     {
  498%       "x":1,
  499%       "y":2
  500%     }
  501%     ==
  502%
  503%     ==
  504%     ?- json_write(current_output, point{x:1,y:2}, [tag(type)]).
  505%     {
  506%       "type":"point",
  507%       "x":1,
  508%       "y":2
  509%     }
  510%     ==
  511%
  512%   In addition to the options recognised by json_read/3, we process
  513%   the following options are recognised:
  514%
  515%       * width(+Width)
  516%       Width in which we try to format the result.  Too long lines
  517%       switch from _horizontal_ to _vertical_ layout for better
  518%       readability. If performance is critical and human
  519%       readability is not an issue use Width = 0, which causes a
  520%       single-line output.
  521%
  522%       * step(+Step)
  523%       Indentation increnment for next level.  Default is 2.
  524%
  525%       * tab(+TabDistance)
  526%       Distance between tab-stops.  If equal to Step, layout
  527%       is generated with one tab per level.
  528%
  529%       * serialize_unknown(+Boolean)
  530%       If =true= (default =false=), serialize unknown terms and
  531%       print them as a JSON string.  The default raises a type
  532%       error.  Note that this option only makes sense if you can
  533%       guarantee that the passed value is not an otherwise valid
  534%       Prolog reporesentation of a Prolog term.
  535%
  536%   If a string is  emitted,  the   sequence  =|</|=  is  emitted as
  537%   =|<\/|=. This is valid  JSON  syntax   which  ensures  that JSON
  538%   objects  can  be  safely  embedded  into  an  HTML  =|<script>|=
  539%   element.
  540
  541%!  json_write_hook(+Term, +Stream, +State, +Options) is semidet.
  542%
  543%   Hook that can be used to  emit   a  JSON  representation for Term to
  544%   Stream. If the  predicate  succeeds  it   __must__  have  written  a
  545%   __valid__ JSON data element and if it fails it may not have produced
  546%   any output. This facility may be used  to map arbitrary Prolog terms
  547%   to JSON. It was added to manage   the  precision with which floating
  548%   point numbers are emitted.
  549%
  550%   Note that this hook is shared by all   users  of this library. It is
  551%   generally  adviced  to  map  a  unique    compound   term  to  avoid
  552%   interference with normal output.
  553%
  554%   @arg State and Options are opaque handles to the current output
  555%   state and settings.  Future versions may provide documented access
  556%   to these terms.  Currently it is adviced to ignore these arguments.
  557
  558
  559
  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, ' ').
  756
  757
  758%!  json_print_length(+Value, +Options, +Max, +Len0, +Len) is semidet.
  759%
  760%   True if Len-Len0 is the print-length of Value on a single line
  761%   and Len-Len0 =< Max.
  762%
  763%   @tbd    Escape sequences in strings are not considered.
  764
  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                 *******************************/
  877
  878%!  is_json_term(@Term) is semidet.
  879%!  is_json_term(@Term, +Options) is semidet.
  880%
  881%   True if Term is  a  json  term.   Options  are  the  same as for
  882%   json_read/2, defining the Prolog  representation   for  the JSON
  883%   =true=, =false= and =null= constants.
  884
  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                 *******************************/
  924
  925%!  json_read_dict(+Stream, -Dict) is det.
  926%!  json_read_dict(+Stream, -Dict, +Options) is det.
  927%
  928%   Read  a  JSON  object,  returning  objects    as  a  dicts.  The
  929%   representation depends on the options, where the default is:
  930%
  931%     * String values are mapped to Prolog strings
  932%     * JSON =true=, =false= and =null= are represented using these
  933%       Prolog atoms.
  934%     * JSON objects are mapped to dicts.
  935%     * Optionally, a =type= field in an object assigns a tag for
  936%       the dict.
  937%
  938%   The predicate json_read_dict/3 processes  the   same  options as
  939%   json_read/3,  but  with  different  defaults.  In  addition,  it
  940%   processes the `tag` option. See   json_read/3  for details about
  941%   the shared options.
  942%
  943%     * tag(+Name)
  944%       When converting to/from a dict, map the indicated JSON
  945%       attribute to the dict _tag_. No mapping is performed if Name
  946%       is the empty atom ('', default). See json_read_dict/2 and
  947%       json_write_dict/2.
  948%     * default_tag(+Tag)
  949%       Provide the default tag if the above `tag` option does not
  950%       apply.
  951%     * null(+NullTerm)
  952%       Default the atom `null`.
  953%     * true(+TrueTerm)
  954%       Default the atom `true`.
  955%     * false(+FalseTerm)
  956%       Default the atom `false`
  957%     * end_of_file(+ErrorOrTerm)
  958%       Action on reading end-of-file. See json_read/3 for details.
  959%     * value_string_as(+Type)
  960%       Prolog type used for strings used as value.  Default
  961%       is `string`.  The alternative is `atom`, producing a
  962%       packed string object.
  963
  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).
 1022
 1023%!  json_write_dict(+Stream, +Dict) is det.
 1024%!  json_write_dict(+Stream, +Dict, +Options) is det.
 1025%
 1026%   Write a JSON term, represented using dicts.  This is the same as
 1027%   json_write/3, but assuming the default   representation  of JSON
 1028%   objects as dicts.
 1029
 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).
 1042
 1043%!  atom_json_dict(+Atom, -JSONDict, +Options) is det.
 1044%!  atom_json_dict(-Text, +JSONDict, +Options) is det.
 1045%
 1046%   Convert  between  textual  representation  and    a   JSON  term
 1047%   represented as a dict. Options are as for json_read/3.
 1048%   In _write_ mode, the addtional option
 1049%
 1050%       * as(Type)
 1051%       defines the output type, which is one of =atom=,
 1052%       =string= or =codes=.
 1053
 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' ]