Search code examples
grailsconvertershateoas

How to create a Grails HAL converter


I know there is a HalJsonRenderer but i wonder if there would be a way to have something like the JSON converter in order to be able to do something like:

render MyObject as HAL

That "as" operator ... how it works?


Solution

  • It's a bit more clear what's happening with render ... as JSON if you add in the optional parentheses:

    render(foo as JSON)
    

    The render method has a few overloads and one of them accepts JSON, so the tricky part is foo as JSON.

    Groovy makes it straightforward to overload operators by pairing every operator with a method that actually does the work. The == operator is implemented by calling the equals method (or compareTo if the class implements Comparable, the << operator calls leftShift, etc. This archived page has a good table of operators and their corresponding methods (the Groovy site was significantly overhauled recently and this page seems to have been lost in the shuffle).

    as isn't technically an operator, but it's treated similarly. If you add an asType method, it will be invoked to give the class an opportunity to convert itself to the requested type:

    Object asType(Class c) { ... }
    

    The way that Grails wires up as JSON and as XML is rather convoluted, but the important part involves adding an asType method to all controllers (in addition to render, redirect, getParams (and the corresponding params property), etc.) This method then does the conversion to JSON or XML, or does a traditional cast/conversion if the target class isn't JSON or XML.

    I think it would be more work than it's worth to support this directly, so if it were me I'd create a method in a service that does the work of converting supported types to HAL and render that, something like

    class SomeService {
       String asHal(foo) {
          ...
       }
    }
    

    and then you can call it from your controllers:

    class SomeController {
    
       def someService
    
       def anAction() {
          ...
          def foo = ...
          render someService.asHal(foo)
       }
    }
    

    That's not quite as convenient as render as HAL, but it's not much more work and has the benefit of being less magical.

    To support render foo as HAL you would need to add an asType method to whatever class foo is, or do something similar to what Grails does for JSON and XML. Adding an asType method is probably impractical, especially if you want to support multiple types, and more so if those types are pre-existing classes like ArrayList. You could implement the org.codehaus.groovy.grails.web.converters.Converter interface like the JSON and XML classes do (looking at the support code, you actually need to extend org.codehaus.groovy.grails.web.converters.AbstractConverter), and create the associated support classes that would do the conversion (this was all designed to be extensible, although I don't think I've seen anyone do it). That would be interesting and probably a good learning experience, but as I said - way more work than it's worth given how simple the more direct implementation is.