Reflections on Maps and Lists

June 1st, 2007

While testing the new Groovy features of Json-lib I discovered some things that made me reflect back on some of the Groovy magic that we have come to love, I suspect that most of the issues have to do with JSONObject and JSONArray now implementing Map and List.

When coding the leftShift operator for JSONObject I decided to process Maps and Lists only, if the parameter was a list (of size >= 2) I’ll just grab the first element and treat it like a key, so it fell very natural to write something like:

  1. json.element( list.shift, list )  


… And obviously it didn’t work because Lists do not have a shift method! (talking about a serious case of Perl memories), the ‘workaround’ is not a problem as shift is in reality a call to remove(0) at the very least. I wonder if we need this syntactic sugar in the GDK…

JSONObject and JSONArray also implement java.util.Comparable, which is the requirement for the comparison operators to work on your classes, although all of them seem to work fine with JSONObject, it is not the case for JSONArray as the following code cases a ScriptException:

  1. def array1 = JSONArray.formObject([1,2,3])  
  2. def array2 = JSONArray.fromObject([1,2,3,4])  
  3. assert array1 < array2  


This is where I think that the way Groovy handles Lists is preventing JSONArray to handle the operator (I have yet to confirm this) but adding log entries to compareTo shows that the method is not called, even if a custom MetaClass is in place (adding logging to invokeMethod). Which takes me to the third issue I found, which is the toString() method. Both JSONarray and JSONObject provide their own implementation but when printed to the console the output appears to be handled by Map’s and List’s toString method:

  1. def object = new JSONObject().element(“key”,1)  
  2. def array = JSONArray.fromObject([object])  
  3. println object // [”key,1]  instead of {”key”:1}  
  4. println array // [[”key”,1]] instead of [{”key”:1}]  


You may think that the problem lies only on JSONObject, but if a JSONFunction is stored as an element on an array, its string representation will be surrounded by double quotes (List.toString() in action) instead of being printed as is (JSONarray.toString() in action). Calls to toString() are not registered by my JSONObjectMetaClass.invokeMethod, that’s why I think someone else intercepted the call first =(

I’ll keep looking for alternatives for my compareTo() and toString() problems, luckily I have a copy of GINA by my side.

Keep on Groovying!

Andres Almiray

Tags:

Groovier JSON

June 1st, 2007

It’s time for a batch of updates on what’s been going on the Groovy side of Json-lib.

  1. Because JSONObject and JSONArray now implement java.util.Map and java.util.List you can use them as such in Groovy.
  2. JSONObject and JSONArray also implement java.util.Comparable, making it very easy to use the comparison operators (even the spaceship operator). I’ve found a caveat so far, because JSONArray is also a List, the interpreter will throw an Exception stating that it can compare the two values, but you can use compareTo() (as in Java) for the time being.
  3. The leftshift ( << ) operator is supported by JSONObject, the accepted values are Map and List and it works as a shorthand for element(), it follows these rules:
    • If the shifted arg is a Map, it will call putAll() on the object.
    • If the shifted arg is a List and its size == 2, the first element will be the key, and the second will be the value.
    • If the shifted arg is a List and its size > 2, the first element will be the key, the arg will be shifted by 1 and passed as the value (will create a JSONArray because it is a List).
    • Any other type will be discarded, the object will not be affected nor an exception will be thrown.
  4. The JsonGrovyBuilder is finished, I used Grail’s BeansBuilder as a reference instead of going with the traditional way of extending BuilderSupport, this approach allows for the following code to be transformed into the same JSON:

 

  1. def books1 = builder.books {
  2. book = [title: “The Definitive Guide to Grails”, author: “Graeme Rocher”]
  3. book = [title: “The Definitive Guide to Grails”, author: “Graeme Rocher”]
  4. }
  5. def books2 = builder.books {
  6. book = new Book(title: “The Definitive Guide to Grails”,
  7. author: “Graeme Rocher”)
  8. book = new Book(title: “The Definitive Guide to Grails”,
  9. author: “Graeme Rocher”)
  10. }
  11. def books3 = builder.books {
  12. book = {
  13. title = “The Definitive Guide to Grails”
  14. author= “Graeme Rocher”
  15. }
  16. book = {
  17. title = “The Definitive Guide to Grails”
  18. author= “Graeme Rocher”
  19. }
  20. }
  21. def books4 = builder.books {
  22. book {
  23. title = “The Definitive Guide to Grails”
  24. author= “Graeme Rocher”
  25. }
  26. book {
  27. title = “The Definitive Guide to Grails”
  28. author= “Graeme Rocher”
  29. }
  30. }
  31. def books5 = builder.books {
  32. 2.times {
  33. book = {
  34. title = “The Definitive Guide to Grails”
  35. author= “Graeme Rocher”
  36. }
  37. }
  38. }
  39. def books6 = builder.books {
  40. 2.times {
  41. book {
  42. title = “The Definitive Guide to Grails”
  43. author= “Graeme Rocher”
  44. }
  45. }
  46. }
  47. /*
  48. all 6 books variables output the same JSON
  49. {”books”: {
  50. “book”: [{
  51. “title”: “The Definitive Guide to Grails”,
  52. “author”: “Graeme Rocher”
  53. },{
  54. “title”: “The Definitive Guide to Grails”,
  55. “author”: “Graeme Rocher”
  56. }]
  57. }
  58. }
  59. */

Andres Almiray

Tags: