Scalaの命名規則的にcase classの属性はキャメルケースになる。spray-jsonがオブジェクトをJSONにシリアライズするとき、属性名をJSONオブジェクトのキー名にそのまま引き継ぐので、キー名はキャメルケースになる。RESTful APIで使うJSONはスネークケースのほうが好きなので、シリアライズするときに自動的に変換したい。
- キャメルケース:
productId
のようにアンダースコアを使わない書き方。 - スネークケース:
product_id
のようにアンダースコアを使う書き方。
期待するJSONの書式
{"product_id": 1, "product_name": "...", ...}
spray-jsonがデフォルトで書きだすJSONの書式
{"productId": 1, "productName": "...", ...}
spray-jsonでキャメルケースのキーをスネークケースに変換するためのスニペットがGistのagemooij / SnakifiedSprayJsonSupport.scalaで紹介されていたので、ここでも紹介したい。
SnakifiedSprayJsonSupport.scala
package com.scalapenos.spray
import spray.json._
/**
* A custom version of the Spray DefaultJsonProtocol with a modified field naming strategy
*/
trait SnakifiedSprayJsonSupport extends DefaultJsonProtocol {
import reflect._
/**
* This is the most important piece of code in this object!
* It overrides the default naming scheme used by spray-json and replaces it with a scheme that turns camelcased
* names into snakified names (i.e. using underscores as word separators).
*/
override protected def extractFieldNames(classTag: ClassTag[_]) = {
import java.util.Locale
def snakify(name: String) = PASS2.replaceAllIn(PASS1.replaceAllIn(name, REPLACEMENT), REPLACEMENT).toLowerCase(Locale.US)
super.extractFieldNames(classTag).map { snakify(_) }
}
private val PASS1 = """([A-Z]+)([A-Z][a-z])""".r
private val PASS2 = """([a-z\d])([A-Z])""".r
private val REPLACEMENT = "$1_$2"
}
object SnakifiedSprayJsonSupport extends SnakifiedSprayJsonSupport
使い方は、特に自前のJsonProtocolを定義していなければ、SnakifiedSprayJsonSupport._
をimport
するだけでOKだ。
自前のJsonProtocolのオブジェクトを作っているなら、with
でSnakifiedSprayJsonSupport
トレイトをミックスインするだけで、スネークケースの変換処理が追加される。