diff --git a/x/logic/predicate/json.go b/x/logic/predicate/json.go index 7028f62c..bd3af008 100644 --- a/x/logic/predicate/json.go +++ b/x/logic/predicate/json.go @@ -3,9 +3,11 @@ package predicate import ( "encoding/json" "fmt" + "sort" "github.com/ichiban/prolog/engine" "github.com/okp4/okp4d/x/logic/util" + "github.com/samber/lo" ) // AtomJSON is a term which represents a json as a compound term `json([Pair])`. @@ -46,6 +48,20 @@ func jsonToTerms(value any) (engine.Term, error) { switch v := value.(type) { case string: return util.StringToTerm(v), nil + case map[string]any: + keys := lo.Keys(v) + sort.Strings(keys) + + attributes := make([]engine.Term, 0, len(v)) + for _, key := range keys { + attributeValue, err := jsonToTerms(v[key]) + if err != nil { + return nil, err + } + attributes = append(attributes, AtomPair.Apply(engine.NewAtom(key), attributeValue)) + } + + return AtomJSON.Apply(engine.List(attributes...)), nil default: return nil, fmt.Errorf("could not convert %s (%T) to a prolog term", v, v) } diff --git a/x/logic/predicate/json_test.go b/x/logic/predicate/json_test.go index 277bd288..39343a73 100644 --- a/x/logic/predicate/json_test.go +++ b/x/logic/predicate/json_test.go @@ -19,23 +19,62 @@ import ( func TestJsonProlog(t *testing.T) { Convey("Given a test cases", t, func() { cases := []struct { + description string program string query string wantResult []types.TermResults wantError error wantSuccess bool }{ + // ** JSON -> Prolog ** + // String { - query: `json_prolog('"foo"', Term).`, + description: "convert direct string (valid json) into prolog", + query: `json_prolog('"foo"', Term).`, wantResult: []types.TermResults{{ "Term": "foo", }}, wantSuccess: true, }, { - query: `json_prolog('{"foo": "bar"}', Term).`, + description: "convert direct string with space (valid json) into prolog", + query: `json_prolog('"a string with space"', Term).`, wantResult: []types.TermResults{{ - "Term": "json([-(foo, 'bar')])", + "Term": "'a string with space'", + }}, + wantSuccess: true, + }, + // ** JSON -> Prolog ** + // Object + { + description: "convert json object into prolog", + query: `json_prolog('{"foo": "bar"}', Term).`, + wantResult: []types.TermResults{{ + "Term": "json([foo-bar])", + }}, + wantSuccess: true, + }, + { + description: "convert json object with multiple attribute into prolog", + query: `json_prolog('{"foo": "bar", "foobar": "bar foo"}', Term).`, + wantResult: []types.TermResults{{ + "Term": "json([foo-bar,foobar-'bar foo'])", + }}, + wantSuccess: true, + }, + { + description: "convert json object with attribute with a space into prolog", + query: `json_prolog('{"string with space": "bar"}', Term).`, + wantResult: []types.TermResults{{ + "Term": "json(['string with space'-bar])", + }}, + wantSuccess: true, + }, + { + description: "ensure determinism on object attribute key sorted alphabetically", + query: `json_prolog('{"b": "a", "a": "b"}', Term).`, + wantResult: []types.TermResults{{ + "Term": "json([a-b,b-a])", }}, wantSuccess: true, },