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

5

11

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!

Thrips answered 4/12, 2013 at 19:26 Comment(0)
S
12

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)
  }
}
Spessartite answered 4/12, 2013 at 19:56 Comment(1)
Thanks, I figured out the solution you posted since my OP, but I was wondering if there's a way to do it with the combinator library as well.Thrips
Z
6

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))
Zooid answered 8/12, 2014 at 11:20 Comment(0)
W
6

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.

Welty answered 16/9, 2015 at 14:12 Comment(0)
F
2

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/

Foreknow answered 16/2, 2014 at 13:29 Comment(0)
H
1

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, i.e.

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

returns

{"a": "a", "b": 2}
Harod answered 5/12, 2013 at 13:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.