Как сериализовать тип объекта в JSON в Scalatra?

Я новичок в Скалатре. У меня есть сервлет с JacksonJsonSupport, который обслуживает конечную точку REST со списком объектов.

class OperationsController extends MyappStack with JacksonJsonSupport {

  before() {
    contentType = formats("json")
  }

  get("/") {
    Data.operations
  }
}

Operation реализуется либо Adding или же Removing тематические занятия. Как мне добавить в GET / Отвечать конкретному классу на значение? Я хотел бы получить в ответ:

[
  {
     "operation": "Adding",
     "value": 100
  }
]

Вместо

[
  {
     "value": 100
  }
]

куда Adding это класс, который расширяется Operation,

3 ответа

Решение

Для полиморфных значений json4s может добавить конкретный тип в качестве дополнительного поля. Это называется "подсказка типа":

[{
    "jsonClass": "Adding",
    "value": 10
}, {
    "jsonClass": "Adding",
    "value": 20
}, {
    "jsonClass": "Removing",
    "value": 20
}]

Это, например, с помощью ShortTypeHints:

import org.json4s.{ShortTypeHints, DefaultFormats}
import org.scalatra.ScalatraServlet
import org.scalatra.json.JacksonJsonSupport
import org.scalatra.test.specs2.MutableScalatraSpec

sealed trait Operation
case class Adding(value: Int) extends Operation
case class Removing(value: Int) extends Operation

class json1 extends MutableScalatraSpec {

  mount(new ScalatraServlet with JacksonJsonSupport {

    def typeHints = new ShortTypeHints(List(
      classOf[Adding], classOf[Removing]
    ))

    implicit lazy val jsonFormats = DefaultFormats + typeHints

    before() {
      contentType = formats("json")
    }

    get("/") {
      List(
        Adding(10),
        Adding(20),
        Removing(20)
      )
    }

  }, "/*")

  "Should return a list of operations" in {

    get("/", headers = Seq("Content-type" -> "application/json")) {
      println(body)
      status should beEqualTo(200)
    }

  }

}

Я думаю, что самый простой способ - это обновить классы, например

case class Adding(value: Int, operation: String = "Adding") 
case class Removing (value: Int, operation: String = "Removing") 

Другой способ - обновить ваши jsonFormats с помощью пользовательского сериализатора. Я нашел пример пользовательской сериализации jsons здесь.

В файле json_conversion.scala мы создали признак SimpleMongoDbJsonConversion и используем его в файле MyScalatraServlet.scala, см. пример ниже.

json_conversion.scala

package com.example.app

import org.scalatra._
import com.mongodb.casbah.Imports._

trait SimpleMongoDbJsonConversion extends ScalatraBase with ApiFormats {

  def renderMongo = {
    case dbo: DBObject =>
      contentType = formats("json")
      dbo.toString

    case xs: TraversableOnce[_] =>
      contentType = formats("json")
      val l = xs map (x => x.toString) mkString(",")
      "[" + l + "]"

  }: RenderPipeline

  override protected def renderPipeline = renderMongo orElse super.renderPipeline

}

MyScalatraServlet.scala

package com.example.app

import org.scalatra._
import com.mongodb.casbah.Imports._

class MyScalatraMongoServlet(mongoColl: MongoCollection) extends MyScalatraWebAppStack with SimpleMongoDbJsonConversion {

  get("/") {
    <html>
      <body>
        <h1>Hello, world!</h1>
        Say <a href="hello-scalate">hello to Scalate</a>.
      </body>
    </html>
  }

  post("/insert") {
    val key = params("key")
    val value = params("value")
    val newObj = MongoDBObject(key->value)
    mongoColl += newObj
  }

  get("/users") {
    mongoColl.find()
    for { x <- mongoColl } yield x
  }

}
Другие вопросы по тегам