How to convert standard json into prolog json?

I’m working on an app that will accept user jsons. Assuming the user enters spec-compliant json, that’s not going be interpretable directly in prolog, ie.

?- J = _{a:1,b:2,c:3}, O = J.b .
J = _{a:1, b:2, c:3},
O = 2.

?- J = {a:1,b:2,c:3}, O = J.b.
ERROR: Type error: `dict' expected, found `{a:1,b:2,c:3}' (a compound)

How do I convert an entered {a:1} into prolog _{a:1} if it’s not coming from a stream?

I’m trying to look through the Supporting json and Reading and writing JSON serialization docs but maybe missing which predicate I need to do this.

Thanks

Where is it coming from?

suppose it’s coming from a direct predicate call within the program

?- [user].
|: get_json_b_param(J,B) :- B = J.b.
|: ^D% user://1 compiled 0.01 sec, 1 clauses
true.

?- get_json_b_param(_{a:1,b:2,c:3},B).
B = 2.

?- get_json_b_param({a:1,b:2,c:3},B).
ERROR: Type error: `dict' expected, found `{a:1,b:2,c:3}' (a compound)

So I need some kind of

get_json_b_param(J,B) :- convert_to_prolog_json(J,Jpl), B = Jpl.b.

for this to work.

Then just write _{k:v, ...}. It is more interesting if the JSON comes from a stream. There is json_read_dict/2 and a couple of related predicates that read and write JSON and transform it into a Prolog dict (or visa versa). As JavaScript objects, Prolog dicts look like JSON, but they are not the same thing. For a start, dicts can hold things JSON cannot such as arbitrary Prolog terms.

Then just write _{k:v, ...}

Alright then just to confirm, if I’m getting user input as a direct argument my source code, and let’s say that user isn’t familiar with prolog, let alone prolog data structures/conventions, and who would be tempted based on most other programming languages to pass in a spec-compliant json, the only way to handle that is through a readme and/or user training?

That’s called programming :wink:

In any programming language - not just Prolog.

Giving non-programmers the ability to edit the source code sounds like a recipe for disaster.

Why not store the JSON in different file(s) to the Prolog source code? And then read the JSON from a file (i.e. a stream).

1 Like

Putting an underscore on the json is pretty unique to prolog afaik. The context here is Can I get some help please taking this jwt encoder over the finish line?

I’m trying to implement jwt in prolog and provide it to some other folks to use. Most of the implementations allow for spec-compliant json. For example GitHub - mpdavis/python-jose: A JOSE implementation in Python

>>> from jose import jwt
>>> token = jwt.encode({'key': 'value'}, 'secret', algorithm='HS256')
u'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ2YWx1ZSJ9.FG-8UppwHaFp1LgRYQQeS6EDQF7_6-bMFegNucHjmWg'

so if this source code is sitting on someone’s laptop, they can just load python and go jwt.encode({'key': 'value'},'k'). I was hoping I could provide the same ease of use in prolog. If I implement this and deliver it to someone else, they’re not going to know to use _{'key': 'value'} instead, and I would prefer that they don’t have to save a single line in a file just so I can open it and read it from stream.

I just wanted to confirm that basically my only option is to put in the readme that they have to pre-pend the json with an underscore.

You have many options. E.g. do similar in Prolog to what you’re doing in Python - put the user-editable text in a string, and then do the conversion into a Prolog dictionary as part of the source code that the users are meant to not mess with.

However - I would love to see Standards documentation which states that giving non-programmers write access to programming source code is a good idea :wink:

Credentials, in particular, should not be part of source code. That’s a Security anti-pattern.

put the user-editable text in a string, and then do the conversion into a Prolog dictionary

This is what I’m asking about :slight_smile: Ok, so how do I do that? Looks like term_string/2 converts the compound to a string

?- J = {a:1}, term_string(J,Jstr).
J = {a:1},
Jstr = "{a:1}".

How do I convert that to a Prolog dictionary?

Example (have split the command into 3 lines, rather than 1 long line):

?- UserText = 'abc def',
   Dict = user{text: UserText},
   get_dict(text, Dict, Val).
UserText = Val, Val = 'abc def',
Dict = user{text:'abc def'}.

Let me ask a different way.

If I want to define

get_json_b_param(Json,B) :-
    ???

so that a user can enter any

?- get_json_b_param({b:1},B).
?- get_json_b_param({b:2},B).
?- get_json_b_param({b:3},B).

with the input json looking just like that ^^^ and return the values 1,2,3,etc, is that possible in prolog?

The example you provided doesn’t get me there.

JSON syntax is not 100% compatible with dicts, so can’t do that.

Can use native Prolog datatypes, e.g. a list contain key-value pairs:

?- Pairs = ['My key 1'-'My value 1', 'My key 2'-'My value 2'].
Pairs = ['My key 1'-'My value 1', 'My key 2'-'My value 2'].

Can just put JSON in a JSON file - why isn’t this the first choice?

Alright then it sounds like there’s no way to work with a spec-compliant json directly in the repl :frowning: I’ll just put a note in the readme.

Can just put JSON in a JSON file - why isn’t this the first choice?

I was hoping I could provide the same ease of use in prolog. If I implement this and deliver it to someone else, they’re not going to know to use _{'key': 'value'} instead, and I would prefer that they don’t have to save a single line in a file just so I can open it and read it from stream.

I mean I guess I can add an option for that too but I was really hoping I could just do

$ swipl
?- [jwtpl].
true.
?- encode({'claim':'abc'},'secret',Jwt).
Jwt = 'abcdefghij123456789' .

just like you can in python.

Thanks all.

Trying to understand: curly brackets is supported syntax in SWI-Prolog, e.g., {a,b,c} is a term with principal functor ‘{}’ and arguments a,b,c.

If the arguments are of the form Name:Value then I guess it looks a lot like a JSON object. So:

get_json_b_param(JSONobj,B) :-
    JSONobj =.. [{}|Values],
    memberchk(b:B,Values).

Now the question is how did one get the JSONobj. If it was entered in toplevel query,as in your examples, the Prolog parser recognizes the JSON syntax as entered and built the JSONobj term for free. If it came from somewhere else, e.g., in string from from a stream, then you’ll need to use one of the builtin predicates which parses strings and produces a term, e.g.,

?- term_string(T,"{a:1,b:2,c:3}").
T = {a:1, b:2, c:3}.

Did I miss your point?

All I’m trying to do is cast spec compliant json into prolog jsons. That’s all — some predicate specjson_swijson/2 that would take {a:1} and give me _{a:1}.

Your get_json_b_param/2 is cool, and thanks for showing me that, but the reason I was looking for a way to convert it to an actual json in prolog is so I can use it with prolog predicates.

Now that I understand your requirements better, ignore my last post (it’s wrong because the argument to {} is a single term - a comma list) and consider:

jTerm_to_dict(JTerm,Tag,Dict) :-
	JTerm =.. [{},Arg],
	(toPairs(Arg,Pairs) -> true ; Pairs = []),
	dict_create(Dict,Tag,Pairs).
	
toPairs((NV,NVs),[NV|Pairs]) :- !,
	toPairs(NVs,Pairs).
toPairs(NV,[NV]).	

Now:

?- jTerm_to_dict({a:1,b:2,c:3}, someTag, Dict), B = Dict.b.
Dict = someTag{a:1, b:2, c:3},
B = 2.

Any closer?

Yeah, exactly :slight_smile: and stuff like this works

?- jTerm_to_dict({a:1,b:2,c:3}, _, Dict), dict_keys(Dict, Keys).
Dict = _{a:1, b:2, c:3},
Keys = [a, b, c].

?- jTerm_to_dict({a:1,b:2,c:3}, _, Dict), dict_size(Dict,KeyCount).
Dict = _{a:1, b:2, c:3},
KeyCount = 3.

Thanks so much!

I guess my one question: Is this a bit hacky? Is the representation under the hood the same as regular dicts?

I wish this casting was more natively supported in swi but yeah it looks like this totally works.

Thanks again!

What’s a “regular dict”?

The internal representation should be exactly the same whether the Prolog parser produces a dict from a literal (in the parser, probably using dict_create/3) or the dict is produced dynamically, i.e., from user code. Prolog is a dynamic (vs. static) language, everything can be done at runtime (including modifying the program, which is a bit scary).

Was that really your expectation given that the Prolog and JSON (and JavaScript, from which it comes) syntaxes come from totally different origins at different times? It should actually be a bit surprising this can be done so easily.

Json is ubiquitous. If it can be done so easily why isn’t it in the standard library, is my point.

JSON was certainly not ubiquitous in the 70’s when Prolog was in its formative stages, nor even in the 90’s when the current ISO standard was being worked. SWIP does provide a library for supporting JSON serialization, in particular converting between JSON strings and either dictionaries or json terms.

But a “standard” library implies some kind of standards body/process to approve such things and AFAIK that doesn’t exist. The same applies to dictionaries, so don’t expect any of this to be portable, even though it’s “easy”.