surprising behaviour constructing json with the cons operator

33 views
Skip to first unread message

cao...@gmail.com

unread,
Jul 30, 2015, 7:57:14 AM7/30/15
to argonaut-json
Hi All,
I was inspired by Tuesdays Advanced Scala Meetup at Mindcandy to try Argonaut. I'm really pleased that I was able to get it working pretty quickly and am looking forward to trying some of the more advanced features.

I did run into one part of the json DSL that surprised me (possibly violating "the principle of least astonishment") and which I'd love to understand if there is a good reason for the API working that way. I think these code snippets nicely demonstrate how I got confused,

scala> "a" := "b"
res0
: (String, argonaut.Json) = (a,"b")

scala
> ("a" := "b") ->: jEmptyObject
res2
: argonaut.Json = {"a":"b"}

This makes sense. I didn't use jEmptyObject on the first line and so I got a non-json result. Things get more confusing when I try something more complicated,

scala> ("a" := ("b" := "c")) ->: jEmptyObject
res3
: argonaut.Json = {"a":["b","c"]}

Above I repeat my mistake from the first code snippet, but nested within another json object. Only Argonaut doesn't error out and manages to generate json for my input, only it infers that I want an array.

In actual fact this is what I was aiming for,

scala> ("a" := ("b" := "c") ->: jEmptyObject) ->: jEmptyObject
res4
: argonaut.Json = {"a":{"b":"c"}}

And I probably would have figured it out pretty quickly if I hadn't been confused by receiving an array instead.


The very slight syntactic differences between the two very different json outputs doesn't feel intentional and was confusing for me. 


Regardless, this was a hiccup, thanks again for a really interesting library that I'm enjoying getting to grips with. 


c

seantparsons

unread,
Jul 30, 2015, 8:10:17 AM7/30/15
to argonaut-json, cao...@gmail.com, cao...@gmail.com
Hmmm.

I would suggest that you'd probably want to break that line down into something like:
val aValue =("b" := "c") ->:jEmptyObject
(a := aValue) ->: jEmptyObject
The intention was mostly that only a single level of nesting would be done when describing objects like this really.

It gets turned into an array purely because there's an instance of EncodeJson for Tuple2[A, B] which is what the := method requires:
As that EncodeJson instance turns it into a JSON array, that's what you end up with.

Sean.

cao...@gmail.com

unread,
Jul 30, 2015, 11:38:58 AM7/30/15
to argonaut-json, seantp...@gmail.com
Hi Sean,
Thanks for the quick answer. Breaking things up is good advice under any circumstance but it's good to know the limits of what I should be trying to do with the DSL. Also, thanks for the explanation of what's going on under the hood. That was really interesting.

Slightly tangentially, but in the same vein, I was also a little surprised at the results once I started breaking my json construction down into helper functions. Everything got turned into a json type regardless of whether it was an array, object or string.

For example,

scala> def foo:Json = ("a" := "b") ->: jEmptyObject
foo: argonaut.Json

when I thought I could have

scala> def foo:JsonObject = ("a" := "b") ->: jEmptyObject
foo: argonaut.JsonObject

I can do,

scala> def foo3 = JsonObject.from(List(("a", jString("b")),  ("c", jString("d"))))
foo3: argonaut.JsonObject

but this is quite cumbersome and also I can't work out how to turn this directly into json without embedding it within a field.

Everything works fine as long as I accept that I can't be sure whether my function returns an array or an object or a string from the function signature. I just wanted to check that was the way I should use the library and that I wasn't missing a trick.

Thanks again,

c

seantparsons

unread,
Jul 30, 2015, 11:47:04 AM7/30/15
to argonaut-json, cao...@gmail.com, cao...@gmail.com
JsonObject is really only there for if you're manipulating a JSON object using the method Json.withObject for example, as _something_ is needed in that case because all of the other types that JSON elements can be boil down to some other existing representation. The Json type is what you should be dealing with by default, in your case to build that you'd use the jObjectAssocList method to construct the Json instance.

Sean.

Caoilte O'Connor

unread,
Jul 31, 2015, 1:15:06 PM7/31/15
to seantparsons, argonaut-json
Thanks Sean,
I thought that might be the case but it was good to have confirmation!

Have a great weekend,

Caoilte

Reply all
Reply to author
Forward
0 new messages