1

我正在使用 Salat 库将案例类序列化为 mongoDb 对象。我的 Item.scala 文件如下所示:

case class Item(_id: String = (new ObjectId).toString, itemId: Int, var name: String, var active: Boolean) extends WithId {
  override def id: ObjectId = new ObjectId(_id)
}

object Item extends MongoDb[Item] with MongoDao[Item] {
  override def collectionName: String = "items"
}

object ItemJsonProtocol extends DefaultJsonProtocol {
  implicit val itemFormat = jsonFormat4(Item.apply)
}

现在,我使用它通过 Spray HTTP 将 Item 实体发布为 Json。我想按如下方式调用它:

curl.exe -H "Content-Type: application/json" -X PUT -d "{\"itemId\":
1, \"active\":true, \"name\" : \"test\"}" http://localhost:8080/items/

如果我不提供,希望它会提供生成的 id。

但是,在调用 curl 命令后,我收到一个错误:

请求内容格式错误:对象缺少必需的成员“_id”

有什么方法可以将 _id 字段标记为可选而不使 Option 不在其中(该字段将始终设置)并定义自定义 JsonFormat 从而自己(反)序列化对象?

我读过这篇文章:https ://stackoverflow.com/a/10820293/1042869 ,但我想知道是否还有其他方法可以做到这一点,因为我有很多 _id 字段的情况。也有一条评论说“您可以在案例类定义中为该字段指定默认值,因此如果该字段不在 json 中,它将为其分配默认值。”,但正如您在此处看到的它似乎不起作用。

最好的,马辛

4

2 回答 2

0

我会使用这个解决方案:

case class Item(_id: Option[String], itemId: Int, var name: String, var active: Boolean)

implicit object ItemJsonFormat extends RootJsonFormat[Item] {
   override def read(value: JsValue) = {
     val _id = fromField[Option[String]](value, "_id")
     val itemId = fromField[Int](value, "itemId")
     val expires = fromField[Long](value, "expires")
     val name = fromField[String](value, "name")
     val active = fromField[Boolean](value, "active")
     Item(_id, itemId, name, active)
   }
   override def write(obj: Item): JsValue = JsObject(
     "_id" -> JsString(obj._id),
     "itemId" -> JsNumber(obj.itemId),
     "name" -> JsString(obj.name),
     "active" -> JsBoolean(obj.active)
   )
}

该解决方案的优势json.asJsObject.getFields在于您可以更好地控制在未定义 id 的情况下接受的内容。失败的示例如下:

  • itemId 是一个字符串,和 id 一样
  • id 已定义,但 itemId 未定义

在这种情况下,匹配案例会将指定的 id 解释为 itemId 而不会捕获错误。

于 2015-02-04T18:08:32.813 回答
0

所以我通过编写自定义 RootJsonFormat:: 解决了这个问题

  implicit object ItemJsonFormat extends RootJsonFormat[Item] {
    override def read(json: JsValue): Item = json.asJsObject.getFields("_id", "itemId", "name", "active") match {
      case Seq(JsString(_id), JsNumber(itemId), JsString(name), JsBoolean(active)) => Item(_id = _id, itemId = itemId.toInt, name = name, active = active)
      case Seq(JsNumber(itemId), JsString(name), JsBoolean(active)) => Item(itemId = itemId.toInt, name = name, active = active)
      case _ => throw new DeserializationException("Item expected")
    }
    override def write(obj: Item): JsValue = JsObject(
      "_id" -> JsString(obj._id),
      "itemId" -> JsNumber(obj.itemId),
      "name" -> JsString(obj.name),
      "active" -> JsBoolean(obj.active)
    )
  }

基本上它的作用是检查我们是否收到了 json 中的 _id,如果收到了,那么我们使用它来构造对象,在其他情况下保留自动生成的 id 字段。

另一件事可能会引起一些麻烦,但在我看来值得一提——如果有人对嵌套对象(“非原始”类型)有疑问——我建议在 write def 中使用.toJson (如obj.time.toJson,其中obj.time 是 jodatime 的 DateTime) 和 JsValue 的.convertTo[T] def in read,比如 time = JsString(time).convertTo[DateTime]。为了使其工作,必须为那些“非原始”对象定义隐式 json 格式。

最好的,马辛

于 2014-07-03T05:29:21.223 回答