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-2025, University of Amsterdam 7 VU University Amsterdam 8 CWI, Amsterdam 9 SWI-Prolog Solutions b.v. 10 All rights reserved. 11 12 Redistribution and use in source and binary forms, with or without 13 modification, are permitted provided that the following conditions 14 are met: 15 16 1. Redistributions of source code must retain the above copyright 17 notice, this list of conditions and the following disclaimer. 18 19 2. Redistributions in binary form must reproduce the above copyright 20 notice, this list of conditions and the following disclaimer in 21 the documentation and/or other materials provided with the 22 distribution. 23 24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 POSSIBILITY OF SUCH DAMAGE. 36*/ 37 38:- module(json, 39 [ json_read/2, % +Stream, -JSONTerm 40 json_read/3, % +Stream, -JSONTerm, +Options 41 atom_json_term/3, % ?Atom, ?JSONTerm, +Options 42 json_write/2, % +Stream, +Term 43 json_write/3, % +Stream, +Term, +Options 44 is_json_term/1, % @Term 45 is_json_term/2, % @Term, +Options 46 % Version 7 dict support 47 json_read_dict/2, % +Stream, -Dict 48 json_read_dict/3, % +Stream, -Dict, +Options 49 json_write_dict/2, % +Stream, +Dict 50 json_write_dict/3, % +Stream, +Dict, +Options 51 atom_json_dict/3, % ?Atom, ?JSONDict, +Options 52 json/4 % {|json(Var,...)||JSON|} 53 ]). 54:- use_module(library(record)). 55:- use_module(library(error)). 56:- use_module(library(option)). 57:- use_module(library(lists)). 58:- use_module(library(apply)). 59:- use_module(library(quasi_quotations)). 60 61:- use_foreign_library(foreign(json)). 62 63:- multifile 64 json_write_hook/4, % +Term, +Stream, +State, +Options 65 json_dict_pairs/2. % +Dict, -Pairs 66 67:- predicate_options(json_read/3, 3, 68 [ null(ground), 69 true(ground), 70 false(ground), 71 end_of_file(ground), 72 value_string_as(oneof([atom,string])), 73 qqdict(list(compound(=(atom,var)))) 74 ]). 75:- predicate_options(json_write/3, 3, 76 [ indent(nonneg), 77 step(positive_integer), 78 tab(positive_integer), 79 width(nonneg), 80 null(ground), 81 true(ground), 82 false(ground), 83 serialize_unknown(boolean) 84 ]). 85:- predicate_options(json_read_dict/3, 3, 86 [ tag(atom), 87 default_tag(atom), 88 pass_to(json_read/3, 3) 89 ]). 90:- predicate_options(json_write_dict/3, 3, 91 [ tag(atom), 92 pass_to(json_write/3, 3) 93 ]). 94:- predicate_options(is_json_term/2, 2, 95 [ null(ground), 96 true(ground), 97 false(ground) 98 ]). 99:- predicate_options(atom_json_term/3, 3, 100 [ as(oneof([atom,string,codes])), 101 pass_to(json_read/3, 3), 102 pass_to(json_write/3, 3) 103 ]).
129:- record json_options( 130 null:ground = @(null), 131 true:ground = @(true), 132 false:ground = @(false), 133 end_of_file:ground = error, 134 value_string_as:oneof([atom,string]) = atom, 135 tag:atom = '', 136 default_tag:(var;atom) = '#', 137 qqdict:list(compound(atom=var))). 138 139default_json_dict_options( 140 json_options(null, true, false, error, string, '', #, _)). 141 142 143 /******************************* 144 * MAP TO/FROM TEXT * 145 *******************************/
atom (default),
string, codes or chars.156atom_json_term(Atom, Term, Options) :- 157 ground(Atom), 158 !, 159 setup_call_cleanup( 160 open_string(Atom, In), 161 json_read(In, Term, Options), 162 close(In)). 163atom_json_term(Result, Term, Options) :- 164 select_option(as(Type), Options, Options1, atom), 165 ( type_term(Type, Result, Out) 166 -> true 167 ; must_be(oneof([atom,string,codes,chars]), Type) 168 ), 169 with_output_to(Out, 170 json_write(current_output, Term, Options1)). 171 172type_term(atom, Result, atom(Result)). 173type_term(string, Result, string(Result)). 174type_term(codes, Result, codes(Result)). 175type_term(chars, Result, chars(Result)). 176 177 178 /******************************* 179 * READING * 180 *******************************/
json(NameValueList), where
NameValueList is a list of Name=Value. Name is an atom
created from the JSON string.true and false are mapped -like JPL-
to @(true) and @(false).null is mapped to the Prolog term
@(null)Here is a complete example in JSON and its corresponding Prolog term.
{ "name":"Demo term",
"created": {
"day":null,
"month":"December",
"year":2007
},
"confirmed":true,
"members":[1,2,3]
}
json([ name='Demo term',
created=json([day= @null, month='December', year=2007]),
confirmed= @true,
members=[1, 2, 3]
])
The following options are processed:
null. Default @(null)true. Default @(true)false. Default @(false)error):
error, throw an unexpected
end of file syntax error
Returning an status term is required to process
Concatenated
JSON.
Suggested values are @(eof) or end_of_file.
atom.
The alternative is string, producing a packed string object.
Please note that codes or chars would produce ambiguous
output and are therefore not supported.255json_read(Stream, Term) :- 256 default_json_options(Options), 257 ( json_value_top(Stream, Term, Options) 258 -> true 259 ; syntax_error(illegal_json, Stream) 260 ). 261json_read(Stream, Term, Options) :- 262 make_json_options(Options, OptionTerm, _RestOptions), 263 ( json_value_top(Stream, Term, OptionTerm) 264 -> true 265 ; syntax_error(illegal_json, Stream) 266 ). 267 268json_value_top(Stream, Term, Options) :- 269 stream_property(Stream, type(binary)), 270 !, 271 setup_call_cleanup( 272 set_stream(Stream, encoding(utf8)), 273 json_value_top_(Stream, Term, Options), 274 set_stream(Stream, type(binary))). 275json_value_top(Stream, Term, Options) :- 276 json_value_top_(Stream, Term, Options). 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 !, 326 json_constant(ID, Constant, Options). 327json_term_top(C, Stream, Var, Options) :- 328 code_type(C, prolog_var_start), 329 json_options_qqdict(Options, QQDict), 330 nonvar(QQDict), 331 !, 332 json_read_var_cont(Stream, Codes), 333 atom_codes(Name, [C | Codes]), 334 ( memberchk(Name=Var, QQDict) 335 -> true 336 ; syntax_error(non_existing_var(Name, QQDict), Stream) 337 ). 338 339json_read_var_cont(Stream, [C | L]) :- 340 peek_code(Stream, C), 341 code_type(C, prolog_identifier_continue), 342 !, 343 get_code(Stream, C), 344 json_read_var_cont(Stream, L). 345json_read_var_cont(_, []). 346 347 348json_pairs(0'}, _, [], _) :- !. 349json_pairs(C0, Stream, [Pair|Tail], Options) :- 350 json_pair(C0, Stream, Pair, C, Options), 351 ws(C, Stream, Next), 352 ( Next == 0', 353 -> ws(Stream, C2), 354 json_pairs(C2, Stream, Tail, Options) 355 ; Next == 0'} 356 -> Tail = [] 357 ; syntax_error(illegal_object, Stream) 358 ). 359 360json_pair(C0, Stream, Name=Value, Next, Options) :- 361 json_string_as_atom(C0, Stream, Name), 362 ws(Stream, C), 363 C == 0':, 364 json_value(Stream, Value, Next, Options). 365 366 367json_array(0'], _, [], _) :- !. 368json_array(C0, Stream, [Value|Tail], Options) :- 369 json_term(C0, Stream, Value, C, Options), 370 ws(C, Stream, Next), 371 ( Next == 0', 372 -> ws(Stream, C1), 373 json_array(C1, Stream, Tail, Options) 374 ; Next == 0'] 375 -> Tail = [] 376 ; syntax_error(illegal_array, Stream) 377 ). 378 379codes_to_type(atom, Codes, Atom) :- 380 atom_codes(Atom, Codes). 381codes_to_type(string, Codes, Atom) :- 382 string_codes(Atom, Codes). 383codes_to_type(codes, Codes, Codes). 384 385json_string_as_atom(0'", Stream, Atom) :- 386 get_code(Stream, C1), 387 json_string_codes(C1, Stream, Codes), 388 atom_codes(Atom, Codes). 389 390json_string_codes(0'", _, []) :- !. 391json_string_codes(0'\\, Stream, [H|T]) :- 392 !, 393 get_code(Stream, C0), 394 ( escape(C0, Stream, H) 395 -> true 396 ; syntax_error(illegal_string_escape, Stream) 397 ), 398 get_code(Stream, C1), 399 json_string_codes(C1, Stream, T). 400json_string_codes(-1, Stream, _) :- 401 !, 402 syntax_error(eof_in_string, Stream). 403json_string_codes(C, Stream, [C|T]) :- 404 get_code(Stream, C1), 405 json_string_codes(C1, Stream, T). 406 407escape(0'", _, 0'") :- !. 408escape(0'\\, _, 0'\\) :- !. 409escape(0'/, _, 0'/) :- !. 410escape(0'b, _, 0'\b) :- !. 411escape(0'f, _, 0'\f) :- !. 412escape(0'n, _, 0'\n) :- !. 413escape(0'r, _, 0'\r) :- !. 414escape(0't, _, 0'\t) :- !. 415escape(0'u, Stream, C) :- 416 get_XXXX(Stream, H), 417 ( hi_surrogate(H) 418 -> get_surrogate_tail(Stream, H, C) 419 ; C = H 420 ). 421 422get_XXXX(Stream, C) :- 423 get_xdigit(Stream, D1), 424 get_xdigit(Stream, D2), 425 get_xdigit(Stream, D3), 426 get_xdigit(Stream, D4), 427 C is D1<<12+D2<<8+D3<<4+D4. 428 429get_xdigit(Stream, D) :- 430 get_code(Stream, C), 431 code_type(C, xdigit(D)), 432 !. 433get_xdigit(Stream, _) :- 434 syntax_error(hexdigit_expected, Stream). 435 436get_surrogate_tail(Stream, Hi, Codepoint) :- 437 ( get_code(Stream, 0'\\), 438 get_code(Stream, 0'u), 439 get_XXXX(Stream, Lo), 440 surrogate([Hi, Lo], Codepoint) 441 -> true 442 ; syntax_error(illegal_surrogate_pair, Stream) 443 ). 444 445 446hi_surrogate(C) :- 447 C >= 0xD800, C < 0xDC00. 448 449lo_surrogate(C) :- 450 C >= 0xDC00, C < 0xE000. 451 452surrogate([Hi, Lo], Codepoint) :- 453 hi_surrogate(Hi), 454 lo_surrogate(Lo), 455 Codepoint is (Hi - 0xD800) * 0x400 + (Lo - 0xDC00) + 0x10000. 456 457json_read_constant(0't, Stream, true) :- 458 !, 459 must_see(`rue`, Stream, true). 460json_read_constant(0'f, Stream, false) :- 461 !, 462 must_see(`alse`, Stream, false). 463json_read_constant(0'n, Stream, null) :- 464 !, 465 must_see(`ull`, Stream, null). 466 467must_see([], _Stream, _). 468must_see([H|T], Stream, Name) :- 469 get_code(Stream, C), 470 ( C == H 471 -> true 472 ; syntax_error(json_expected(Name), Stream) 473 ), 474 must_see(T, Stream, Name). 475 476json_constant(true, Constant, Options) :- 477 !, 478 json_options_true(Options, Constant). 479json_constant(false, Constant, Options) :- 480 !, 481 json_options_false(Options, Constant). 482json_constant(null, Constant, Options) :- 483 !, 484 json_options_null(Options, Constant).
// ... comments.492ws(Stream, Next) :- 493 get_code(Stream, C0), 494 json_skip_ws(Stream, C0, Next). 495 496ws(C0, Stream, Next) :- 497 json_skip_ws(Stream, C0, Next). 498 499syntax_error(Message, Stream) :- 500 stream_error_context(Stream, Context), 501 throw(error(syntax_error(json(Message)), Context)). 502 503stream_error_context(Stream, stream(Stream, Line, LinePos, CharNo)) :- 504 stream_pair(Stream, Read, _), 505 character_count(Read, CharNo), 506 line_position(Read, LinePos), 507 line_count(Read, Line). 508 509 510 /******************************* 511 * JSON OUTPUT * 512 *******************************/
519% foreign json_write_string/2.
line_position(Stream, Pos) is not 0. Then it writes Indent //
TabDistance tab characters and Indent mode TabDistance spaces.
527% foreign json_write_indent/3.
Values can be of the form #(Term), which causes Term to be stringified if it is not an atom or string. Stringification is based on term_string/2.
Rational numbers are emitted as floating point numbers. The hook json_write_hook/4 can be used to realize domain specific alternatives.
The version 7 dict type is supported as well. Optionally, if the
dict has a tag, a property "type":"tag" can be added to the
object. This behaviour can be controlled using the tag option (see
below). For example:
?- json_write(current_output, point{x:1,y:2}).
{
"x":1,
"y":2
}
?- json_write(current_output, point{x:1,y:2}, [tag(type)]).
{
"type":"point",
"x":1,
"y":2
}
In addition to the options recognised by json_read/3, we process the following options are recognised:
true (default false), serialize unknown terms and
print them as a JSON string. The default raises a type
error. Note that this option only makes sense if you can
guarantee that the passed value is not an otherwise valid
Prolog representation of a Prolog term.
If a string is emitted, the sequence </ is emitted as
<\/. This is valid JSON syntax which ensures that JSON
objects can be safely embedded into an HTML <script>
element.
Note that this hook is shared by all users of this library. It is generally advised to map a unique compound term to avoid interference with normal output.
618:- record json_write_state(indent:nonneg = 0, 619 step:positive_integer = 2, 620 tab:positive_integer = 8, 621 width:nonneg = 72, 622 serialize_unknown:boolean = false 623 ). 624 625json_write(Stream, Term) :- 626 json_write(Stream, Term, []). 627json_write(Stream, Term, Options) :- 628 make_json_write_state(Options, State, Options1), 629 make_json_options(Options1, OptionTerm, _RestOptions), 630 json_write_term(Term, Stream, State, OptionTerm). 631 632json_write_term(Var, _, _, _) :- 633 var(Var), 634 !, 635 instantiation_error(Var). 636json_write_term(json(Pairs), Stream, State, Options) :- 637 !, 638 json_write_object(Pairs, Stream, State, Options). 639json_write_term(Dict, Stream, State, Options) :- 640 is_dict(Dict, Tag), 641 !, 642 json_pairs(Dict, Pairs0), 643 ( nonvar(Tag), 644 json_options_tag(Options, Name), 645 Name \== '' 646 -> Pairs = [Name-Tag|Pairs0] 647 ; Pairs = Pairs0 648 ), 649 json_write_object(Pairs, Stream, State, Options). 650json_write_term(List, Stream, State, Options) :- 651 is_list(List), 652 !, 653 space_if_not_at_left_margin(Stream, State), 654 write(Stream, '['), 655 ( json_write_state_width(State, Width), 656 ( Width == 0 657 -> true 658 ; json_write_state_indent(State, Indent), 659 json_print_length(List, Options, Width, Indent, _) 660 ) 661 -> set_width_of_json_write_state(0, State, State2), 662 write_array_hor(List, Stream, State2, Options), 663 write(Stream, ']') 664 ; step_indent(State, State2), 665 write_array_ver(List, Stream, State2, Options), 666 indent(Stream, State), 667 write(Stream, ']') 668 ). 669 670json_write_term(Term, Stream, State, Options) :- 671 json_write_hook(Term, Stream, State, Options), 672 !. 673json_write_term(Number, Stream, _State, _Options) :- 674 number(Number), 675 !, 676 ( float(Number) 677 -> write(Stream, Number) 678 ; integer(Number) 679 -> write(Stream, Number) 680 ; Float is float(Number) % rational number 681 -> write(Stream, Float) 682 ). 683json_write_term(True, Stream, _State, Options) :- 684 json_options_true(Options, True), 685 !, 686 write(Stream, true). 687json_write_term(False, Stream, _State, Options) :- 688 json_options_false(Options, False), 689 !, 690 write(Stream, false). 691json_write_term(Null, Stream, _State, Options) :- 692 json_options_null(Options, Null), 693 !, 694 write(Stream, null). 695json_write_term(#(Text), Stream, _State, _Options) :- 696 !, 697 ( ( atom(Text) 698 ; string(Text) 699 ) 700 -> json_write_string(Stream, Text) 701 ; term_string(Text, String), 702 json_write_string(Stream, String) 703 ). 704json_write_term(String, Stream, _State, _Options) :- 705 atom(String), 706 !, 707 json_write_string(Stream, String). 708json_write_term(String, Stream, _State, _Options) :- 709 string(String), 710 !, 711 json_write_string(Stream, String). 712json_write_term(AnyTerm, Stream, State, _Options) :- 713 ( json_write_state_serialize_unknown(State, true) 714 -> term_string(AnyTerm, String), 715 json_write_string(Stream, String) 716 ; type_error(json_term, AnyTerm) 717 ). 718 719json_pairs(Dict, Pairs) :- 720 json_dict_pairs(Dict, Pairs), 721 !. 722json_pairs(Dict, Pairs) :- 723 dict_pairs(Dict, _, Pairs). 724 725json_write_object(Pairs, Stream, State, Options) :- 726 space_if_not_at_left_margin(Stream, State), 727 write(Stream, '{'), 728 ( json_write_state_width(State, Width), 729 ( Width == 0 730 -> true 731 ; json_write_state_indent(State, Indent), 732 json_print_length(json(Pairs), Options, Width, Indent, _) 733 ) 734 -> set_width_of_json_write_state(0, State, State2), 735 write_pairs_hor(Pairs, Stream, State2, Options), 736 write(Stream, '}') 737 ; step_indent(State, State2), 738 write_pairs_ver(Pairs, Stream, State2, Options), 739 indent(Stream, State), 740 write(Stream, '}') 741 ). 742 743 744write_pairs_hor([], _, _, _). 745write_pairs_hor([H|T], Stream, State, Options) :- 746 json_pair(H, Name, Value), 747 json_write_string(Stream, Name), 748 write(Stream, ':'), 749 json_write_term(Value, Stream, State, Options), 750 ( T == [] 751 -> true 752 ; ( json_write_state_width(State, 0) 753 -> write(Stream, ',') 754 ; write(Stream, ', ') 755 ), 756 write_pairs_hor(T, Stream, State, Options) 757 ). 758 759write_pairs_ver([], _, _, _). 760write_pairs_ver([H|T], Stream, State, Options) :- 761 indent(Stream, State), 762 json_pair(H, Name, Value), 763 json_write_string(Stream, Name), 764 write(Stream, ':'), 765 json_write_term(Value, Stream, State, Options), 766 ( T == [] 767 -> true 768 ; write(Stream, ','), 769 write_pairs_ver(T, Stream, State, Options) 770 ). 771 772 773json_pair(Var, _, _) :- 774 var(Var), 775 !, 776 instantiation_error(Var). 777json_pair(Name=Value, Name, Value) :- !. 778json_pair(Name-Value, Name, Value) :- !. 779json_pair(NameValue, Name, Value) :- 780 compound(NameValue), 781 NameValue =.. [Name, Value], 782 !. 783json_pair(Pair, _, _) :- 784 type_error(json_pair, Pair). 785 786 787write_array_hor([], _, _, _). 788write_array_hor([H|T], Stream, State, Options) :- 789 json_write_term(H, Stream, State, Options), 790 ( T == [] 791 -> write(Stream, ' ') 792 ; write(Stream, ', '), 793 write_array_hor(T, Stream, State, Options) 794 ). 795 796write_array_ver([], _, _, _). 797write_array_ver([H|T], Stream, State, Options) :- 798 indent(Stream, State), 799 json_write_term(H, Stream, State, Options), 800 ( T == [] 801 -> true 802 ; write(Stream, ','), 803 write_array_ver(T, Stream, State, Options) 804 ). 805 806 807indent(Stream, State) :- 808 json_write_state_indent(State, Indent), 809 json_write_state_tab(State, Tab), 810 json_write_indent(Stream, Indent, Tab). 811 812step_indent(State0, State) :- 813 json_write_state_indent(State0, Indent), 814 json_write_state_step(State0, Step), 815 NewIndent is Indent+Step, 816 set_indent_of_json_write_state(NewIndent, State0, State). 817 818space_if_not_at_left_margin(Stream, State) :- 819 stream_pair(Stream, _, Write), 820 line_position(Write, LinePos), 821 ( LinePos == 0 822 ; json_write_state_indent(State, LinePos) 823 ), 824 !. 825space_if_not_at_left_margin(Stream, _) :- 826 put_char(Stream, ' ').
836json_print_length(Var, _, _, _, _) :- 837 var(Var), 838 !, 839 instantiation_error(Var). 840json_print_length(json(Pairs), Options, Max, Len0, Len) :- 841 !, 842 Len1 is Len0 + 2, 843 Len1 =< Max, 844 must_be(list, Pairs), 845 pairs_print_length(Pairs, Options, Max, Len1, Len). 846json_print_length(Dict, Options, Max, Len0, Len) :- 847 is_dict(Dict), 848 !, 849 dict_pairs(Dict, _Tag, Pairs), 850 Len1 is Len0 + 2, 851 Len1 =< Max, 852 pairs_print_length(Pairs, Options, Max, Len1, Len). 853json_print_length(Array, Options, Max, Len0, Len) :- 854 is_list(Array), 855 !, 856 Len1 is Len0 + 2, 857 Len1 =< Max, 858 array_print_length(Array, Options, Max, Len1, Len). 859json_print_length(Null, Options, Max, Len0, Len) :- 860 json_options_null(Options, Null), 861 !, 862 Len is Len0 + 4, 863 Len =< Max. 864json_print_length(False, Options, Max, Len0, Len) :- 865 json_options_false(Options, False), 866 !, 867 Len is Len0 + 5, 868 Len =< Max. 869json_print_length(True, Options, Max, Len0, Len) :- 870 json_options_true(Options, True), 871 !, 872 Len is Len0 + 4, 873 Len =< Max. 874json_print_length(Number, _Options, Max, Len0, Len) :- 875 number(Number), 876 !, 877 write_length(Number, AL, []), 878 Len is Len0 + AL, 879 Len =< Max. 880json_print_length(@(Id), _Options, Max, Len0, Len) :- 881 atom(Id), 882 !, 883 atom_length(Id, IdLen), 884 Len is Len0+IdLen, 885 Len =< Max. 886json_print_length(String, _Options, Max, Len0, Len) :- 887 string_len(String, Len0, Len), 888 !, 889 Len =< Max. 890json_print_length(AnyTerm, _Options, Max, Len0, Len) :- 891 write_length(AnyTerm, AL, []), % will be serialized 892 Len is Len0 + AL+2, 893 Len =< Max. 894 895pairs_print_length([], _, _, Len, Len). 896pairs_print_length([H|T], Options, Max, Len0, Len) :- 897 pair_len(H, Options, Max, Len0, Len1), 898 ( T == [] 899 -> Len = Len1 900 ; Len2 is Len1 + 2, 901 Len2 =< Max, 902 pairs_print_length(T, Options, Max, Len2, Len) 903 ). 904 905pair_len(Pair, Options, Max, Len0, Len) :- 906 compound(Pair), 907 pair_nv(Pair, Name, Value), 908 !, 909 string_len(Name, Len0, Len1), 910 Len2 is Len1+2, 911 Len2 =< Max, 912 json_print_length(Value, Options, Max, Len2, Len). 913pair_len(Pair, _Options, _Max, _Len0, _Len) :- 914 type_error(pair, Pair). 915 916pair_nv(Name=Value, Name, Value) :- !. 917pair_nv(Name-Value, Name, Value) :- !. 918pair_nv(Term, Name, Value) :- 919 compound_name_arguments(Term, Name, [Value]). 920 921array_print_length([], _, _, Len, Len). 922array_print_length([H|T], Options, Max, Len0, Len) :- 923 json_print_length(H, Options, Max, Len0, Len1), 924 ( T == [] 925 -> Len = Len1 926 ; Len2 is Len1+2, 927 Len2 =< Max, 928 array_print_length(T, Options, Max, Len2, Len) 929 ). 930 931string_len(String, Len0, Len) :- 932 atom(String), 933 !, 934 atom_length(String, AL), 935 Len is Len0 + AL + 2. 936string_len(String, Len0, Len) :- 937 string(String), 938 !, 939 string_length(String, AL), 940 Len is Len0 + AL + 2. 941 942 943 /******************************* 944 * TEST * 945 *******************************/
true, false and null constants.954is_json_term(Term) :- 955 default_json_options(Options), 956 is_json_term2(Options, Term). 957 958is_json_term(Term, Options) :- 959 make_json_options(Options, OptionTerm, _RestOptions), 960 is_json_term2(OptionTerm, Term). 961 962is_json_term2(_, Var) :- 963 var(Var), !, fail. 964is_json_term2(Options, json(Pairs)) :- 965 !, 966 is_list(Pairs), 967 maplist(is_json_pair(Options), Pairs). 968is_json_term2(Options, List) :- 969 is_list(List), 970 !, 971 maplist(is_json_term2(Options), List). 972is_json_term2(_, Primitive) :- 973 atomic(Primitive), 974 !. % atom, string or number 975is_json_term2(Options, True) :- 976 json_options_true(Options, True). 977is_json_term2(Options, False) :- 978 json_options_false(Options, False). 979is_json_term2(Options, Null) :- 980 json_options_null(Options, Null). 981 982is_json_pair(_, Var) :- 983 var(Var), !, fail. 984is_json_pair(Options, Name=Value) :- 985 atom(Name), 986 is_json_term2(Options, Value). 987 988 /******************************* 989 * DICT SUPPORT * 990 *******************************/
true, false and null are represented using these
Prolog atoms.type field in an object assigns a tag for
the dict.
The predicate json_read_dict/3 processes the same options as
json_read/3, but with different defaults. In addition, it
processes the tag option. See json_read/3 for details about
the shared options.
tag option does not
apply.null.true.falsestring. The alternative is atom, producing a
packed string object.1031json_read_dict(Stream, Dict) :- 1032 json_read_dict(Stream, Dict, []). 1033 1034json_read_dict(Stream, Dict, Options) :- 1035 make_json_dict_options(Options, OptionTerm, _RestOptions), 1036 ( json_value_top(Stream, Term, OptionTerm) 1037 -> true 1038 ; syntax_error(illegal_json, Stream) 1039 ), 1040 term_to_dict(Term, Dict, OptionTerm). 1041 1042term_to_dict(Var, Var, _Options) :- 1043 var(Var), 1044 !. 1045term_to_dict(json(Pairs), Dict, Options) :- 1046 !, 1047 ( json_options_tag(Options, TagName), 1048 Tag \== '', 1049 select(TagName = Tag0, Pairs, NVPairs), 1050 to_atom(Tag0, Tag) 1051 -> json_dict_pairs(NVPairs, DictPairs, Options) 1052 ; json_options_default_tag(Options, DefTag), 1053 ( var(DefTag) 1054 -> true 1055 ; Tag = DefTag 1056 ), 1057 json_dict_pairs(Pairs, DictPairs, Options) 1058 ), 1059 dict_create(Dict, Tag, DictPairs). 1060term_to_dict(Value0, Value, _Options) :- 1061 atomic(Value0), Value0 \== [], 1062 !, 1063 Value = Value0. 1064term_to_dict(List0, List, Options) :- 1065 is_list(List0), 1066 !, 1067 terms_to_dicts(List0, List, Options). 1068term_to_dict(Special, Special, Options) :- 1069 ( json_options_true(Options, Special) 1070 ; json_options_false(Options, Special) 1071 ; json_options_null(Options, Special) 1072 ; json_options_end_of_file(Options, Special) 1073 ), 1074 !. 1075 1076json_dict_pairs([], [], _). 1077json_dict_pairs([Name=Value0|T0], [Name=Value|T], Options) :- 1078 term_to_dict(Value0, Value, Options), 1079 json_dict_pairs(T0, T, Options). 1080 1081terms_to_dicts([], [], _). 1082terms_to_dicts([Value0|T0], [Value|T], Options) :- 1083 term_to_dict(Value0, Value, Options), 1084 terms_to_dicts(T0, T, Options). 1085 1086to_atom(Tag, Atom) :- 1087 string(Tag), 1088 !, 1089 atom_string(Atom, Tag). 1090to_atom(Atom, Atom) :- 1091 atom(Atom).
1100json_write_dict(Stream, Dict) :- 1101 json_write_dict(Stream, Dict, []). 1102 1103json_write_dict(Stream, Dict, Options) :- 1104 make_json_write_state(Options, State, Options1), 1105 make_json_dict_options(Options1, OptionTerm, _RestOptions), 1106 json_write_term(Dict, Stream, State, OptionTerm). 1107 1108 1109make_json_dict_options(Options, Record, RestOptions) :- 1110 default_json_dict_options(Record0), 1111 set_json_options_fields(Options, Record0, Record, RestOptions).
atom,
string or codes.1124atom_json_dict(Atom, Term, Options) :- 1125 ground(Atom), 1126 !, 1127 setup_call_cleanup( 1128 open_string(Atom, In), 1129 json_read_dict(In, Term, Options), 1130 close(In)). 1131atom_json_dict(Result, Term, Options) :- 1132 select_option(as(Type), Options, Options1, atom), 1133 ( type_term(Type, Result, Out) 1134 -> true 1135 ; must_be(oneof([atom,string,codes]), Type) 1136 ), 1137 with_output_to(Out, 1138 json_write_dict(current_output, Term, Options1)). 1139 1140 /******************************* 1141 * QUASI-QUOTATIONS * 1142 *******************************/
json
indicator. Notably, you can't use a Prolog variable in place of an
object key. Here is an example.
{|json(Name)||
{ "name": Name,
"created": {
"day":null,
"month":"December",
"year":2007
},
"confirmed":true,
"members":[1,2,3]
}
|}.
1168:- quasi_quotation_syntax(json). 1169 1170json(Content, Vars, Dict, Result) :- 1171 must_be(list, Dict), 1172 include(qq_var(Vars), Dict, QQDict), 1173 with_quasi_quotation_input(Content, Stream, 1174 json_read_dict(Stream, Result, 1175 [ qqdict(QQDict) 1176 ])). 1177 1178qq_var(Vars, _=Var) :- 1179 member(V, Vars), 1180 V == Var, 1181 !. 1182 1183 1184 /******************************* 1185 * MESSAGES * 1186 *******************************/ 1187 1188:- multifile 1189 prolog:error_message/3. 1190 1191prologerror_message(syntax_error(json(Id))) --> 1192 [ 'JSON syntax error: ' ], 1193 json_syntax_error(Id). 1194 1195json_syntax_error(illegal_comment) --> 1196 [ 'Illegal comment' ]. 1197json_syntax_error(illegal_string_escape) --> 1198 [ 'Illegal escape sequence in string' ]. 1199json_syntax_error(illegal_surrogate_pair) --> 1200 [ 'Illegal escaped surrogate pair in string' ]. 1201json_syntax_error(non_existing_var(Var, QQDict)) --> 1202 { maplist(arg(1), QQDict, Vars), 1203 Term =.. [json|Vars] 1204 }, 1205 [ 'Variable ', ansi(code, '~w', [Var]), 1206 ' is not defined in {|',ansi(code,'~w',Term),'|||}' 1207 ]. 1208 1209 /******************************* 1210 * SANDBOX * 1211 *******************************/ 1212 1213:- multifile sandbox:safe_primitive/1. 1214 1215sandbox:safe_primitive(json:json(_,_,_,_))
Reading and writing JSON serialization
This module supports reading and writing JSON objects. This library supports two Prolog representations (the new representation is only supported in SWI-Prolog version 7 and later):
json(NameValueList), a JSON string as an atom and the JSON constantsnull,trueandfalseas @(null), @(true) and @false.null,trueandfalse.This module provides the
jsonQuasi Quotation syntax that allows for embedding JSON documents in Prolog.http_json.pllinks JSON to the HTTP client and server modules.json_convert.plconverts JSON Prolog terms to more comfortable terms. */