View source with raw comments or as raw
   1/*  Part of SWI-Prolog
   2
   3    Author:        Jan Wielemaker
   4    E-mail:        J.Wielemaker@vu.nl
   5    WWW:           http://www.swi-prolog.org
   6    Copyright (c)  2007-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    clause(error:has_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    clause(error:has_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).