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-2014, University of Amsterdam
    7                              VU University Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module(json_convert,
   37          [ prolog_to_json/2,           % :Term, -JSON object
   38            json_to_prolog/2,           % +JSON, :Term
   39            (json_object)/1,            % +Definition
   40            op(1150, fx, (json_object))
   41          ]).   42:- use_module(library(error)).   43:- use_module(library(pairs)).   44:- use_module(library(apply)).   45:- use_module(json).   46
   47:- meta_predicate
   48    prolog_to_json(:, -),
   49    json_to_prolog(+, :).   50
   51:- public
   52    clear_cache/0,
   53    prolog_list_to_json/3,          % +ListIn, -ListOut, +Module
   54    prolog_to_json/3,               % +In, -Out, +Module
   55    prolog_bool_to_json/2.          % +In, -Boolean
   56
   57/** <module> Convert between JSON terms and Prolog application terms
   58
   59The idea behind this module is to  provide a flexible high-level mapping
   60between Prolog terms as you would like   to see them in your application
   61and the standard representation of a JSON   object as a Prolog term. For
   62example,  an  X-Y  point  may  be  represented  in  JSON  as  =|{"x":25,
   63"y":50}|=. Represented in Prolog  this   becomes  json([x=25,y=50]), but
   64this is a pretty non-natural  representation   from  the Prolog point of
   65view.
   66
   67This module allows for defining records (just like library(record)) that
   68provide   transparent   two-way   transformation     between   the   two
   69representations.
   70
   71  ==
   72  :- json_object
   73          point(x:integer, y:integer).
   74  ==
   75
   76This declaration causes prolog_to_json/2 to translate the native Prolog
   77representation into a JSON Term:
   78
   79  ==
   80  ?- prolog_to_json(point(25,50), X).
   81
   82  X = json([x=25, y=50])
   83  ==
   84
   85A json_object/1 declaration can define multiple   objects separated by a
   86comma (,), similar to the dynamic/1 directive. Optionally, a declaration
   87can  be  qualified   using   a    module.   The   conversion  predicates
   88prolog_to_json/2 and json_to_prolog/2 first try  a conversion associated
   89with the calling  module.  If  not   successful,  they  try  conversions
   90associated with the module =user=.
   91
   92JSON objects have no _type_. This can be solved by adding an extra field
   93to the JSON object, e.g. =|{"type":"point", "x":25, "y":50}|=. As Prolog
   94records are typed by their functor we need some notation to handle this
   95gracefully. This is achieved by adding +Fields to the declaration. I.e.
   96
   97  ==
   98  :- json_object
   99          point(x:integer, y:integer) + [type=point].
  100  ==
  101
  102Using this declaration, the conversion becomes:
  103
  104  ==
  105  ?- prolog_to_json(point(25,50), X).
  106
  107  X = json([x=25, y=50, type=point])
  108  ==
  109
  110The predicate json_to_prolog/2 is often  used after http_read_json/2 and
  111prolog_to_json/2 before reply_json/1. For now  we consider them seperate
  112predicates because the transformation may be   too  general, too slow or
  113not needed for dedicated  applications.  Using   a  seperate  step  also
  114simplifies debugging this rather complicated process.
  115
  116@tbd    Ignore extra fields.  Using a partial list of _extra_?
  117@tbd    Consider a sensible default for handling JSON =null=.  Conversion
  118        to Prolog could translate @null into a variable if the desired type
  119        is not =any=.  Conversion to JSON could map variables to =null=,
  120        though this may be unsafe.  If the Prolog term is known to be
  121        non-ground and JSON @null is a sensible mapping, we can also use
  122        this simple snipit to deal with that fact.
  123
  124        ==
  125                term_variables(Term, Vars),
  126                maplist(=(@null), Vars).
  127        ==
  128*/
  129
  130%!  current_json_object(Term, Module, Fields)
  131%
  132%   Multifile   predicate   computed   from     the    json_object/1
  133%   declarations. Term is the most  general Prolog term representing
  134%   the object. Module is the module in  which the object is defined
  135%   and Fields is a list of f(Name,  Type, Default, Var), ordered by
  136%   Name. Var is the corresponding variable in Term.
  137
  138:- multifile
  139    json_object_to_pairs/3,         % Term, Module, Pairs
  140    current_json_object/3.          % Term, Module, Fields
  141
  142%!  json_object(+Declaration)
  143%
  144%   Declare a JSON object.  The declaration takes the same format as
  145%   using in record/1 from library(record).  E.g.
  146%
  147%     ==
  148%     ?- json_object
  149%           point(x:int, y:int, z:int=0).
  150%     ==
  151%
  152%   The type arguments are either types as know to library(error) or
  153%   functor  names  of  other  JSON   objects.  The  constant  =any=
  154%   indicates an untyped argument.  If  this   is  a  JSON  term, it
  155%   becomes  subject  to  json_to_prolog/2.  I.e.,  using  the  type
  156%   list(any) causes the conversion to be   executed on each element
  157%   of the list.
  158%
  159%   If a field has a default, the default   is  used if the field is
  160%   not specified in the JSON  object.   Extending  the  record type
  161%   definition, types can be of  the   form  (Type1|Type2). The type
  162%   =null= means that the field may _not_ be present.
  163%
  164%   Conversion of JSON  to  Prolog   applies  if  all  non-defaulted
  165%   arguments can be found in  the   JSON  object. If multiple rules
  166%   match, the term with the highest arity gets preference.
  167
  168json_object(Declaration) :-
  169    throw(error(context_error(nodirective, json_object(Declaration)), _)).
  170
  171
  172%!  compile_json_objects(+Spec, -Clauses) is det.
  173%
  174%   Compiles a :- json_object directive into Clauses. Clauses are of
  175%   the form:
  176%
  177%   ==
  178%   json_object_to_pairs(Term, Module, Pairs) :-
  179%           <type-checks on Term>,
  180%           <make Pairs from Term>.
  181%   ==
  182
  183compile_json_objects(Spec, Clauses) :-
  184    phrase(compile_objects(Spec), Clauses).
  185
  186compile_objects(A) -->
  187    { var(A),
  188      !,
  189      instantiation_error(A)
  190    }.
  191compile_objects((A,B)) -->
  192    !,
  193    compile_objects(A),
  194    compile_objects(B).
  195compile_objects(Term) -->
  196    compile_object(Term).
  197
  198compile_object(ObjectDef) -->
  199    { prolog_load_context(module, CM),
  200      strip_module(CM:ObjectDef, M, Def),
  201      extra_defs(Def, Term, ExtraFields),
  202      Term =.. [Constructor|Args],
  203      defaults(Args, Defs, TypedArgs),
  204      types(TypedArgs, Names, Types)
  205    },
  206    record_to_json_clause(Constructor, M, Types, Names, ExtraFields),
  207    current_clause(Constructor, M, Types, Defs, Names, ExtraFields),
  208    [ (:- json_convert:clear_cache) ].
  209
  210extra_defs(Term+Extra0, Term, Extra) :-
  211    !,
  212    must_be(list, Extra0),
  213    maplist(canonical_pair, Extra0, Extra).
  214extra_defs(Term,       Term, []).
  215
  216
  217canonical_pair(Var, _) :-
  218    var(Var),
  219    !,
  220    instantiation_error(Var).
  221canonical_pair(Name=Value, Name=Value) :-
  222    !,
  223    must_be(atom, Name).
  224canonical_pair(Name-Value, Name=Value) :-
  225    !,
  226    must_be(atom, Name).
  227canonical_pair(NameValue, Name=Value) :-
  228    NameValue =.. [Name,Value],
  229    !.
  230canonical_pair(Pair, _) :-
  231    type_error(pair, Pair).
  232
  233
  234%!  record_to_json_clause(+Constructor, +Module, +Type, +Names)
  235%
  236%   Create a clause translating the record   definition into a pairs
  237%   representation.
  238
  239record_to_json_clause(Constructor, Module, Types, Names, Extra) -->
  240    { type_checks(Types, VarsHead, VarsBody, Body0, Module),
  241      clean_body(Body0, Body),
  242      Term =.. [Constructor|VarsHead],
  243      make_pairs(Names, VarsBody, Pairs, Extra),
  244      Head =.. [json_object_to_pairs,Term,Module,json(Pairs)]
  245    },
  246    [ (json_convert:(Head :- Body)) ].
  247
  248
  249%!  type_checks(+Types, -VarsIn, -VarsOut, -Goal, +Module) is det.
  250%
  251%   Goal is a body-term  that  validates   Vars  satisfy  Types.  In
  252%   addition to the types accepted by   must_be/2,  it accepts =any=
  253%   and Name/Arity. The latter demands  a   json_object  term of the
  254%   given Name and Arity.
  255%
  256%   @tbd    Compile list(Type) specification.  Currently Type is
  257%           handled like =any=
  258
  259type_checks([], [], [], true, _).
  260type_checks([Type|T], [IV|IVars], [OV|OVars], (Goal, Body), M) :-
  261    !,
  262    type_check(Type, IV, OV, M, Goal),
  263    type_checks(T, IVars, OVars, Body, M).
  264
  265type_check(any, IV, OV, M, prolog_to_json(IV, OV, M)) :- !.
  266type_check(Name/Arity, IV, OV, M, prolog_to_json(IV, OV, M)) :-
  267    !,
  268    functor(IV, Name, Arity).
  269type_check(boolean, IV, OV, _, prolog_bool_to_json(IV, OV)) :- !.
  270type_check(list, IV, OV, M, prolog_list_to_json(IV, OV, M)) :- !.
  271type_check(list(any), IV, OV, M, prolog_list_to_json(IV, OV, M)) :- !.
  272type_check(list(_Type), IV, OV, M, prolog_list_to_json(IV, OV, M)) :- !.
  273type_check(Type, V, V, _, Goal) :-
  274    type_goal(Type, V, Goal).
  275
  276
  277%!  prolog_bool_to_json(+Prolog, -JSON) is semidet.
  278%
  279%   JSON is the JSON boolean for Prolog. It is a flexible the Prolog
  280%   notation for thruth-value, accepting one of  =true=, =on= or =1=
  281%   for @true and one of =false=, =fail=, =off= or =0= for @false.
  282%
  283%   @error  instantiation_error if Prolog is unbound.
  284
  285prolog_bool_to_json(Var, _) :-
  286    var(Var),
  287    instantiation_error(Var).
  288prolog_bool_to_json(true, @(true)).
  289prolog_bool_to_json(false, @(false)).
  290prolog_bool_to_json(fail, @(false)).
  291prolog_bool_to_json(0, @(false)).
  292prolog_bool_to_json(on, @(true)).
  293prolog_bool_to_json(off, @(false)).
  294prolog_bool_to_json(1, @(false)).
  295prolog_bool_to_json(@(True), True) :-
  296    prolog_bool_to_json(True, True).
  297
  298
  299%!  type_goal(+Type, +Var, -BodyTerm) is det.
  300%
  301%   Inline type checking calls.
  302
  303type_goal(Type, Var, Body) :-
  304    current_type(Type, Var, Body0),
  305    primitive(Body0, Body),
  306    !.
  307type_goal(Type, Var, is_of_type(Type, Var)).
  308
  309primitive((A0,B0), (A,B)) :-
  310    !,
  311    primitive(A0, A),
  312    primitive(B0, B).
  313primitive((A0;B0), (A,B)) :-
  314    !,
  315    primitive(A0, A),
  316    primitive(B0, B).
  317primitive((A0->B0), (A,B)) :-
  318    !,
  319    primitive(A0, A),
  320    primitive(B0, B).
  321primitive(_:G, G) :-
  322    !,
  323    predicate_property(system:G, built_in).
  324primitive(G, G) :-
  325    predicate_property(system:G, built_in).
  326
  327non_json_type(Type) :-
  328    current_type(Type, _, _),
  329    !.
  330
  331
  332%!  clean_body(+BodyIn, -BodyOut) is det.
  333%
  334%   Cleanup a body goal. Eliminate   redundant =true= statements and
  335%   perform partial evaluation on some  commonly constructs that are
  336%   generated from the has_type/2 clauses in library(error).
  337
  338clean_body(Var, Var) :-
  339    var(Var),
  340    !.
  341clean_body((A0,B0), G) :-
  342    !,
  343    clean_body(A0, A),
  344    clean_body(B0, B),
  345    conj(A, B, G).
  346clean_body(ground(X), true) :-          % Generated from checking extra fields.
  347    ground(X),
  348    !.
  349clean_body(memberchk(V, Values), true) :- % generated from oneof(List)
  350    ground(V), ground(Values),
  351    memberchk(V, Values),
  352    !.
  353clean_body((integer(Low) -> If ; Then), Goal) :- % generated from between(Low,High)
  354    number(Low),
  355    !,
  356    (   integer(Low)
  357    ->  Goal = If
  358    ;   Goal = Then
  359    ).
  360clean_body((A->true;fail), A) :- !.     % nullable fields.
  361clean_body((fail->_;A), A) :- !.
  362clean_body(A, A).
  363
  364conj(T, A, A) :- T == true, !.
  365conj(A, T, A) :- T == true, !.
  366conj(A, B, (A,B)).
  367
  368make_pairs([], [], L, L).
  369make_pairs([N|TN], [V|TV], [N=V|T], Tail) :-
  370    make_pairs(TN, TV, T, Tail).
  371
  372%!  current_clause(+Constructor, +Module, +Types, +Defs, +Names, +Extra)
  373%
  374%   Create the clause current_json_object/3.
  375
  376current_clause(Constructor, Module, Types, Defs, Names, Extra) -->
  377    { length(Types, Arity),
  378      functor(Term, Constructor, Arity),
  379      extra_fields(Extra, EF),
  380      Term =.. [_|Vars],
  381      mk_fields(Names, Types, Defs, Vars, Fields0, EF),
  382      sort(Fields0, Fields),
  383      Head =.. [current_json_object, Term, Module, Fields]
  384    },
  385    [ json_convert:Head ].
  386
  387extra_fields([], []).
  388extra_fields([Name=Value|T0], [f(Name, oneof([Value]), _, Value)|T]) :-
  389    extra_fields(T0, T).
  390
  391mk_fields([], [], [], [], Fields, Fields).
  392mk_fields([Name|TN], [Type|TT], [Def|DT], [Var|VT],
  393          [f(Name, Type, Def, Var)|T], Tail) :-
  394    mk_fields(TN, TT, DT, VT, T, Tail).
  395
  396
  397/* The code below is copied from library(record) */
  398
  399%!  defaults(+ArgsSpecs, -Defaults, -Args)
  400%
  401%   Strip the default specification from the argument specification.
  402
  403defaults([], [], []).
  404defaults([Arg=Default|T0], [Default|TD], [Arg|TA]) :-
  405    !,
  406    defaults(T0, TD, TA).
  407defaults([Arg|T0], [NoDefault|TD], [Arg|TA]) :-
  408    no_default(NoDefault),
  409    defaults(T0, TD, TA).
  410
  411no_default('$no-default$').
  412
  413%!  types(+ArgsSpecs, -Defaults, -Args)
  414%
  415%   Strip the default specification from the argument specification.
  416
  417types([], [], []).
  418types([Name:Type|T0], [Name|TN], [Type|TT]) :-
  419    !,
  420    must_be(atom, Name),
  421    types(T0, TN, TT).
  422types([Name|T0], [Name|TN], [any|TT]) :-
  423    must_be(atom, Name),
  424    types(T0, TN, TT).
  425
  426
  427                 /*******************************
  428                 *       PROLOG --> JSON        *
  429                 *******************************/
  430
  431%!  prolog_to_json(:Term, -JSONObject) is det.
  432%
  433%   Translate a Prolog application Term  into   a  JSON object term.
  434%   This transformation is based on   :- json_object/1 declarations.
  435%   If  a  json_object/1  declaration  declares   a  field  of  type
  436%   =boolean=, commonly used thruth-values in   Prolog are converted
  437%   to JSON booleans. Boolean  translation   accepts  one of =true=,
  438%   =on=, =1=, @true, =false=, =fail=, =off= or =0=, @false.
  439%
  440%   @error  type_error(json_term, X)
  441%   @error  instantiation_error
  442
  443prolog_to_json(Module:Term, JSON) :-
  444    prolog_to_json(Term, JSON, Module).
  445
  446prolog_to_json(Var, _, _) :-
  447    var(Var),
  448    !,
  449    instantiation_error(Var).
  450prolog_to_json(Atomic, Atomic, _) :-
  451    atomic(Atomic),
  452    !.
  453prolog_to_json(List, JSON, Module) :-
  454    is_list(List),
  455    !,
  456    prolog_list_to_json(List, JSON, Module).
  457prolog_to_json(Record, JSON, Module) :-
  458    record_to_pairs(Record, JSON, Module),
  459    !.
  460prolog_to_json(Term, Term, _) :-
  461    is_json_term(Term),
  462    !.
  463prolog_to_json(Term, _, _) :-
  464    type_error(json_term, Term).
  465
  466record_to_pairs(T, _, _) :-
  467    var(T),
  468    !,
  469    instantiation_error(T).
  470record_to_pairs(T, JSON, M) :-
  471    object_module(M, Module),
  472    json_object_to_pairs(T, Module, JSON),
  473    !.
  474
  475object_module(user, user) :- !.
  476object_module(M, M).
  477object_module(_, user).
  478
  479prolog_list_to_json([], [], _).
  480prolog_list_to_json([H0|T0], [H|T], M) :-
  481    prolog_to_json(H0, H, M),
  482    prolog_list_to_json(T0, T, M).
  483
  484
  485                 /*******************************
  486                 *       JSON --> PROLOG        *
  487                 *******************************/
  488
  489:- dynamic
  490    json_to_prolog_rule/3,          % Module, Pairs, Term
  491    created_rules_for_pairs/2.      % Module, Pairs
  492
  493clear_cache :-
  494    retractall(json_to_prolog_rule(_,_,_)),
  495    retractall(created_rules_for_pairs(_,_)).
  496
  497:- clear_cache.  498
  499%!  json_to_prolog(+JSON, -Term) is det.
  500%
  501%   Translate  a  JSON  term   into    an   application  term.  This
  502%   transformation is based on  :-   json_object/1  declarations. An
  503%   efficient transformation is non-trivial,  but   we  rely  on the
  504%   assumption that, although the order of   fields in JSON terms is
  505%   irrelevant and can therefore vary  a lot, practical applications
  506%   will normally generate the JSON objects in a consistent order.
  507%
  508%   If a field in a json_object is declared of type =boolean=, @true
  509%   and @false are  translated  to  =true=   or  =false=,  the  most
  510%   commonly used Prolog representation for truth-values.
  511
  512json_to_prolog(JSON, Module:Term) :-
  513    json_to_prolog(JSON, Term, Module).
  514
  515json_to_prolog(json(Pairs), Term, Module) :-
  516    !,
  517    (   pairs_to_term(Pairs, Term, Module)
  518    ->  true
  519    ;   json_pairs_to_prolog(Pairs, Prolog, Module),
  520        Term = json(Prolog)
  521    ).
  522json_to_prolog(List, Prolog, Module) :-
  523    is_list(List),
  524    !,
  525    json_list_to_prolog(List, Prolog, Module).
  526json_to_prolog(@(Special), @(Special), _) :- !.
  527json_to_prolog(Atomic, Atomic, _).
  528
  529json_pairs_to_prolog([], [], _).
  530json_pairs_to_prolog([Name=JSONValue|T0], [Name=PrologValue|T], Module) :-
  531    json_to_prolog(JSONValue, PrologValue, Module),
  532    json_pairs_to_prolog(T0, T, Module).
  533
  534json_list_to_prolog([], [], _).
  535json_list_to_prolog([JSONValue|T0], [PrologValue|T], Module) :-
  536    json_to_prolog(JSONValue, PrologValue, Module),
  537    json_list_to_prolog(T0, T, Module).
  538
  539
  540%!  pairs_to_term(+Pairs, ?Term, +Module) is semidet.
  541%
  542%   Convert a Name=Value set into a Prolog application term based on
  543%   json_object/1 declarations. If multiple rules   can  be created,
  544%   make the one with the highest arity the preferred one.
  545%
  546%   @tbd    Ignore extra pairs if term is partially given?
  547
  548pairs_to_term(Pairs, Term, Module) :-
  549    object_module(Module, M),
  550    (   json_to_prolog_rule(M, Pairs, Term)
  551    ->  !
  552    ;   created_rules_for_pairs(M, Pairs)
  553    ->  !, fail
  554    ;   pairs_args(Pairs, PairArgs),
  555        sort(PairArgs, SortedPairArgs),
  556        findall(Q-Rule,
  557                ( create_rule(SortedPairArgs, Module, M, Term0, Body, Q),
  558                  Rule = (json_to_prolog_rule(M, PairArgs, Term0) :- Body)
  559                ),
  560                RulePairs),
  561        keysort(RulePairs, ByQuality),
  562        pairs_values(ByQuality, Rules),
  563        maplist(asserta, Rules),
  564        asserta(created_rules_for_pairs(M, PairArgs)),
  565        json_to_prolog_rule(M, Pairs, Term), !
  566    ).
  567
  568pairs_args([], []).
  569pairs_args([Name=_Value|T0], [Name=_|T]) :-
  570    pairs_args(T0, T).
  571
  572%!  create_rule(+PairArgs, +Module, -ObjectM, -Term, -Body,
  573%!              -Quality) is det.
  574%
  575%   Create a new rule for dealing with Pairs, a Name=Value list of a
  576%   particular order.  Here is an example rule:
  577%
  578%     ==
  579%     json_to_prolog_rule([x=X, y=Y], point(X,Y)) :-
  580%           integer(X),
  581%           integer(Y).
  582%     ==
  583%
  584%   @arg PairArgs is an ordered list of Name=Variable pairs
  585%   @arg Module is the module requesting the conversion
  586%   @arg ObjectM is the module where the object is defined
  587%   @arg Term is the converted term (with variable arguments)
  588%   @arg Body is a Prolog goal that validates the types and
  589%        converts arguments.
  590%   @arg Quality is a number that indicates the matching quality.
  591%        Larger values are better.  Max is 0.  There is a penalty
  592%        of 1 for applying a default value and a penalty of 2 for
  593%        ignoring a value in the JSON term.
  594
  595create_rule(PairArgs, Module, M, Term, Body, Quality) :-
  596    current_json_object(Term, M, Fields),
  597    match_fields(PairArgs, Fields, Body0, Module, 0, Quality),
  598    clean_body(Body0, Body).
  599
  600match_fields(Ignored, [], true, _, Q0, Q) :-
  601    !,
  602    length(Ignored, Len),
  603    Q is Q0-2*Len.
  604match_fields([Name=JSON|TP], [f(Name, Type, _, Prolog)|TF], (Goal,Body),
  605             M, Q0, Q) :-
  606    !,
  607    match_field(Type, JSON, Prolog, M, Goal),
  608    match_fields(TP, TF, Body, M, Q0, Q).
  609match_fields([Name=JSON|TP], [f(OptName, Type, Def, Prolog)|TF], Body,
  610             M, Q0, Q) :-
  611    OptName @< Name,
  612    !,
  613    (   nullable(Type)
  614    ->  true
  615    ;   no_default(NoDef),
  616        Def \== NoDef
  617    ->  Prolog = Def
  618    ),
  619    Q1 is Q0-1,
  620    match_fields([Name=JSON|TP], TF, Body, M, Q1, Q).
  621match_fields([Name=_|TP], [F|TF], Body, M, Q0, Q) :-
  622    arg(1, F, Next),
  623    Name @< Next,
  624    Q1 is Q0-2,
  625    match_fields(TP, [F|TF], Body, M, Q1, Q).
  626
  627nullable(null).
  628nullable((A|B)) :- ( nullable(A) -> true ; nullable(B) ).
  629
  630match_field((A|B), JSON, Prolog, M, (BodyA->true;BodyB)) :-
  631    !,
  632    match_field(A, JSON, Prolog, M, BodyA),
  633    match_field(B, JSON, Prolog, M, BodyB).
  634match_field(null, _, _, _, fail) :- !.
  635match_field(any, JSON, Prolog, M, json_to_prolog(JSON,Prolog,M)) :- !.
  636match_field(F/A, JSON, Prolog, M, json_to_prolog(JSON,Prolog,M)) :-
  637    !,
  638    functor(Prolog, F, A).
  639match_field(boolean, JSON, Prolog, _, json_bool_to_prolog(JSON, Prolog)) :- !.
  640match_field(list(Type), JSON, Prolog, M, json_list_to_prolog(JSON, Prolog, M)) :-
  641    !,
  642    (   Type = _Funcor/_Arity
  643    ->  true
  644    ;   non_json_type(Type)
  645    ->  true
  646    ;   current_json_object(Term, M, _Fields),
  647        functor(Term, Type, _)
  648    ).
  649match_field(list, JSON, Prolog, M, Goal) :-
  650    !,
  651    match_field(list(any), JSON, Prolog, M, Goal).
  652match_field(Type, Var, Var, _, Goal) :-
  653    type_goal(Type, Var, Goal).
  654
  655:- public json_bool_to_prolog/2.  656
  657json_bool_to_prolog(@(True), True).
  658
  659
  660                 /*******************************
  661                 *            EXPANSION         *
  662                 *******************************/
  663
  664:- multifile
  665    system:term_expansion/2.  666:- dynamic
  667    system:term_expansion/2.  668
  669system:term_expansion((:- json_object(Spec)), Clauses) :-
  670    compile_json_objects(Spec, Clauses)