The predicate json_dict/2 was created to understand some of the finer intricacies for consistently converting JSON to an SWI-Prolog dict and/or converting an SWI-Prolog dict to JSON . It is not meant to be used directly in code, it is for learning.
json_dict(Json0,Dict1) :-
% mode(+,-)
nonvar(Json0),
var(Dict1), !,
% is_json_term/1 expects an atom and not a term
% so convert term to an atom.
term_to_atom(Json0,Json_atom0),
is_json_term(Json_atom0),
% When using atom_json_dict/3 to create a dict, use the option value_string_as(atom) to get a consistent tractable representation.
atom_json_dict(Json_atom0,Dict0,[value_string_as(atom)]),
atom_json_dict(Json_atom1,Dict0,[]),
% When using atom_json_dict/3 to create a dict, use the option value_string_as(atom) to get a consistent tractable representation.
atom_json_dict(Json_atom1,Dict1,[value_string_as(atom)]),
is_dict(Dict1),
% Since atoms and strings can be used interchangeably for most SWI-Prolog built-in predicate arguments
% one can think of term_string/2 as term_atom/2.
% This can be used as the reverse of term_to_atom/2, (think atom_to_term/2)
term_string(Json1,Json_atom1),
% =@= used to allow for difference in system generated tags, e.g. _65108{} and _36929{} are the same.
assertion( Dict0 =@= Dict1 ),
assertion( Json0 == Json1 ).
json_dict(Json1,Dict0) :-
% mode(-,+)
var(Json1),
nonvar(Dict0), !,
% atom_json_dict/3 will accept some values that are not valid dicts, e.g. {}
% Since the goal is to consistently convert the values, only valid dicts are allowed.
is_dict(Dict0),
atom_json_dict(Json_atom0,Dict0,[]),
% When using atom_json_dict/3 to create a dict, use the option value_string_as(atom) to get a consistent tractable representation.
atom_json_dict(Json_atom0,Dict1,[value_string_as(atom)]),
atom_json_dict(Json_atom1,Dict1,[]),
is_json_term(Json_atom1),
% Since atoms and strings can be used interchangeably for most SWI-Prolog built-in predicate arguments
% one can think of term_string/2 as term_atom/2.
% This can be used as the reverse of term_to_atom/2, (think atom_to_term/2)
term_string(Json0,Json_atom0),
term_string(Json1,Json_atom1),
% =@= used to allow for difference in system generated tags, e.g. _65108{} and _36929{} are the same.
assertion( Dict0 =@= Dict1 ),
assertion( Json0 == Json1 ).
json_dict(Json0,Dict0) :-
% mode(+,+)
nonvar(Json0),
nonvar(Dict0), !,
% check mode(+,-)
json_dict(Json0,Dict1),
% check mode(-,+)
json_dict(Json1,Dict0),
assertion( Dict0 =@= Dict1 ),
assertion( Json0 == Json1 ).
json_dict(Json,Dict) :-
format('Invalid input:\n Json: ~w\n Dict: ~w~n',[Json,Dict]).
Notes (Click triangle to exapand)
SWI-Prolog dicts are for holding key-value pairs so an SWI-Prolog dict can only represent JSON starting as an object, an SWI-Prolog dict can not represent some stand alone JSON values like []
.
atom_json_dict/3 will accept '[]'
as the first argument and correctly convert it to a term but the term is not a valid SWI-Prolog dict.
e.g.
?- json:atom_json_dict('[]',Dict,[]).
Dict = [].
?- json:atom_json_dict('[]',Dict,[]),is_dict(Dict).
false.
{}
is not a SW-Prolog dict however atom_json_dict/2 will correctly convert it to an SWI-Prolog dict.
_{}
is a dict.
e.g.
?- Json = '{}',json:atom_json_dict(Json,Dict,[]),is_dict(Dict).
Json = {},
Dict = _19260{}.
When reading a JSON file using json_read_dict/3 one may find that converting the internal representation into atoms using option value_string_as(atom)
works more intuitively with the built-in SWI-Prolog predicates.
SWI-Prolog dicts allow for numbers as keys but JSON only allows for strings as keys.
If an SWI-Prolog dict uses a number for a key then json_write_dict/3 will throw an error.
e.g.
?- Dict = _{ 1:"a" },json:json_write_dict(current_output,Dict).
{
ERROR: Type error: `text' expected, found `1' (an integer)
is_json_term/1 will correctly process the JSON {"key":value}
as an atom and not as a term.
e.g.
?- json:is_json_term({"key":value}).
false.
?- json:is_json_term('{"key":value}').
true.
Parsing JSON is a Minefield
Why JSON isn’t a good configuration language
I wanted to save this somewhere related to SWI-Prolog and JSON this looked like an easy to find location.