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-2020, 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:- module(json,
   38          [ json_read/2,                % +Stream, -JSONTerm
   39            json_read/3,                % +Stream, -JSONTerm, +Options
   40            atom_json_term/3,           % ?Atom, ?JSONTerm, +Options
   41            json_write/2,               % +Stream, +Term
   42            json_write/3,               % +Stream, +Term, +Options
   43            is_json_term/1,             % @Term
   44            is_json_term/2,             % @Term, +Options
   45                                        % Version 7 dict support
   46            json_read_dict/2,           % +Stream, -Dict
   47            json_read_dict/3,           % +Stream, -Dict, +Options
   48            json_write_dict/2,          % +Stream, +Dict
   49            json_write_dict/3,          % +Stream, +Dict, +Options
   50            atom_json_dict/3            % ?Atom, ?JSONDict, +Options
   51          ]).   52:- use_module(library(record)).   53:- use_module(library(memfile)).   54:- use_module(library(error)).   55:- use_module(library(option)).   56:- use_module(library(lists)).   57
   58:- use_foreign_library(foreign(json)).   59
   60:- multifile
   61    json_write_hook/4.                          % +Term, +Stream, +State, +Options
   62
   63:- predicate_options(json_read/3, 3,
   64                     [ null(ground),
   65                       true(ground),
   66                       false(ground),
   67                       value_string_as(oneof([atom,string]))
   68                     ]).   69:- predicate_options(json_write/3, 3,
   70                     [ indent(nonneg),
   71                       step(positive_integer),
   72                       tab(positive_integer),
   73                       width(nonneg),
   74                       null(ground),
   75                       true(ground),
   76                       false(ground),
   77                       serialize_unknown(boolean)
   78                     ]).   79:- predicate_options(json_read_dict/3, 3,
   80                     [ tag(atom),
   81                       default_tag(atom),
   82                       pass_to(json_read/3, 3)
   83                     ]).   84:- predicate_options(json_write_dict/3, 3,
   85                     [ tag(atom),
   86                       pass_to(json_write/3, 3)
   87                     ]).   88:- predicate_options(is_json_term/2, 2,
   89                     [ null(ground),
   90                       true(ground),
   91                       false(ground)
   92                     ]).   93:- predicate_options(atom_json_term/3, 3,
   94                     [ as(oneof([atom,string,codes])),
   95                       pass_to(json_read/3, 3),
   96                       pass_to(json_write/3, 3)
   97                     ]).   98
   99/** <module> Reading and writing JSON serialization
  100
  101This module supports reading and  writing   JSON  objects.  This library
  102supports two Prolog representations (the   _new_  representation is only
  103supported in SWI-Prolog version 7 and later):
  104
  105  - The *classical* representation is provided by json_read/3 and
  106    json_write/3.  This represents a JSON object as json(NameValueList),
  107    a JSON string as an atom and the JSON constants =null=, =true= and
  108    =false= as @(null), @(true) and @false.
  109
  110  - The *new* representation is provided by json_read_dict/3 and
  111    json_write_dict/3. This represents a JSON object as a dict, a JSON
  112    string as a Prolog string and the JSON constants using the Prolog
  113    atoms =null=, =true= and =false=.
  114
  115@author Jan Wielemaker
  116@see    http_json.pl links JSON to the HTTP client and server modules.
  117@see    json_convert.pl converts JSON Prolog terms to more comfortable
  118terms.
  119*/
  120
  121:- record json_options(
  122              null:ground = @(null),
  123              true:ground = @(true),
  124              false:ground = @(false),
  125              end_of_file:ground = error,
  126              value_string_as:oneof([atom,string]) = atom,
  127              tag:atom = '',
  128              default_tag:atom).  129
  130default_json_dict_options(
  131    json_options(null, true, false, error, string, '', _)).
  132
  133
  134                 /*******************************
  135                 *       MAP TO/FROM TEXT       *
  136                 *******************************/
  137
  138%!  atom_json_term(?Atom, ?JSONTerm, +Options) is det.
  139%
  140%   Convert between textual  representation  and   a  JSON  term. In
  141%   _write_ mode (JSONTerm to Atom), the option
  142%
  143%       * as(Type)
  144%       defines the output type, which is one of =atom= (default),
  145%       =string=, =codes= or =chars=.
  146
  147atom_json_term(Atom, Term, Options) :-
  148    ground(Atom),
  149    !,
  150    setup_call_cleanup(
  151        ( atom_to_memory_file(Atom, MF),
  152          open_memory_file(MF, read, In, [free_on_close(true)])
  153        ),
  154        json_read(In, Term, Options),
  155        close(In)).
  156atom_json_term(Result, Term, Options) :-
  157    select_option(as(Type), Options, Options1, atom),
  158    (   type_term(Type, Result, Out)
  159    ->  true
  160    ;   must_be(oneof([atom,string,codes,chars]), Type)
  161    ),
  162    with_output_to(Out,
  163                   json_write(current_output, Term, Options1)).
  164
  165type_term(atom,   Result, atom(Result)).
  166type_term(string, Result, string(Result)).
  167type_term(codes,  Result, codes(Result)).
  168type_term(chars,  Result, chars(Result)).
  169
  170
  171                 /*******************************
  172                 *           READING            *
  173                 *******************************/
  174
  175%!  json_read(+Stream, -Term) is det.
  176%!  json_read(+Stream, -Term, +Options) is det.
  177%
  178%   Read next JSON value from Stream into a Prolog term. The
  179%   canonical representation for Term is:
  180%
  181%     * A JSON object is mapped to a term json(NameValueList), where
  182%       NameValueList is a list of Name=Value. Name is an atom
  183%       created from the JSON string.
  184%
  185%     * A JSON array is mapped to a Prolog list of JSON values.
  186%
  187%     * A JSON string is mapped to a Prolog atom
  188%
  189%     * A JSON number is mapped to a Prolog number
  190%
  191%     * The JSON constants =true= and =false= are mapped -like JPL-
  192%       to @(true) and @(false).
  193%
  194%     * The JSON constant =null= is mapped to the Prolog term
  195%       @(null)
  196%
  197%   Here is a complete example in  JSON and its corresponding Prolog
  198%   term.
  199%
  200%     ```
  201%     { "name":"Demo term",
  202%       "created": {
  203%         "day":null,
  204%         "month":"December",
  205%         "year":2007
  206%       },
  207%       "confirmed":true,
  208%       "members":[1,2,3]
  209%     }
  210%     ```
  211%
  212%     ```
  213%     json([ name='Demo term',
  214%            created=json([day= @null, month='December', year=2007]),
  215%            confirmed= @true,
  216%            members=[1, 2, 3]
  217%          ])
  218%     ```
  219%
  220%   The following options are processed:
  221%
  222%     - null(+NullTerm)
  223%       Term used to represent JSON =null=.  Default @(null)
  224%     - true(+TrueTerm)
  225%       Term used to represent JSON =true=.  Default @(true)
  226%     - false(+FalseTerm)
  227%       Term used to represent JSON =false=.  Default @(false)
  228%     - end_of_file(+ErrorOrTerm)
  229%       If end of file is reached after skipping white space
  230%       but before any input is processed take the following
  231%       action (default `error`):
  232%         - If ErrorOrTerm == `error`, throw an unexpected
  233%           end of file syntax error
  234%         - Otherwise return ErrorOrTerm.
  235%       Returning an status term is required to process
  236%       [Concatenated
  237%       JSON](https://en.wikipedia.org/wiki/JSON_streaming#Concatenated_JSON).
  238%       Suggested values are `@(eof)` or `end_of_file`.
  239%     - value_string_as(+Type)
  240%       Prolog type used for strings used as value. Default is `atom`.
  241%       The alternative is `string`, producing a packed string object.
  242%       Please note that `codes` or `chars` would produce ambiguous
  243%       output and are therefore not supported.
  244%
  245%   @see    json_read_dict/3 to read a JSON term using the version 7
  246%           extended data types.
  247
  248json_read(Stream, Term) :-
  249    default_json_options(Options),
  250    (   json_value_top(Stream, Term, Options)
  251    ->  true
  252    ;   syntax_error(illegal_json, Stream)
  253    ).
  254json_read(Stream, Term, Options) :-
  255    make_json_options(Options, OptionTerm, _RestOptions),
  256    (   json_value_top(Stream, Term, OptionTerm)
  257    ->  true
  258    ;   syntax_error(illegal_json, Stream)
  259    ).
  260
  261json_value_top(Stream, Term, Options) :-
  262    stream_property(Stream, type(binary)),
  263    !,
  264    setup_call_cleanup(
  265        set_stream(Stream, encoding(utf8)),
  266        json_value_top_(Stream, Term, Options),
  267        set_stream(Stream, type(binary))).
  268json_value_top(Stream, Term, Options) :-
  269    json_value_top_(Stream, Term, Options).
  270
  271json_value_top_(Stream, Term, Options) :-
  272    get_code(Stream, C0),
  273    ws(C0, Stream, C1),
  274    (   C1 == -1
  275    ->  json_options_end_of_file(Options, Action),
  276        (   Action == error
  277        ->  syntax_error(unexpected_end_of_file, Stream)
  278        ;   Term = Action
  279        )
  280    ;   json_term_top(C1, Stream, Term, Options)
  281    ).
  282
  283json_value(Stream, Term, Next, Options) :-
  284    get_code(Stream, C0),
  285    ws(C0, Stream, C1),
  286    (   C1 == -1
  287    ->  syntax_error(unexpected_end_of_file, Stream)
  288    ;   json_term(C1, Stream, Term, Next, Options)
  289    ).
  290
  291json_term(C0, Stream, JSON, Next, Options) :-
  292    json_term_top(C0, Stream, JSON, Options),
  293    get_code(Stream, Next).
  294
  295json_term_top(0'{, Stream, json(Pairs), Options) :-
  296    !,
  297    ws(Stream, C),
  298    json_pairs(C, Stream, Pairs, Options).
  299json_term_top(0'[, Stream, Array, Options) :-
  300    !,
  301    ws(Stream, C),
  302    json_array(C, Stream, Array, Options).
  303json_term_top(0'", Stream, String, Options) :-
  304    !,
  305    get_code(Stream, C1),
  306    json_string_codes(C1, Stream, Codes),
  307    json_options_value_string_as(Options, Type),
  308    codes_to_type(Type, Codes, String).
  309json_term_top(0'-, Stream, Number, _Options) :-
  310    !,
  311    json_read_number(Stream, 0'-, Number).
  312json_term_top(D, Stream, Number, _Options) :-
  313    between(0'0, 0'9, D),
  314    !,
  315    json_read_number(Stream, D, Number).
  316json_term_top(C, Stream, Constant, Options) :-
  317    json_read_constant(C, Stream, ID),
  318    json_constant(ID, Constant, Options).
  319
  320json_pairs(0'}, _, [], _) :- !.
  321json_pairs(C0, Stream, [Pair|Tail], Options) :-
  322    json_pair(C0, Stream, Pair, C, Options),
  323    ws(C, Stream, Next),
  324    (   Next == 0',
  325    ->  ws(Stream, C2),
  326        json_pairs(C2, Stream, Tail, Options)
  327    ;   Next == 0'}
  328    ->  Tail = []
  329    ;   syntax_error(illegal_object, Stream)
  330    ).
  331
  332json_pair(C0, Stream, Name=Value, Next, Options) :-
  333    json_string_as_atom(C0, Stream, Name),
  334    ws(Stream, C),
  335    C == 0':,
  336    json_value(Stream, Value, Next, Options).
  337
  338
  339json_array(0'], _, [], _) :- !.
  340json_array(C0, Stream, [Value|Tail], Options) :-
  341    json_term(C0, Stream, Value, C, Options),
  342    ws(C, Stream, Next),
  343    (   Next == 0',
  344    ->  ws(Stream, C1),
  345        json_array(C1, Stream, Tail, Options)
  346    ;   Next == 0']
  347    ->  Tail = []
  348    ;   syntax_error(illegal_array, Stream)
  349    ).
  350
  351codes_to_type(atom, Codes, Atom) :-
  352    atom_codes(Atom, Codes).
  353codes_to_type(string, Codes, Atom) :-
  354    string_codes(Atom, Codes).
  355codes_to_type(codes, Codes, Codes).
  356
  357json_string_as_atom(0'", Stream, Atom) :-
  358    get_code(Stream, C1),
  359    json_string_codes(C1, Stream, Codes),
  360    atom_codes(Atom, Codes).
  361
  362json_string_codes(0'", _, []) :- !.
  363json_string_codes(0'\\, Stream, [H|T]) :-
  364    !,
  365    get_code(Stream, C0),
  366    (   escape(C0, Stream, H)
  367    ->  true
  368    ;   syntax_error(illegal_string_escape, Stream)
  369    ),
  370    get_code(Stream, C1),
  371    json_string_codes(C1, Stream, T).
  372json_string_codes(-1, Stream, _) :-
  373    !,
  374    syntax_error(eof_in_string, Stream).
  375json_string_codes(C, Stream, [C|T]) :-
  376    get_code(Stream, C1),
  377    json_string_codes(C1, Stream, T).
  378
  379escape(0'", _, 0'") :- !.
  380escape(0'\\, _, 0'\\) :- !.
  381escape(0'/, _, 0'/) :- !.
  382escape(0'b, _, 0'\b) :- !.
  383escape(0'f, _, 0'\f) :- !.
  384escape(0'n, _, 0'\n) :- !.
  385escape(0'r, _, 0'\r) :- !.
  386escape(0't, _, 0'\t) :- !.
  387escape(0'u, Stream, C) :-
  388    !,
  389    get_code(Stream, C1),
  390    get_code(Stream, C2),
  391    get_code(Stream, C3),
  392    get_code(Stream, C4),
  393    code_type(C1, xdigit(D1)),
  394    code_type(C2, xdigit(D2)),
  395    code_type(C3, xdigit(D3)),
  396    code_type(C4, xdigit(D4)),
  397    C is D1<<12+D2<<8+D3<<4+D4.
  398
  399json_read_constant(0't, Stream, true) :-
  400    !,
  401    must_see(`rue`, Stream, true).
  402json_read_constant(0'f, Stream, false) :-
  403    !,
  404    must_see(`alse`, Stream, false).
  405json_read_constant(0'n, Stream, null) :-
  406    !,
  407    must_see(`ull`, Stream, null).
  408
  409must_see([], _Stream, _).
  410must_see([H|T], Stream, Name) :-
  411    get_code(Stream, C),
  412    (   C == H
  413    ->  true
  414    ;   syntax_error(json_expected(Name), Stream)
  415    ),
  416    must_see(T, Stream, Name).
  417
  418json_constant(true, Constant, Options) :-
  419    !,
  420    json_options_true(Options, Constant).
  421json_constant(false, Constant, Options) :-
  422    !,
  423    json_options_false(Options, Constant).
  424json_constant(null, Constant, Options) :-
  425    !,
  426    json_options_null(Options, Constant).
  427
  428%!  ws(+Stream, -Next) is det.
  429%!  ws(+C0, +Stream, -Next)
  430%
  431%   Skip white space on the Stream, returning the first non-ws
  432%   character.  Also skips =|//|= ... comments.
  433
  434ws(Stream, Next) :-
  435    get_code(Stream, C0),
  436    json_skip_ws(Stream, C0, Next).
  437
  438ws(C0, Stream, Next) :-
  439    json_skip_ws(Stream, C0, Next).
  440
  441syntax_error(Message, Stream) :-
  442    stream_error_context(Stream, Context),
  443    throw(error(syntax_error(json(Message)), Context)).
  444
  445stream_error_context(Stream, stream(Stream, Line, LinePos, CharNo)) :-
  446    stream_pair(Stream, Read, _),
  447    character_count(Read, CharNo),
  448    line_position(Read, LinePos),
  449    line_count(Read, Line).
  450
  451
  452                 /*******************************
  453                 *          JSON OUTPUT         *
  454                 *******************************/
  455
  456%!  json_write_string(+Stream, +Text) is det.
  457%
  458%   Write a JSON string to  Stream.  Stream   must  be  opened  in a
  459%   Unicode capable encoding, typically UTF-8.
  460
  461% foreign json_write_string/2.
  462
  463%!  json_write_indent(+Stream, +Indent, +TabDistance) is det.
  464%
  465%   Newline and indent to  Indent.  A   Newline  is  only written if
  466%   line_position(Stream, Pos) is not 0. Then   it  writes Indent //
  467%   TabDistance tab characters and Indent mode TabDistance spaces.
  468
  469% foreign json_write_indent/3.
  470
  471%!  json_write(+Stream, +Term) is det.
  472%!  json_write(+Stream, +Term, +Options) is det.
  473%
  474%   Write a JSON term to Stream. The JSON   object is of the same format
  475%   as  produced  by  json_read/2,  though  we    allow  for  some  more
  476%   flexibility with regard to pairs  in   objects.  All  of Name=Value,
  477%   Name-Value and Name(Value) produce the same output.
  478%
  479%   Values can be of  the  form  #(Term),   which  causes  `Term`  to be
  480%   _stringified_ if it is not  an   atom  or string. Stringification is
  481%   based on term_string/2.
  482%
  483%   Rational numbers are emitted as  floating   point  numbers. The hook
  484%   json_write_hook/4  can  be  used   to    realize   domain   specific
  485%   alternatives.
  486%
  487%   The version 7 _dict_ type is supported   as well. Optionally, if the
  488%   dict has a _tag_, a  property  "type":"tag"   can  be  added  to the
  489%   object. This behaviour can be controlled using the =tag= option (see
  490%   below). For example:
  491%
  492%     ==
  493%     ?- json_write(current_output, point{x:1,y:2}).
  494%     {
  495%       "x":1,
  496%       "y":2
  497%     }
  498%     ==
  499%
  500%     ==
  501%     ?- json_write(current_output, point{x:1,y:2}, [tag(type)]).
  502%     {
  503%       "type":"point",
  504%       "x":1,
  505%       "y":2
  506%     }
  507%     ==
  508%
  509%   In addition to the options recognised by json_read/3, we process
  510%   the following options are recognised:
  511%
  512%       * width(+Width)
  513%       Width in which we try to format the result.  Too long lines
  514%       switch from _horizontal_ to _vertical_ layout for better
  515%       readability. If performance is critical and human
  516%       readability is not an issue use Width = 0, which causes a
  517%       single-line output.
  518%
  519%       * step(+Step)
  520%       Indentation increnment for next level.  Default is 2.
  521%
  522%       * tab(+TabDistance)
  523%       Distance between tab-stops.  If equal to Step, layout
  524%       is generated with one tab per level.
  525%
  526%       * serialize_unknown(+Boolean)
  527%       If =true= (default =false=), serialize unknown terms and
  528%       print them as a JSON string.  The default raises a type
  529%       error.  Note that this option only makes sense if you can
  530%       guarantee that the passed value is not an otherwise valid
  531%       Prolog reporesentation of a Prolog term.
  532%
  533%   If a string is  emitted,  the   sequence  =|</|=  is  emitted as
  534%   =|<\/|=. This is valid  JSON  syntax   which  ensures  that JSON
  535%   objects  can  be  safely  embedded  into  an  HTML  =|<script>|=
  536%   element.
  537
  538%!  json_write_hook(+Term, +Stream, +State, +Options) is semidet.
  539%
  540%   Hook that can be used to  emit   a  JSON  representation for Term to
  541%   Stream. If the  predicate  succeeds  it   __must__  have  written  a
  542%   __valid__ JSON data element and if it fails it may not have produced
  543%   any output. This facility may be used  to map arbitrary Prolog terms
  544%   to JSON. It was added to manage   the  precision with which floating
  545%   point numbers are emitted.
  546%
  547%   Note that this hook is shared by all   users  of this library. It is
  548%   generally  adviced  to  map  a  unique    compound   term  to  avoid
  549%   interference with normal output.
  550%
  551%   @arg State and Options are opaque handles to the current output
  552%   state and settings.  Future versions may provide documented access
  553%   to these terms.  Currently it is adviced to ignore these arguments.
  554
  555
  556
  557:- record json_write_state(indent:nonneg = 0,
  558                       step:positive_integer = 2,
  559                       tab:positive_integer = 8,
  560                       width:nonneg = 72,
  561                       serialize_unknown:boolean = false
  562                      ).  563
  564json_write(Stream, Term) :-
  565    json_write(Stream, Term, []).
  566json_write(Stream, Term, Options) :-
  567    make_json_write_state(Options, State, Options1),
  568    make_json_options(Options1, OptionTerm, _RestOptions),
  569    json_write_term(Term, Stream, State, OptionTerm).
  570
  571json_write_term(Var, _, _, _) :-
  572    var(Var),
  573    !,
  574    instantiation_error(Var).
  575json_write_term(json(Pairs), Stream, State, Options) :-
  576    !,
  577    json_write_object(Pairs, Stream, State, Options).
  578json_write_term(Dict, Stream, State, Options) :-
  579    is_dict(Dict),
  580    !,
  581    dict_pairs(Dict, Tag, Pairs0),
  582    (   nonvar(Tag),
  583        json_options_tag(Options, Name),
  584        Name \== ''
  585    ->  Pairs = [Name-Tag|Pairs0]
  586    ;   Pairs = Pairs0
  587    ),
  588    json_write_object(Pairs, Stream, State, Options).
  589json_write_term(List, Stream, State, Options) :-
  590    is_list(List),
  591    !,
  592    space_if_not_at_left_margin(Stream, State),
  593    write(Stream, '['),
  594    (   json_write_state_width(State, Width),
  595        (   Width == 0
  596        ->  true
  597        ;   json_write_state_indent(State, Indent),
  598            json_print_length(List, Options, Width, Indent, _)
  599        )
  600    ->  set_width_of_json_write_state(0, State, State2),
  601        write_array_hor(List, Stream, State2, Options),
  602        write(Stream, ']')
  603    ;   step_indent(State, State2),
  604        write_array_ver(List, Stream, State2, Options),
  605        indent(Stream, State),
  606        write(Stream, ']')
  607    ).
  608
  609json_write_term(Term, Stream, State, Options) :-
  610    json_write_hook(Term, Stream, State, Options),
  611    !.
  612json_write_term(Number, Stream, _State, _Options) :-
  613    number(Number),
  614    !,
  615    (   float(Number)
  616    ->  write(Stream, Number)
  617    ;   integer(Number)
  618    ->  write(Stream, Number)
  619    ;   Float is float(Number)              % rational number
  620    ->  write(Stream, Float)
  621    ).
  622json_write_term(True, Stream, _State, Options) :-
  623    json_options_true(Options, True),
  624    !,
  625    write(Stream, true).
  626json_write_term(False, Stream, _State, Options) :-
  627    json_options_false(Options, False),
  628    !,
  629    write(Stream, false).
  630json_write_term(Null, Stream, _State, Options) :-
  631    json_options_null(Options, Null),
  632    !,
  633    write(Stream, null).
  634json_write_term(#(Text), Stream, _State, _Options) :-
  635    !,
  636    (   (   atom(Text)
  637        ;   string(Text)
  638        )
  639    ->  json_write_string(Stream, Text)
  640    ;   term_string(Text, String),
  641        json_write_string(Stream, String)
  642    ).
  643json_write_term(String, Stream, _State, _Options) :-
  644    atom(String),
  645    !,
  646    json_write_string(Stream, String).
  647json_write_term(String, Stream, _State, _Options) :-
  648    string(String),
  649    !,
  650    json_write_string(Stream, String).
  651json_write_term(AnyTerm, Stream, State, _Options) :-
  652    (   json_write_state_serialize_unknown(State, true)
  653    ->  term_string(AnyTerm, String),
  654        json_write_string(Stream, String)
  655    ;   type_error(json_term, AnyTerm)
  656    ).
  657
  658json_write_object(Pairs, Stream, State, Options) :-
  659    space_if_not_at_left_margin(Stream, State),
  660    write(Stream, '{'),
  661    (   json_write_state_width(State, Width),
  662        (   Width == 0
  663        ->  true
  664        ;   json_write_state_indent(State, Indent),
  665            json_print_length(json(Pairs), Options, Width, Indent, _)
  666        )
  667    ->  set_width_of_json_write_state(0, State, State2),
  668        write_pairs_hor(Pairs, Stream, State2, Options),
  669        write(Stream, '}')
  670    ;   step_indent(State, State2),
  671        write_pairs_ver(Pairs, Stream, State2, Options),
  672        indent(Stream, State),
  673        write(Stream, '}')
  674    ).
  675
  676
  677write_pairs_hor([], _, _, _).
  678write_pairs_hor([H|T], Stream, State, Options) :-
  679    json_pair(H, Name, Value),
  680    json_write_string(Stream, Name),
  681    write(Stream, ':'),
  682    json_write_term(Value, Stream, State, Options),
  683    (   T == []
  684    ->  true
  685    ;   write(Stream, ', '),
  686        write_pairs_hor(T, Stream, State, Options)
  687    ).
  688
  689write_pairs_ver([], _, _, _).
  690write_pairs_ver([H|T], Stream, State, Options) :-
  691    indent(Stream, State),
  692    json_pair(H, Name, Value),
  693    json_write_string(Stream, Name),
  694    write(Stream, ':'),
  695    json_write_term(Value, Stream, State, Options),
  696    (   T == []
  697    ->  true
  698    ;   write(Stream, ','),
  699        write_pairs_ver(T, Stream, State, Options)
  700    ).
  701
  702
  703json_pair(Var, _, _) :-
  704    var(Var),
  705    !,
  706    instantiation_error(Var).
  707json_pair(Name=Value, Name, Value) :- !.
  708json_pair(Name-Value, Name, Value) :- !.
  709json_pair(NameValue, Name, Value) :-
  710    compound(NameValue),
  711    NameValue =.. [Name, Value],
  712    !.
  713json_pair(Pair, _, _) :-
  714    type_error(json_pair, Pair).
  715
  716
  717write_array_hor([], _, _, _).
  718write_array_hor([H|T], Stream, State, Options) :-
  719    json_write_term(H, Stream, State, Options),
  720    (   T == []
  721    ->  write(Stream, ' ')
  722    ;   write(Stream, ', '),
  723        write_array_hor(T, Stream, State, Options)
  724    ).
  725
  726write_array_ver([], _, _, _).
  727write_array_ver([H|T], Stream, State, Options) :-
  728    indent(Stream, State),
  729    json_write_term(H, Stream, State, Options),
  730    (   T == []
  731    ->  true
  732    ;   write(Stream, ','),
  733        write_array_ver(T, Stream, State, Options)
  734    ).
  735
  736
  737indent(Stream, State) :-
  738    json_write_state_indent(State, Indent),
  739    json_write_state_tab(State, Tab),
  740    json_write_indent(Stream, Indent, Tab).
  741
  742step_indent(State0, State) :-
  743    json_write_state_indent(State0, Indent),
  744    json_write_state_step(State0, Step),
  745    NewIndent is Indent+Step,
  746    set_indent_of_json_write_state(NewIndent, State0, State).
  747
  748space_if_not_at_left_margin(Stream, State) :-
  749    stream_pair(Stream, _, Write),
  750    line_position(Write, LinePos),
  751    (   LinePos == 0
  752    ;   json_write_state_indent(State, LinePos)
  753    ),
  754    !.
  755space_if_not_at_left_margin(Stream, _) :-
  756    put_char(Stream, ' ').
  757
  758
  759%!  json_print_length(+Value, +Options, +Max, +Len0, +Len) is semidet.
  760%
  761%   True if Len-Len0 is the print-length of Value on a single line
  762%   and Len-Len0 =< Max.
  763%
  764%   @tbd    Escape sequences in strings are not considered.
  765
  766json_print_length(Var, _, _, _, _) :-
  767    var(Var),
  768    !,
  769    instantiation_error(Var).
  770json_print_length(json(Pairs), Options, Max, Len0, Len) :-
  771    !,
  772    Len1 is Len0 + 2,
  773    Len1 =< Max,
  774    must_be(list, Pairs),
  775    pairs_print_length(Pairs, Options, Max, Len1, Len).
  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).
  783json_print_length(Array, Options, Max, Len0, Len) :-
  784    is_list(Array),
  785    !,
  786    Len1 is Len0 + 2,
  787    Len1 =< Max,
  788    array_print_length(Array, Options, Max, Len1, Len).
  789json_print_length(Null, Options, Max, Len0, Len) :-
  790    json_options_null(Options, Null),
  791    !,
  792    Len is Len0 + 4,
  793    Len =< Max.
  794json_print_length(False, Options, Max, Len0, Len) :-
  795    json_options_false(Options, False),
  796    !,
  797    Len is Len0 + 5,
  798    Len =< Max.
  799json_print_length(True, Options, Max, Len0, Len) :-
  800    json_options_true(Options, True),
  801    !,
  802    Len is Len0 + 4,
  803    Len =< Max.
  804json_print_length(Number, _Options, Max, Len0, Len) :-
  805    number(Number),
  806    !,
  807    write_length(Number, AL, []),
  808    Len is Len0 + AL,
  809    Len =< Max.
  810json_print_length(@(Id), _Options, Max, Len0, Len) :-
  811    atom(Id),
  812    !,
  813    atom_length(Id, IdLen),
  814    Len is Len0+IdLen,
  815    Len =< Max.
  816json_print_length(String, _Options, Max, Len0, Len) :-
  817    string_len(String, Len0, Len),
  818    !,
  819    Len =< Max.
  820json_print_length(AnyTerm, _Options, Max, Len0, Len) :-
  821    write_length(AnyTerm, AL, []),          % will be serialized
  822    Len is Len0 + AL+2,
  823    Len =< Max.
  824
  825pairs_print_length([], _, _, Len, Len).
  826pairs_print_length([H|T], Options, Max, Len0, Len) :-
  827    pair_len(H, Options, Max, Len0, Len1),
  828    (   T == []
  829    ->  Len = Len1
  830    ;   Len2 is Len1 + 2,
  831        Len2 =< Max,
  832        pairs_print_length(T, Options, Max, Len2, Len)
  833    ).
  834
  835pair_len(Pair, Options, Max, Len0, Len) :-
  836    compound(Pair),
  837    pair_nv(Pair, Name, Value),
  838    !,
  839    string_len(Name, Len0, Len1),
  840    Len2 is Len1+2,
  841    Len2 =< Max,
  842    json_print_length(Value, Options, Max, Len2, Len).
  843pair_len(Pair, _Options, _Max, _Len0, _Len) :-
  844    type_error(pair, Pair).
  845
  846pair_nv(Name=Value, Name, Value) :- !.
  847pair_nv(Name-Value, Name, Value) :- !.
  848pair_nv(Term, Name, Value) :-
  849    compound_name_arguments(Term, Name, [Value]).
  850
  851array_print_length([], _, _, Len, Len).
  852array_print_length([H|T], Options, Max, Len0, Len) :-
  853    json_print_length(H, Options, Max, Len0, Len1),
  854    (   T == []
  855    ->  Len = Len1
  856    ;   Len2 is Len1+2,
  857        Len2 =< Max,
  858        array_print_length(T, Options, Max, Len2, Len)
  859    ).
  860
  861string_len(String, Len0, Len) :-
  862    atom(String),
  863    !,
  864    atom_length(String, AL),
  865    Len is Len0 + AL + 2.
  866string_len(String, Len0, Len) :-
  867    string(String),
  868    !,
  869    string_length(String, AL),
  870    Len is Len0 + AL + 2.
  871
  872
  873                 /*******************************
  874                 *             TEST             *
  875                 *******************************/
  876
  877%!  is_json_term(@Term) is semidet.
  878%!  is_json_term(@Term, +Options) is semidet.
  879%
  880%   True if Term is  a  json  term.   Options  are  the  same as for
  881%   json_read/2, defining the Prolog  representation   for  the JSON
  882%   =true=, =false= and =null= constants.
  883
  884is_json_term(Term) :-
  885    default_json_options(Options),
  886    is_json_term2(Options, Term).
  887
  888is_json_term(Term, Options) :-
  889    make_json_options(Options, OptionTerm, _RestOptions),
  890    is_json_term2(OptionTerm, Term).
  891
  892is_json_term2(_, Var) :-
  893    var(Var), !, fail.
  894is_json_term2(Options, json(Pairs)) :-
  895    !,
  896    is_list(Pairs),
  897    maplist(is_json_pair(Options), Pairs).
  898is_json_term2(Options, List) :-
  899    is_list(List),
  900    !,
  901    maplist(is_json_term2(Options), List).
  902is_json_term2(_, Primitive) :-
  903    atomic(Primitive),
  904    !.           % atom, string or number
  905is_json_term2(Options, True) :-
  906    json_options_true(Options, True).
  907is_json_term2(Options, False) :-
  908    json_options_false(Options, False).
  909is_json_term2(Options, Null) :-
  910    json_options_null(Options, Null).
  911
  912is_json_pair(_, Var) :-
  913    var(Var), !, fail.
  914is_json_pair(Options, Name=Value) :-
  915    atom(Name),
  916    is_json_term2(Options, Value).
  917
  918                 /*******************************
  919                 *         DICT SUPPORT         *
  920                 *******************************/
  921
  922%!  json_read_dict(+Stream, -Dict) is det.
  923%!  json_read_dict(+Stream, -Dict, +Options) is det.
  924%
  925%   Read  a  JSON  object,  returning  objects    as  a  dicts.  The
  926%   representation depends on the options, where the default is:
  927%
  928%     * String values are mapped to Prolog strings
  929%     * JSON =true=, =false= and =null= are represented using these
  930%       Prolog atoms.
  931%     * JSON objects are mapped to dicts.
  932%     * Optionally, a =type= field in an object assigns a tag for
  933%       the dict.
  934%
  935%   The predicate json_read_dict/3 processes  the   same  options as
  936%   json_read/3,  but  with  different  defaults.  In  addition,  it
  937%   processes the `tag` option. See   json_read/3  for details about
  938%   the shared options.
  939%
  940%     * tag(+Name)
  941%       When converting to/from a dict, map the indicated JSON
  942%       attribute to the dict _tag_. No mapping is performed if Name
  943%       is the empty atom ('', default). See json_read_dict/2 and
  944%       json_write_dict/2.
  945%     * default_tag(+Tag)
  946%       Provide the default tag if the above `tag` option does not
  947%       apply.
  948%     * null(+NullTerm)
  949%       Default the atom `null`.
  950%     * true(+TrueTerm)
  951%       Default the atom `true`.
  952%     * false(+FalseTerm)
  953%       Default the atom `false`
  954%     * end_of_file(+ErrorOrTerm)
  955%       Action on reading end-of-file. See json_read/3 for details.
  956%     * value_string_as(+Type)
  957%       Prolog type used for strings used as value.  Default
  958%       is `string`.  The alternative is `atom`, producing a
  959%       packed string object.
  960
  961json_read_dict(Stream, Dict) :-
  962    json_read_dict(Stream, Dict, []).
  963
  964json_read_dict(Stream, Dict, Options) :-
  965    make_json_dict_options(Options, OptionTerm, _RestOptions),
  966    (   json_value_top(Stream, Term, OptionTerm)
  967    ->  true
  968    ;   syntax_error(illegal_json, Stream)
  969    ),
  970    term_to_dict(Term, Dict, OptionTerm).
  971
  972term_to_dict(json(Pairs), Dict, Options) :-
  973    !,
  974    (   json_options_tag(Options, TagName),
  975        Tag \== '',
  976        select(TagName = Tag0, Pairs, NVPairs),
  977        to_atom(Tag0, Tag)
  978    ->  json_dict_pairs(NVPairs, DictPairs, Options)
  979    ;   json_options_default_tag(Options, DefTag),
  980        (   var(DefTag)
  981        ->  true
  982        ;   Tag = DefTag
  983        ),
  984        json_dict_pairs(Pairs, DictPairs, Options)
  985    ),
  986    dict_create(Dict, Tag, DictPairs).
  987term_to_dict(Value0, Value, _Options) :-
  988    atomic(Value0), Value0 \== [],
  989    !,
  990    Value = Value0.
  991term_to_dict(List0, List, Options) :-
  992    is_list(List0),
  993    !,
  994    terms_to_dicts(List0, List, Options).
  995term_to_dict(Special, Special, Options) :-
  996    (   json_options_true(Options, Special)
  997    ;   json_options_false(Options, Special)
  998    ;   json_options_null(Options, Special)
  999    ;   json_options_end_of_file(Options, Special)
 1000    ),
 1001    !.
 1002
 1003json_dict_pairs([], [], _).
 1004json_dict_pairs([Name=Value0|T0], [Name=Value|T], Options) :-
 1005    term_to_dict(Value0, Value, Options),
 1006    json_dict_pairs(T0, T, Options).
 1007
 1008terms_to_dicts([], [], _).
 1009terms_to_dicts([Value0|T0], [Value|T], Options) :-
 1010    term_to_dict(Value0, Value, Options),
 1011    terms_to_dicts(T0, T, Options).
 1012
 1013to_atom(Tag, Atom) :-
 1014    string(Tag),
 1015    !,
 1016    atom_string(Atom, Tag).
 1017to_atom(Atom, Atom) :-
 1018    atom(Atom).
 1019
 1020%!  json_write_dict(+Stream, +Dict) is det.
 1021%!  json_write_dict(+Stream, +Dict, +Options) is det.
 1022%
 1023%   Write a JSON term, represented using dicts.  This is the same as
 1024%   json_write/3, but assuming the default   representation  of JSON
 1025%   objects as dicts.
 1026
 1027json_write_dict(Stream, Dict) :-
 1028    json_write_dict(Stream, Dict, []).
 1029
 1030json_write_dict(Stream, Dict, Options) :-
 1031    make_json_write_state(Options, State, Options1),
 1032    make_json_dict_options(Options1, OptionTerm, _RestOptions),
 1033    json_write_term(Dict, Stream, State, OptionTerm).
 1034
 1035
 1036make_json_dict_options(Options, Record, RestOptions) :-
 1037    default_json_dict_options(Record0),
 1038    set_json_options_fields(Options, Record0, Record, RestOptions).
 1039
 1040%!  atom_json_dict(+Atom, -JSONDict, +Options) is det.
 1041%!  atom_json_dict(-Text, +JSONDict, +Options) is det.
 1042%
 1043%   Convert  between  textual  representation  and    a   JSON  term
 1044%   represented as a dict. Options are as for json_read/3.
 1045%   In _write_ mode, the addtional option
 1046%
 1047%       * as(Type)
 1048%       defines the output type, which is one of =atom=,
 1049%       =string= or =codes=.
 1050
 1051atom_json_dict(Atom, Term, Options) :-
 1052    ground(Atom),
 1053    !,
 1054    setup_call_cleanup(
 1055        open_string(Atom, In),
 1056        json_read_dict(In, Term, Options),
 1057        close(In)).
 1058atom_json_dict(Result, Term, Options) :-
 1059    select_option(as(Type), Options, Options1, atom),
 1060    (   type_term(Type, Result, Out)
 1061    ->  true
 1062    ;   must_be(oneof([atom,string,codes]), Type)
 1063    ),
 1064    with_output_to(Out,
 1065                   json_write_dict(current_output, Term, Options1)).
 1066
 1067
 1068                 /*******************************
 1069                 *           MESSAGES           *
 1070                 *******************************/
 1071
 1072:- multifile
 1073    prolog:error_message/3. 1074
 1075prolog:error_message(syntax_error(json(Id))) -->
 1076    [ 'JSON syntax error: ' ],
 1077    json_syntax_error(Id).
 1078
 1079json_syntax_error(illegal_comment) -->
 1080    [ 'Illegal comment' ].
 1081json_syntax_error(illegal_string_escape) -->
 1082    [ 'Illegal escape sequence in string' ]