Spring MVC can pretty easily be used to render Java objects as JSON content. But for us (at work), the default out-of-the-box configuration wasn’t ideal for rendering Scala objects. I wanted to be able to write Scala code like:
... case class ServiceXResponse(id: String, val1: Option[String] = None) @RequestMapping(Array("/serviceX")) @ResponseBody def serviceX(request: HttpServletRequest) = { // do some logic but ultimately return an object for json rendering ServiceXResponse(id = "123") // or ServiceXResponse(id = "123", val1 = Some("value for optional val1")) } ...
and have JSON rendered like:
{ id: "123" }
or
{ id: "123", val1: "value for optional val1" }
The default implementation that is enabled with Spring 3.1 uses Jackson which didn’t work very well with Scala case classes and Option
. I wanted None
to be rendered as a non-existent JSON values (vs. null
). I wanted to stick with Jackson because of its performance and in-house track-record. After finding and playing around with Jerkson, I decided it would be a better fit for our Spring / Scala scenario. Spring 3.1 makes it pretty easy to configure custom MessageConverter
s. I used Rossen Stoyanchev’s post and the Spring source to determine how to write a MessageConverter
that will use Jerkson:
import org.springframework.http.{MediaType, HttpOutputMessage, HttpInputMessage} import com.codahale.jerkson.Json import org.springframework.http.converter.{HttpMessageNotWritableException, HttpMessageNotReadableException, AbstractHttpMessageConverter} import java.nio.charset.Charset import org.codehaus.jackson.JsonParseException class JerksonHttpMessageConverter extends AbstractHttpMessageConverter[Object] (new MediaType("application", "json", Charset.forName("UTF-8"))) { val json = new Json { def canWrite(clazz : Class[_]) = mapper.canSerialize(clazz) def canDeserialize(clazz: Class[_]) = mapper.canDeserialize(mapper.constructType(clazz)) } override def writeInternal(o: Object, outputMessage: HttpOutputMessage) = { try { json.generate(o, outputMessage.getBody) } catch { case ex: Exception => throw new HttpMessageNotWritableException( "Could not write JSON: " + ex.getMessage(), ex); } } override def readInternal(clazz: Class[_ <: Object], inputMessage: HttpInputMessage) = { try { json.parse(inputMessage.getBody())(Manifest.classType(clazz)) } catch { case ex: JsonParseException => throw new HttpMessageNotReadableException( "Could not read JSON: " + ex.getMessage(), ex); } } override def supports(clazz: Class[_]): Boolean = { throw new UnsupportedOperationException() } override def canRead(clazz: Class[_], mediaType: MediaType): Boolean = { json.canDeserialize(clazz) && canRead(mediaType) } override def canWrite(clazz : Class[_], medianType : MediaType) : Boolean = { json.canWrite(clazz) && canWrite(medianType) } }
Now, you can replace the default MessageConverter
s with your own. In your Spring XML file:
<mvc:annotation-driven> <mvc:message-converters> <bean class="....JerksonHttpMessageConverter"/> </mvc:message-converters> </mvc:annotation-driven>
BTW, since Jerkson has great support for not only Option
but also for Scala collections, you’ll be able to use those as well.