简体   繁体   中英

Custom Json Writes with combinators - not all the fields of the case class are needed

I'm trying to write a custom Json serializer in play for a case class but I don't want it to serialize all the fields of the class. I'm pretty new to Scala, so that is surely the problem but this is what I tried so far:

case class Foo(a: String, b: Int, c: Double)

Now the default way of doing this, as far as I saw in the examples is:

implicit val fooWrites: Writes[Foo] = (
  (__ \ "a").write[String] and
  (__ \ "b").write[Int]
  (__ \ "c").write[Double]
) (unlift(Foo.unapply))

But what if I want to omit "c" from the Json output? I've tried this so far but it doesn't compile:

implicit val fooWritesAlt: Writes[Foo] = (
  (__ \ "a").write[String] and
  (__ \ "b").write[Int]
) (unlift({(f: Foo) => Some((f.a, f.b))}))

Any help is greatly appreciated!

If you are using Playframework 2.2 (not sure about earlier versions, but it should work as well) try this:

implicit val writer = new Writes[Foo] {
  def writes(foo: Foo): JsValue = {
    Json.obj("a" -> foo.a,
             "b" -> foo.b)
  }
}

What I usually do is convert a field to None and use writeNullable on it:

implicit val fooWrites: Writes[Foo] = (
  (__ \ "a").write[String] and
  (__ \ "b").write[Int]
  (__ \ "c").writeNullable[Double].contramap((_: Double) => None)
) (unlift(Foo.unapply))

Instead of specifying a combinator which produces an OWrites instance, one can directly construct an OWrites just as well:

val ignore = OWrites[Any](_ => Json.obj())

implicit val fooWrites: Writes[Foo] = (
  (__ \ "a").write[String] and
  (__ \ "b").write[Int] and
  ignore
) (unlift(Foo.unapply))

This will ignore any value of the case class at that position and simply always return an empty JSON object.

This is very easy to do with the Play's JSON transformers:

val outputFoo = (__ \ 'c).json.prune

and then apply transform(outputFoo) to your existing JsValue:

  val foo: Foo
  val unprunedJson: JsValue = Json.toJson(foo)

  unprunedJson.transform(outputFoo).map { jsonp =>
    Ok(Json.obj("foo" -> jsonp))
  }.recoverTotal { e =>
    BadRequest(JsError.toFlatJson(e))
  }

see here http://mandubian.com/2013/01/13/JSON-Coast-to-Coast/

What version of Play are you using? fooWritesAlt compiles for me using 2.1.3. One thing to note is that I needed to explicitly use the writes object to write the partial JSON object, ie

fooWritesAt.writes(Foo("a", 1, 2.0))

returns

{"a": "a", "b": 2}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM