实体类中的scala spray json可选id参数

时间:2014-07-01 06:39:14

标签: json scala spray spray-json

我使用Salat库将case类序列化为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因此(de)自行序列化对象?

我已经阅读了这篇文章:https://stackoverflow.com/a/10820293/1042869,但我想知道是否还有其他办法可以做到这一点,因为我收到了许多_id字段。还有一条评论说"你可以在案例类定义中给该字段一个默认值,所以如果字段不在json中,它会为它分配默认值。",但正如你在这里看到的那样,它似乎无法发挥作用。

最佳, 马尔钦

2 个答案:

答案 0 :(得分: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字段。

另一件可能导致一些麻烦的事情,但在我看来值得一提 - 如果有人对嵌套对象有问题(&#34;非原始&#34;类型) - 我建议使用 .toJson < / strong> in write def(比如 obj.time.toJson ,其中obj.time是jodatime&#39; DateTime)和JsValue&#39; s .convertTo [T] def读取,如time = JsString(time).convertTo [DateTime] 。为了使其工作,必须为那些&#34;非原始&#34;定义隐式json格式。对象。

最佳, 马尔钦

答案 1 :(得分: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而不会捕获错误。