GithubHelp home page GithubHelp logo

playframework / play-json Goto Github PK

View Code? Open in Web Editor NEW
354.0 17.0 132.0 2.06 MB

The Play JSON library

License: Apache License 2.0

Scala 99.95% Shell 0.05%
json dsl convenience-macros json-ast automatic-conversion scala playframework scalajs serialization serializer

play-json's Introduction

Play JSON

Twitter Follow Discord GitHub Discussions StackOverflow YouTube Twitch Status OpenCollective

Build Status Maven Repository size Scala Steward badge Mergify Status

Play JSON is a powerful Scala JSON library, originally developed by the Play team for use with Play Framework. It uses Jackson for JSON parsing and has no Play dependencies.

We've provided some documentation here on how to use Play JSON in your app (without Play). For more information on how to use Play JSON in Play, please refer to the Play documentation.

Getting Started

To get started, you can add play-json as a dependency in your project:

  • sbt
    libraryDependencies += "org.playframework" %% "play-json" % -version-
  • Gradle
    compile group: 'org.playframework', name: 'play-json_2.13', version: -version-
    
  • Maven
    <dependency>
      <groupId>org.playframework</groupId>
      <artifactId>play-json_2.13</artifactId>
      <version>-version-</version>
    </dependency>

See GitHub releases for the correct version.

Play JSON supports Scala 2.12, 2.13 and Scala 3.3+. Choosing the right JAR is automatically managed in sbt. If you're using Gradle or Maven then you need to use the correct version in the artifactId.

JSON AST

The base type in Play JSON is play.api.libs.json.JsValue, and has several subtypes representing different JSON types:

  • JsObject: a JSON object, represented as a Map. Can be constructed from an ordered Seq or any kind of Map using JsObject.apply
  • JsArray: a JSON array, consisting of a Seq[JsValue]
  • JsNumber: a JSON number, represented as a BigDecimal.
  • JsString: a JSON string.
  • JsBoolean: a JSON boolean, either JsTrue or JsFalse.
  • JsNull: the JSON null value.

The play.api.libs.json package includes several features for constructing JSON from scratch, as well as for converting to and from case classes.

Basic reading and writing

The play.api.libs.json.Json object has several methods for reading and writing:

Json.parse parses a JSON string or InputStream into a JSON tree:

val json: JsValue = Json.parse("""
{
  "name" : "Watership Down",
  "location" : {
    "lat" : 51.235685,
    "long" : -1.309197
  },
  "residents" : [ {
    "name" : "Fiver",
    "age" : 4,
    "role" : null
  }, {
    "name" : "Bigwig",
    "age" : 6,
    "role" : "Owsla"
  } ]
}
""")

and Json.stringify is used to convert a JsValue to a String of JSON:

val jsonString = Json.stringify(json)
// {"name":"Watership Down","location":{"lat":51.235685,"long":-1.309197},"residents":[{"name":"Fiver","age":4,"role":null},{"name":"Bigwig","age":6,"role":"Owsla"}]}

Traversing a JsValue

Play JSON provides a traversal DSL that lets you query fields in the JSON:

Simple path \

Applying the \ operator will return the property corresponding to the field argument, supposing this is a JsObject.

val lat = (json \ "location" \ "lat").get
// returns JsNumber(51.235685)

The (json \ "location" \ "lat") returns a JsLookupResult which may or may not contain a value. Note that the get operation is not always safe; it throws an exception if the path doesn't exist.

You can also use \ to look up indices within a JsArray:

val bigwig = (json \ "residents" \ 1).get
// returns {"name":"Bigwig","age":6,"role":"Owsla"}

Recursive path \\

Applying the \\ operator will do a lookup for the field in the current object and all descendants.

val names = json \\ "name"
// returns Seq(JsString("Watership Down"), JsString("Fiver"), JsString("Bigwig"))

Index lookup

You can retrieve a value in a JsObject or JsArray using an apply operator with the index number or key.

val name = json("name")
// returns JsString("Watership Down")

val bigwig = json("residents")(1)
// returns {"name":"Bigwig","age":6,"role":"Owsla"}

Like get, this will throw an exception if the index doesn't exist. Use the Simple Path \ operator and validate or asOpt (described below) if you expect that they key may not be present.

Reading and writing objects

To convert a Scala object to and from JSON, we use Json.toJson[T: Writes] and Json.fromJson[T: Reads] respectively. Play JSON provides the Reads and Writes typeclasses to define how to read or write specific types. You can get these either by using Play's automatic JSON macros, or by manually defining them.

You can also read JSON from a JsValue using validate, as and asOpt methods. Generally it's preferable to use validate since it returns a JsResult which may contain an error if the JSON is malformed.

For example:

val unsafeName = (json \ "name").as[String]
// "Watership Down"

val unsafeBogusName = (json \ "bogus").as[String]
// throws exception

val nameOption = (json \ "name").asOpt[String]
// Some("Watership Down")

val bogusOption = (json \ "bogus").asOpt[String]
// None

val nameResult = (json \ "name").validate[String]
// JsSuccess("Watership Down")

val bogusResult = (json \ "bogus").validate[String]
// JsError

val unsafeName2 = json("name").as[String]
// "Watership Down"

val unsafeBogusName2 = json("bogus").as[String]
// throws exception

Automatic conversion

Usually you don't need to traverse JSON AST directly. Play JSON comes equipped with some convenient macros to convert to and from case classes.

For example, suppose I have the following class:

case class Resident(name: String, age: Int, role: Option[String])

I can define a Reads (JSON parser), Writes (JSON writer) using convenient macros:

implicit val residentReads = Json.reads[Resident]
implicit val residentWrites = Json.writes[Resident]

I can also define a Format that does both:

implicit val residentFormat = Json.format[Resident]

With the Reads and/or Writes in scope, I can then easily convert my class using toJson and fromJson

Constructing Reads and Writes

Play JSON provides a convenient functional DSL for constructing Reads and Writes. For example, assume I have the following classes:

case class Location(lat: Double, long: Double)
case class Resident(name: String, age: Int, role: Option[String])
case class Place(name: String, location: Location, residents: Seq[Resident])

Then I could construct Reads for them as follows:

import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._

implicit val locationReads: Reads[Location] = (
  (JsPath \ "lat").read[Double](min(-90.0) keepAnd max(90.0)) and
  (JsPath \ "long").read[Double](min(-180.0) keepAnd max(180.0))
)(Location.apply _)

implicit val residentReads: Reads[Resident] = (
  (JsPath \ "name").read[String](minLength[String](2)) and
  (JsPath \ "age").read[Int](min(0) keepAnd max(150)) and
  (JsPath \ "role").readNullable[String]
)(Resident.apply _)

implicit val placeReads: Reads[Place] = (
  (JsPath \ "name").read[String](minLength[String](2)) and
  (JsPath \ "location").read[Location] and
  (JsPath \ "residents").read[Seq[Resident]]
)(Place.apply _)


val json = { ... }

json.validate[Place] match {
  case s: JsSuccess[Place] => {
    val place: Place = s.get
    // do something with place
  }
  case e: JsError => {
    // error handling flow
  }
}

Similarly, I could construct Writes like this:

import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val locationWrites: Writes[Location] = (
  (JsPath \ "lat").write[Double] and
  (JsPath \ "long").write[Double]
)(unlift(Location.unapply))

implicit val residentWrites: Writes[Resident] = (
  (JsPath \ "name").write[String] and
  (JsPath \ "age").write[Int] and
  (JsPath \ "role").writeNullable[String]
)(unlift(Resident.unapply))

implicit val placeWrites: Writes[Place] = (
  (JsPath \ "name").write[String] and
  (JsPath \ "location").write[Location] and
  (JsPath \ "residents").write[Seq[Resident]]
)(unlift(Place.unapply))


val place = Place(
  "Watership Down",
  Location(51.235685, -1.309197),
  Seq(
    Resident("Fiver", 4, None),
    Resident("Bigwig", 6, Some("Owsla"))
  )
)

val json = Json.toJson(place)

It is also possible to implement custom logic by implementing the Reads, Writes and/or Format traits manually, but we recommend using the automatic conversion macros or the functional DSL if possible.

Manual JSON construction

JSON can also be manually constructed using a DSL:

val json: JsValue = Json.obj(
  "name" -> "Watership Down",
  "location" -> Json.obj("lat" -> 51.235685, "long" -> -1.309197),
  "residents" -> Json.arr(
    Json.obj(
      "name" -> "Fiver",
      "age" -> 4,
      "role" -> JsNull
    ),
    Json.obj(
      "name" -> "Bigwig",
      "age" -> 6,
      "role" -> "Owsla"
    )
  )
)

Releasing a new version

See https://github.com/playframework/.github/blob/main/RELEASING.md

License

Play JSON is licensed under the Apache license, version 2. See the LICENSE file for more information.

play-json's People

Contributors

aaabramov avatar avdv avatar billyautrey avatar cchantep avatar dwickern avatar dwijnand avatar ennru avatar gmethvin avatar htmldoug avatar ignasi35 avatar ihostage avatar jroper avatar lavrov avatar lihaoyi avatar lolgab avatar marcospereira avatar mergify[bot] avatar mkurz avatar octonato avatar pjfanning avatar raboof avatar richdougherty avatar scala-steward avatar sethtisue avatar sgodbillon avatar sullis avatar tototoshi avatar vdebergue avatar wsargent avatar xuwei-k avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

play-json's Issues

Misuse of c.info() in JsMacroImpl

Specifically here: https://github.com/playframework/play-json/blob/master/play-json/shared/src/main/scala/JsMacroImpl.scala#L243

If you have a case class with an apply method of the same arity as the primary constructor, this code logs an INFO statement during compilation.

However, the docs specify that c.info should represent a warning, not an INFO statement (http://docs.scala-lang.org/overviews/macros/overview.html#reporting-warnings-and-errors)

Having multiple apply methods of the same arity is fine, and should not be warned against.

getOrElse argument could to be lazy to match with Option.getOrElse

Hi,

First of all, Happy New Year to you all and thanks for all your efforts on this project!

Second, I ran into an issue today, where I tried the following code:
val structureId = (json \ "id").getOrElse(sys.error(s"Cannot find 'id' property in SessieOverview JSON: $json"))

But I didn't get the behaviour I expected from getOrElse (the same behaviour as Option.getOrElse), which is that the parameter to getOrElse is lazy. In my case, the parameter is evaluated eagerly, which resulted in an exception (I know I'm not being purely functional here, but in general I think it's good practice to evaluate the else part lazily, since that corresponds with the "else" keyword).

Could you change this line in:
def getOrElse(v: JsValue): JsValue = toOption.getOrElse(v)

into:
@inline def getOrElse(v: => JsValue): JsValue = toOption.getOrElse(v)

Much appreciated!

Regards,
Jan-Kees

JsPath.json.put writing to an object in a nested array exception: expected KeyPathNode

Play JSON Version (2.5.x / etc)

2.5.15

API (Scala / Java / Neither / Both)

Scala (didn't test Java)

Operating System

Window 10

JDK

java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

Library Dependencies

Expected Behavior

I would expect to use a JsPath.json.update and JsPath.json.put to update a single field within an object within a nested array. (See code below)

Actual Behavior

When attempting to put a new field in a JsObject that's inside an array, a RuntimeException is generated.
The following code throws an exception:

expected KeyPathNode
java.lang.RuntimeException: expected KeyPathNode
	at play.api.libs.json.JsPath$.step$1(JsPath.scala:142)
	at play.api.libs.json.JsPath$.step$1(JsPath.scala:141)
	at play.api.libs.json.JsPath$.play$api$libs$json$JsPath$$buildSubPath$1(JsPath.scala:147)
	at play.api.libs.json.JsPath$$anonfun$createObj$1.apply(JsPath.scala:152)
	at play.api.libs.json.JsPath$$anonfun$createObj$1.apply(JsPath.scala:150)
	at scala.collection.IndexedSeqOptimized$class.foldl(IndexedSeqOptimized.scala:57)
	at scala.collection.IndexedSeqOptimized$class.foldLeft(IndexedSeqOptimized.scala:66)
	at scala.collection.mutable.WrappedArray.foldLeft(WrappedArray.scala:35)
	at play.api.libs.json.JsPath$.createObj(JsPath.scala:150)
	at play.api.libs.json.PathReads$$anonfun$jsPut$1.apply(JsConstraints.scala:63)
	at play.api.libs.json.PathReads$$anonfun$jsPut$1.apply(JsConstraints.scala:63)
	at play.api.libs.json.Reads$$anon$8.reads(Reads.scala:126)
	at play.api.libs.json.PathReads$$anonfun$jsUpdate$1$$anonfun$apply$11.apply(JsConstraints.scala:72)
	at play.api.libs.json.PathReads$$anonfun$jsUpdate$1$$anonfun$apply$11.apply(JsConstraints.scala:72)
	at play.api.libs.json.JsResult$class.flatMap(JsResult.scala:99)
	at play.api.libs.json.JsSuccess.flatMap(JsResult.scala:9)
	at play.api.libs.json.PathReads$$anonfun$jsUpdate$1.apply(JsConstraints.scala:72)
	at play.api.libs.json.PathReads$$anonfun$jsUpdate$1.apply(JsConstraints.scala:69)
	at play.api.libs.json.Reads$$anon$8.reads(Reads.scala:126)
	at play.api.libs.json.JsValue$class.validate(JsValue.scala:18)
	at play.api.libs.json.JsObject.validate(JsValue.scala:76)
	at play.api.libs.json.JsReadable$class.transform(JsReadable.scala:29)
	at play.api.libs.json.JsObject.transform(JsValue.scala:76)
  private def setValueByPath(data: JsObject, path: JsPath, value: JsValue) : JsObject = {
     data.transform(__.json.update(path.json.put({ value }))) match {
      case s: JsSuccess[JsObject] => s.get
      case e: JsError => throw new IllegalArgumentException("Failed")
    }
  }
  val data = Json.obj(
    "array" -> Json.arr(Json.obj())
  )
  val newData = setValueByPath(data, __ \ "array" \ 0 \ "field", Json.toJson("arrayField"))

Reproducible Test Case

package jspathtest
import org.scalatest._
import play.api.libs.json._

class TestJsPath extends FlatSpec with Matchers {

  "A JsPath" should "allow setting a value in a nested branch in array" in {

    val data = Json.obj(
      "array" -> Json.arr(Json.obj())
    )
    val newData = setValueByPath(data, __ \ "array" \ 0 \ "field", Json.toJson("arrayField"))
    newData should be (Json.obj("array" -> Json.arr(Json.obj("field" -> "arrayField"))))
  }

  it should "allow setting a value in a root field" in {

    val data = Json.obj(
      "array" -> Json.arr(Json.obj())
    )
    val newData = setValueByPath(data, __ \ "field", Json.toJson("field"))

    newData should be (Json.obj("field" -> "field", "array" -> Json.arr(Json.obj())))
  }


  it should "allow setting a value in a nested obj field" in {

    val data = Json.obj(
      "other" -> Json.obj()
    )
    val newData = setValueByPath(data, __ \ "other" \ "field", Json.toJson("otherField"))
    newData should be (Json.obj("other" -> Json.obj("field" -> "otherField")))
  }

  private def setValueByPath(data: JsObject, path: JsPath, value: JsValue) : JsObject = {
    data.transform(__.json.update(path.json.put({ value }))) match {
      case s: JsSuccess[JsObject] => s.get
      case e: JsError => throw new IllegalArgumentException("Failed")
    }
  }
}

Links to scaladoc broken when published

json.Json.format does not work with -Yno-predef

If a project has

scalacOptions += "-Yno-predef"

it will be unable to use the Json.format (and related) macros because they expand to use symbols that are not imported, such as scala.Predef.implicitly.

The fix is pretty simple (and good practice in macros) which is to use _root_.-qualified FQNs for all symbols in the macro (thereby not making any assumptions about the caller's scope).

This is a regression from play 2.5... I assume the macro has changed.

Collision of names causes a compiler error for disparate formatter types

Play JSON Version (2.5.x / etc)

play-json 2.5.7

API (Scala / Java / Neither / Both)

scala

Operating System (Ubuntu 15.10 / MacOS 10.10 / Windows 10)

OS X 10.13.2

JDK (Oracle 1.8.0_72, OpenJDK 1.8.x, Azul Zing)

java version "1.8.0_152"

Description and test case

Reproducible Test Case

I am writing a simple JSON serializer for java.io.File just stingifying the path:

import java.io.File

import play.api.libs.json._
import Implicits.File._

object Implicits {
  object File {
    implicit val format: Format[File] = new Format[File] {
      override def writes(o: File): JsValue = JsString(o.toString)
      override def reads(js: JsValue): JsResult[File] = js.validate[String].map(f => new File(f))
    }
  }
}

final case class Bar(path: File) 

object Bar {
  implicit val format: Format[Bar] = Json.format
}

I find that the above does not work:

No instance of play.api.libs.json.Format is available for java.io.File in the implicit scope

However, if I change the name of Implicit.File.format to Implicit.File.fmt, it works fine.

Make sure JSON macro performance is reasonable

The way we construct the Writes implementations with the JSON macros is not particularly efficient, as I have noticed in a few real-world apps. We should devise some tests to determine how much a performance impact there is versus manually constructing the object, and see where we can improve.

Enhance json example

We can improve validation pattern matching example a bit:

val nameResult: JsResult[String] = (json \ "name").validate[String]

// Pattern matching
nameResult match {
  case s: JsSuccess[String] => println("Name: " + s.get)
  case e: JsError => println("Errors: " + JsError.toJson(e).toString())
}

With this:

val nameResult: JsResult[String] = (json \ "name").validate[String]

// Pattern matching
nameResult match {
  case JsSuccess(name) => println("Name: " + name)
  case e: JsError => println("Errors: " + JsError.toJson(e).toString())
}

What do you think, community?

Json.writes derivation fails to support simple case classes

import play.api.libs.json._

final case class EventDeserializationFailure(cause: String)
object EventDeserializationFailure {
  implicit val writes: Writes[EventDeserializationFailure] = Json.writes
}

fails with

[error] No apply function found for scala.Any
[error]     implicit val writes: Writes[EventDeserializationFailure] = Json.writes
[error]                                                                     ^

using play 2.6.6 on scala 2.12.4

toJson does not always preserve the order of elements in collection

Play Version 2.6.3

I found that LowPriorityWrites.traversableWrites does not always preserve the order.
For instance we have scala.collection.mutable.SortedSet of elements, however json has wrong output because of as.map(w.writes(_))
I suppose it should be something like as.toSeq.map(w.writes(_)

Expected Behavior

Json.toJson(scala.collection.mutable.SortedSet(1, 2, 3)) = [1, 2, 3]

Actual Behavior

Json.toJson(scala.collection.mutable.SortedSet(1, 2, 3)) = [2,1,3]

NullPointerException with Writes.contramap

I have a project where I made this change:

-    Writes(
-      a => Writes.of[Seq[Either[JsonRpcNotificationMessage, JsonRpcRequestMessage]]].writes(a.messages)
-    )
+    Writes
+      .of[Seq[Either[JsonRpcNotificationMessage, JsonRpcRequestMessage]]]
+      .contramap(_.messages)
   )

That change works just fine.

I also made this change:

-    Writes(
-      a => Writes.of[Seq[JsonRpcResponseMessage]].writes(a.messages)
-    )
+    Writes
+      .of[Seq[JsonRpcResponseMessage]]
+      .contramap(_.messages)

That results in a NullPointerException when running the tests:

[info]   - should encode to [{"jsonrpc":"2.0","result":{"param1":"param1","param2":"param2"},"id":1}] *** FAILED ***
[info]     java.lang.NullPointerException:
[info]     at play.api.libs.json.LowPriorityWrites.$anonfun$traversableWrites$2(Writes.scala:267)
[info]     at play.api.libs.json.LowPriorityWrites$$Lambda$2412/520876750.apply(Unknown Source)
[info]     at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:234)
[info]     at scala.collection.TraversableLike$$Lambda$95/1119390268.apply(Unknown Source)
[info]     at scala.collection.immutable.List.foreach(List.scala:378)
[info]     at scala.collection.TraversableLike.map(TraversableLike.scala:234)
[info]     at scala.collection.TraversableLike.map$(TraversableLike.scala:227)
[info]     at scala.collection.immutable.List.map(List.scala:284)
[info]     at play.api.libs.json.LowPriorityWrites.$anonfun$traversableWrites$1(Writes.scala:267)
[info]     at play.api.libs.json.LowPriorityWrites$$Lambda$2161/1735133288.apply(Unknown Source)
[info]     ...

The full reproducible test case is an open pull request at https://github.com/dhpcs/scala-json-rpc/pull/19/files.

The test failure can be seen in the build log at https://travis-ci.org/dhpcs/scala-json-rpc/builds/210733337.

I've spent a couple of hours trying to figure out the root cause (mostly by looking at the stack in IntelliJ when it fails to see what is null) but have not reached any conclusion yet and am starting to doubt that I will. If it is that I've misunderstood something and the issue is with what I have done, it's certainly not obvious to me, so I thought it better that I open this issue in case this really is a play-json bug.

The issue isn't critical because I already have something that works. I just ran into this while tidying it up, and it does seem like it could be a play-json issue.

Configuration

play-json version: Replicated with 2.5.13 and 2.6.0-M5
API: Replicated with Scala 2.11.8 and 2.12.1
Library dependencies: ScalaTest 3.0.1

OS and JVM:

Replicated on Ubuntu 16.04.2 (Linux ash 4.8.0-42-generic #45~16.04.1-Ubuntu SMP Thu Mar 9 14:10:58 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux) with

openjdk version "1.8.0_121"
OpenJDK Runtime Environment (build 1.8.0_121-8u121-b13-0ubuntu1.16.04.2-b13)
OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)

and on macOS 10.12 with

java version "1.8.0_102"
Java(TM) SE Runtime Environment (build 1.8.0_102-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode)

Implement support for > 22 field case classes in macros

(Moved from playframework/playframework#3174)

In Scala 2.11, a case class can have more than 22 fields. We could modify our existing macros to support these. Here's approximately how to do it:

  • Detect > 22 field case class - if it's not greater than 22 fields, then simply generate reads/writes identical to the way they are generated now.
  • Group fields into groups of 22 (or less).
  • Generate reads/writes for tuples of each of the groups fields
  • Generate reads/writes for the case class that combines the reads/writes for the groups. It will have to use the constructor to create the class, and access the fields directly to write the class into a set of tuples, rather than using the apply/unapply methods of the companion object.

Then we should be able to support case classes with 484 parameters. I don't know if it's possible to create such a thing, since I think there's a 255 parameter limit for methods.

Customization for Json macros

(Moved from playframework/playframework#4684)

As more people start using the json macros, a lot of people seem to want to customize features slightly. We should come up with a general strategy for doing this. Ideally it should be possible to use several of these customizations at once.

Here are a few features we might want to customize:

  • Property naming strategy (see playframework/playframework#2333)
  • Using default values in Reads implementation (see #4)
  • Serializing properties for getters (see #3306)
  • Renaming/ignoring specific properties

When it comes to the behavior for things like property naming and default values, it should also be easy to use the same conventions across your application.

Why there is need for `val vjs = ...` in JsMacroImpl:503?

During compilation for 2.12 with set of scalac options:

    "-encoding", "utf8",
    "-feature",
    "-deprecation",
    "-unchecked",
    "-deprecation",
    "-target:jvm-1.8",
    "-language:_",
    "-Xlog-reflective-calls",
    "-Xlint",
    "-Ywarn-adapted-args",
    "-Ywarn-numeric-widen",
    "-Xfatal-warnings",
    "-Xfuture"

I got error like:

....local val vjs in value $anonfun is never used
[error]   implicit val XXXFormat: Format[XXX] = Json.format[XXX]

And I couldn't find the source of the error, till I searched in All Places in IDEA...
https://github.com/playframework/play-json/blob/2.6.3/play-json/shared/src/main/scala/JsMacroImpl.scala#L503

It would be nice, if code generated by macros would be clean and minimal. :)

NullPointerException trying to pickle Coursier case class

$ amm
Loading...
Welcome to the Ammonite Repl 1.0.3
(Scala 2.12.4 Java 1.8.0_152)
If you like Ammonite, please support our development at www.patreon.com/lihaoyi

@ import $ivy.`com.typesafe.play::play-json:2.6.6`
import $ivy.$

@ import play.api.libs.json._
import play.api.libs.json._

@ {
    implicit val modFormat: Format[coursier.Module] = Json.format
    implicit val depFormat: Format[coursier.Dependency] = Json.format
    implicit val attrFormat: Format[coursier.Attributes] = Json.format
  }
modFormat: Format[coursier.package.Module] = play.api.libs.json.OFormat$$anon$1@295d8852
depFormat: Format[coursier.package.Dependency] = play.api.libs.json.OFormat$$anon$1@142409b4
attrFormat: Format[coursier.package.Attributes] = play.api.libs.json.OFormat$$anon$1@7445fdb2

@ Json.toJson(coursier.Dependency(coursier.Module("org.scala-lang", "scala-reflect"), "2.12.4"))
java.lang.NullPointerException
  play.api.libs.json.PathWrites.$anonfun$at$3(JsConstraints.scala:175)
  play.api.libs.json.OWrites$$anon$3.writes(Writes.scala:112)
  play.api.libs.json.OFormat$$anon$2.writes(Format.scala:46)
  play.api.libs.json.OWrites$MergedOWrites$.mergeIn(Writes.scala:76)
  play.api.libs.json.OWrites$MergedOWrites$$anon$1.writeFields(Writes.scala:68)
  play.api.libs.json.OWrites$MergedOWrites$$anon$1.writeFields(Writes.scala:64)
  play.api.libs.json.OWrites$OWritesFromFields.writes(Writes.scala:96)
  play.api.libs.json.OWrites$OWritesFromFields.writes$(Writes.scala:94)
  play.api.libs.json.OWrites$MergedOWrites$$anon$1.writes(Writes.scala:64)
  play.api.libs.json.OFormat$$anon$2.writes(Format.scala:46)
  play.api.libs.json.OWrites$MergedOWrites$.mergeIn(Writes.scala:76)
  play.api.libs.json.OWrites$MergedOWrites$$anon$1.writeFields(Writes.scala:67)
  play.api.libs.json.OWrites$MergedOWrites$$anon$1.writeFields(Writes.scala:64)
  play.api.libs.json.OWrites$OWritesFromFields.writes(Writes.scala:96)
  play.api.libs.json.OWrites$OWritesFromFields.writes$(Writes.scala:94)
  play.api.libs.json.OWrites$MergedOWrites$$anon$1.writes(Writes.scala:64)
  play.api.libs.json.OFormat$$anon$2.writes(Format.scala:46)
  play.api.libs.json.OWrites$MergedOWrites$.mergeIn(Writes.scala:76)
  play.api.libs.json.OWrites$MergedOWrites$$anon$1.writeFields(Writes.scala:67)
  play.api.libs.json.OWrites$MergedOWrites$$anon$1.writeFields(Writes.scala:64)
  play.api.libs.json.OWrites$OWritesFromFields.writes(Writes.scala:96)
  play.api.libs.json.OWrites$OWritesFromFields.writes$(Writes.scala:94)
  play.api.libs.json.OWrites$MergedOWrites$$anon$1.writes(Writes.scala:64)
  play.api.libs.json.OFormat$$anon$2.writes(Format.scala:46)
  play.api.libs.json.OFormat$$anon$5.$anonfun$inmap$2(Format.scala:29)
  play.api.libs.json.OFormat$$anon$1.writes(Format.scala:39)
  ammonite.$sess.cmd7$.$anonfun$depFormat$4(cmd7.sc:2)
  play.api.libs.json.OFormat$$anon$1.writes(Format.scala:39)
  play.api.libs.json.OFormat$$anon$1.writes(Format.scala:35)
  play.api.libs.json.Json$.toJson(Json.scala:189)
  ammonite.$sess.cmd8$.<init>(cmd8.sc:1)
  ammonite.$sess.cmd8$.<clinit>(cmd8.sc)

json ContravariantFunctor[OWrites] should be a ContravariantFunctor[Writes]

(Moved from playframework/playframework#952)

OWrites defines an implicit ContravariantFunctor[OWrites]
it should be a ContravariantFunctor[Writes]

use case

lets have

case class PoneyFriend(friends: List[String])

object PoneyFriend {
    implicit def poneyFriendWrites: Writes[PoneyFriend] = (
      list(
         (__ \ "friend").write[String]
      ) contramap (unlift(PoneyFriend.unapply))
    )
}

list return a Writes[List[String]] and I will have an error :

scala: value contramap is not a member of play.api.libs.json.Writes[List[String]]
possible cause: maybe a semicolon is missing before `value contramap'?
) contramap (unlift(PoneyFriend.unapply))
^

adding the following code solve this error

implicit val contravariantfunctorWrites: ContravariantFunctor[Writes] = new ContravariantFunctor[Writes] {
    def contramap[A, B](wa: Writes[A], f: (B) => A) = Writes[B](b => wa.writes(f(b)))
  }

is it possible to change ContravariantFunctor[OWrites] as a ContravariantFunctor[Writes] ?

json.Json.format does not support case object

this is the upstream bug for https://github.com/fommil/stalactite/issues/14

I've created a macro that makes it a lot easier to derive typeclasses with semi-auto derivation. But it seems that .format does not support case object, requiring special handling. I'd like you to consider supporting case object in your macro to avoid the need for special casing in stalactite. Everything based on shapeless derivation is able to support case object as a product alongside case classes, so I think play-json is the odd one out.

Integrating with ScalaJSON AST

The efforts from both SLIP-28 and the scalaplatform has finally been materialized, ScalaJSON AST is now released on the scalaplatfrom with its first milestone release (see https://github.com/mdedetrich/scalajson for more information). Its expected that a final release will be made in a few days pending that there aren't many major issues.

This ticket is meant to deal with any potential migration issues, also note that there already exists an integration at https://github.com/mdedetrich/playframework/tree/scalaJsonAstIntegration (although this is with an older version of Play 2.5).

There are different ways to integrate the AST, one would be with (mainly) source compatibility which is the reason behind https://github.com/mdedetrich/playframework/blob/b0934ce452d82312f37cd62af28524c6840a4c45/framework/src/play-json/src/main/scala/play/api/libs/json/package.scala#L47-L58. Another alternative would be to just deal with the fact that JsValue is now just JValue (same goes for all other json types). In any case I suspect that this is going to be a big breaking change that will require a major release (play-json version 2.7.x to coincide with play 2.7 release).

I can also help with the integration once I know what the goals are

Monoid[JsObject] does not satisfy associative law

(Moved from playframework/playframework#4651)

Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_45).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import play.api.libs.json._
import play.api.libs.json._

scala> import play.api.libs.json.Reads.JsObjectMonoid
import play.api.libs.json.Reads.JsObjectMonoid

scala> val a = Json.obj("key" -> Json.obj("x" -> 1))
a: play.api.libs.json.JsObject = {"key":{"x":1}}

scala> val b = Json.obj("key" -> 2)
b: play.api.libs.json.JsObject = {"key":2}

scala> val c = Json.obj("key" -> Json.obj("y" -> 3))
c: play.api.libs.json.JsObject = {"key":{"y":3}}

scala> JsObjectMonoid.append(JsObjectMonoid.append(a, b), c)
res0: play.api.libs.json.JsObject = {"key":{"y":3}}

scala> JsObjectMonoid.append(a, JsObjectMonoid.append(b, c))
res1: play.api.libs.json.JsObject = {"key":{"x":1,"y":3}}
libraryDependencies += "com.typesafe.play" %% "play-json" % "2.4.0"

scalaVersion := "2.11.6"

Split core library from high-level utilities

Libraries like Play Framework usually only need to depend on the low-level library for reading and writing JSON: JsValue, Json.fromJson/Json.stringify, etc. Many of the newer changes in play-json relate to high-level functionality like the macros and configuration that would typically only be used by the end user.

It may be useful to separate this "core" library from the more high-level parts of the library like the macros and functional transformers. This way we can add features and make binary incompatible changes to the macros (for example) without creating compatibility issues with libraries like Play.

Scala.js compiler crash when using macro api

Play JSON Version

2.6.5

API

scala-js

Operating System

  • Ubuntu 16.04 (Linux hostname 4.10.0-37-generic #41~16.04.1-Ubuntu SMP Fri Oct 6 22:42:59 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux)
  • macOS Sierra

JDK

  • openjdk version "1.8.0_131" on Ubuntu
  • Oracle JVM 1.8.0_141 on Mac (As far as I remember; I don't use a Mac anymore)

Library Dependencies

Nothing other than play-json and scala-js itself

Expected Behavior

  1. Create an ADT (sealed trait hierarchy)
  2. Create Format instances of the case classes by calling Json.format[Class] respectively, and have them in the implicit scope
  3. Call Json.format[Trait]
  4. Format[Trait] should have been created by macros

Actual Behavior

Compiler crash. Additional details are available here.

Reproducible Test Case

I have a separate repo.

Branding and versioning

Seeing as how this library is no longer a part of the play release cycle, and is meant to be used outside Play, is it worth rebranding it using a new name?

If we change the artifact name we can also change the versioning scheme with minimal confusion.

Create contribution guidelines

Hello guys,

As an extensive user of Play and Play and Play Json, I would like to start contributing to those libraries. Unfortunately, for Play Json there are no contribution guidelines.

I think the README should include contributing guidelines, and it might refer to the regular (Play ones)[https://www.playframework.com/contributing]

Case class with several similar `apply` functions

I tried to upgrade from "2.5.14" to "2.6.0" and stumbled on interesting issue when case class has several similar apply functions.

package xxx

import play.api.libs.json.{Json, OFormat}

case class XXX(xxx: String, age: Int)

object XXX {
  implicit val XxxFormat: OFormat[XXX] = Json.format[XXX]
}

Compiles fine, until one adds second apply function like:

  def apply(xxx: String, age: Option[Int]): XXX = XXX(xxx, age getOrElse 21)

When above code is added in companion object, I am getting warning:

Warning:(8, 53) Type parameters are not matching: Option[Int] != Int
  implicit val XxxFormat: OFormat[XXX] = Json.format[XXX]

In our project we have many such places which are working fine with "2.5.14", and we have enabled "-Xfatal-warnings" in scalac options - this option promotes this warning to compilation error.

What is suggested way to migrate such code to 2.6.0 version?
Maybe I am doing something wrong here?

Build JsPath from raw string

Is there a way to build a JsPath from a raw string ?

Example :

From "/foo/bar" get JsPath \ "foo" \ "bar"

Is it conceivable to support this, if it is not already the case ?

If so, it would also be interesting to have a format to read / write a path.

wrong implicit resolution

import play.api.libs.json._

def dontSerialize[T](fallback: T): Format[T] = new Format[T]{
  def writes(v: T): JsValue = JsNull
  def reads(json: JsValue): JsResult[T] = JsSuccess(fallback)
}
def dontSerializeOption[T]: Format[Option[T]] = dontSerialize(None)

// Does not compile
case class Fur()
case class Foo(v: Option[Fur])
implicit val dontSerializeFur: Format[Option[Fur]] = dontSerializeOption[Fur]
implicit val formatFoo = Json.format[Foo]


// Workarround
case class Fur2()
type OFur2 = Option[Fur2]
case class Foo2(v: OFur2)
implicit val dontSerializeFur: Format[OFur2] = dontSerializeOption[Fur2]
implicit val formatFoo = Json.format[Foo2]

NPE when Json.toJson

Caused by: java.lang.NullPointerException
	at play.api.libs.json.LowPriorityWrites.$anonfun$traversableWrites$2(Writes.scala:307)
	at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:234)
	at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:59)
	at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:52)
	at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
	at scala.collection.TraversableLike.map(TraversableLike.scala:234)
	at scala.collection.TraversableLike.map$(TraversableLike.scala:227)
	at scala.collection.AbstractTraversable.map(Traversable.scala:104)
	at play.api.libs.json.LowPriorityWrites.$anonfun$traversableWrites$1(Writes.scala:307)
	at play.api.libs.json.Writes$$anon$6.writes(Writes.scala:142)
	at play.api.libs.json.PathWrites.$anonfun$nullable$2(JsConstraints.scala:184)
	at play.api.libs.json.OWrites$$anon$3.writes(Writes.scala:111)
	at play.api.libs.json.OWrites$MergedOWrites$.mergeIn(Writes.scala:75)
	at play.api.libs.json.OWrites$MergedOWrites$$anon$1.writeFields(Writes.scala:67)
	at play.api.libs.json.OWrites$MergedOWrites$$anon$1.writeFields(Writes.scala:63)
	at play.api.libs.json.OWrites$MergedOWrites$.mergeIn(Writes.scala:73)
	at play.api.libs.json.OWrites$MergedOWrites$$anon$1.writeFields(Writes.scala:66)
	at play.api.libs.json.OWrites$MergedOWrites$$anon$1.writeFields(Writes.scala:63)
	at play.api.libs.json.OWrites$OWritesFromFields.writes(Writes.scala:95)
	at play.api.libs.json.OWrites$OWritesFromFields.writes$(Writes.scala:93)
	at play.api.libs.json.OWrites$MergedOWrites$$anon$1.writes(Writes.scala:63)
	at play.api.libs.json.OWrites$$anon$2.$anonfun$contramap$1(Writes.scala:106)
	at play.api.libs.json.OWrites$$anon$3.writes(Writes.scala:111)
	at play.api.libs.json.OWrites$$anon$3.writes(Writes.scala:110)
	at play.api.libs.json.LowPriorityWrites.$anonfun$traversableWrites$2(Writes.scala:307)
	at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:234)
	at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:59)
	at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:52)
	at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
	at scala.collection.TraversableLike.map(TraversableLike.scala:234)
	at scala.collection.TraversableLike.map$(TraversableLike.scala:227)
	at scala.collection.AbstractTraversable.map(Traversable.scala:104)
	at play.api.libs.json.LowPriorityWrites.$anonfun$traversableWrites$1(Writes.scala:307)
	at play.api.libs.json.Writes$$anon$6.writes(Writes.scala:142)
	at play.api.libs.json.Json$.toJson(Json.scala:186)
	at controllers.FacadesController.$anonfun$queryTypes$2(FacadesController.scala:28)

Double values with 0 fraction written without fraction

play-json 2.6.5

issue

Play json will serialize JsNumber(BigDecimal("1.0")) to 1 where this should be 1.0

reproducer

  val mapper = new ObjectMapper()
  val jacksonObject = mapper.createObjectNode()
  jacksonObject.put("test", 1d)
  println(mapper.writerWithDefaultPrettyPrinter.writeValueAsString(jacksonObject))
  
  val playObject2 = obj(
    "test" -> 1d
  )
  println(prettyPrint(playObject2))

output looks like this

// correct with jackson
{
  "test" : 1.0
}
// incorrect with play
{
  "test" : 1
}

The code explicitly strips trailing zeros, this is a loss of precision:
https://github.com/playframework/play-json/blob/master/play-json/jvm/src/main/scala/play/api/libs/json/jackson/JacksonJson.scala#L66

usecase

Writing to KairosDB will create a schema for Real values if the incoming datapoint contains a ., otherwise data is seen as Integer.

Macro warning in 2.6.4

Hello.

When I upgraded from 2.6.3 to 2.6.4 these warnings appeared.

Ignore instance of play.api.libs.json.Writes for Option[String] (ArrayProperty.scala:106:105); Alias for Option[String] will be handled by the nullable operations.

The case class:

case class ArrayProperty(required: Boolean = false, title: Option[String] = None, description: Option[String] = None, minItems: Option[Int] = None, maxItems: Option[Int] = None, items: Property[JsValue]) extends Property[JsArray](SchemaTypes.array) {

 /* some code */

}

object ArrayProperty {
  implicit val reads: Reads[ArrayProperty] = Json.using[Json.WithDefaultValues].reads[ArrayProperty]
    .filterNot(JsonValidationError("For array property, minimum size must be less or equal to maximum size and the sizes must be greater than to zero.")) {
      case ArrayProperty(_, _, _, minItems, maxItems, _) =>
        minItems.zip(maxItems).exists { case (min, max) => min > max } ||
          minItems.exists(_ < 0) ||
          maxItems.exists(_ < 0)
    }

  implicit val format: OFormat[ArrayProperty] = OFormat(reads,Json.writes[ArrayProperty])
}

This must be related to #88 and #89. However, i not use alias type here.

JSON readNullable erroneously fails with "error.path.empty"

(Moved from playframework/playframework#6710)

In Play 2.5 (and current master) the JSON readers for nullable types do not work when used without a nested path.

// works
val composableRootReadsNullableScoped: Reads[Root] = (
  (__ \ "foo").read[Foo] and
  (__ \ "bar").readNullable[Bar]
)((foo, barOpt) => Root(foo, barOpt.getOrElse(Bar(-1, -1, -1))))

// does not work
val composableRootReadsNullable: Reads[Root] = (
  (__ \ "foo").read[Foo] and
   __.readNullable[Bar]
)((foo, barOpt) => Root(foo, barOpt.getOrElse(Bar(-1, -1, -1))))

using the following JSON inputs

// for first reader
{
  "foo": { "a": 1, "b": 0 },
  "bar": {
    "barA": 0,
    "barB": 0,
    "barC": 0
  }
},

// for second reader
{
  "foo": { "a": 1, "b": 0 },
  "barA": 0,
  "barB": 0,
  "barC": 0
}

I expect both examples to result in the same parsed Root object containing the same Foo and Bar. The use-case of handling several fields with their own specialised Reads seems very natural to me. Often API's contain rows which are multiple merged objects like bar and foo in this example, and composing the combined Reads using other Reads is the nicest solution in my opinion.

For all types & a test case, see my full example:
https://gist.github.com/hermanbanken/98057a12f349ddbc6fab9575b8aa5f2e

[error] Actual:   JsError(List((,List(ValidationError(List(error.path.empty),WrappedArray())))))
[error] Expected: JsSuccess(Root(Foo(1,0),Bar(0,0,0)),)

The JsError originates in JsPath.scala#L184. I'm not sure, but maybe the branch checking Nil is unnecessary? Nil is a valid tail, so maybe it should be handled as a empty tail?

Reads.nullable behaves weirdly

(Moved from playframework/playframework#5863)

Actual Behavior

scala> import play.api.libs.json._
import play.api.libs.json._

scala> (__ \ "foo").readNullable[String]
res0: play.api.libs.json.Reads[Option[String]] = play.api.libs.json.Reads$$anon$8@4a733e24

scala> res0.reads(Json.obj())
res1: play.api.libs.json.JsResult[Option[String]] = JsSuccess(None,)

scala> res0.reads(JsString("hello"))
res2: play.api.libs.json.JsResult[Option[String]] = JsSuccess(None,)

Expected Behavior

res2 should be a JsError.

We are trying to apply a Reads that tries to read a foo field, however our JSON value is not an object but a string. What does “reading a field” mean in the case of a string value?

Note that the case of res1 is perfectly fine: we try to read a foo field in an object that does not have such a field, so we successfully read no field.

Proposed Fix

Reads.nullable should successfully read no field (ie. return JsSuccess(None)) only if the input JSON is an object and the last path node is a KeyPathNode, or if the input JSON is an array and the last path node is a IdxPathNode.

What should we do in case of RecursiveSearch?

Make Json.reads macro handle case class default params gracefully

(Moved from playframework/playframework#3244)

It would be nice if Json.reads was able to synthesize Reads classes for case classs with default parameter values. So riffing on the example, we might have:

case class Person(name: String, age: Int, lovesChocolate: Boolean, favoriteColor: String = "blue")

implicit val personReads = (
  (__ \ 'name).read[String] and
  (__ \ 'age).read[Int] and
  (__ \ 'lovesChocolate).read[Boolean] and
  ((__ \ 'favoriteColor).read[String] orElse Reads.pure("blue"))
)(Person)

(I imagine, there's probably some non-trivial reflection needed to get the default parameter value.)

Perhaps of interest to @mandubian

Properly support ISO8601 date/time strings for Java 8

Java 8 does not properly support the ISO8601 standard.
It does not support the zone formats 'HHMM' or 'HH'.
This has been raised in this JDK Issue, but it has only been fixed in Java 9 and not for Java 8 (which everyone is still using).

Since Play Json's Reads type composes nicely, it is fairly simple to fix this so deserialising ISO8601 date/time strings to Java Time types does work as expected.

I already have the solution in our application.
If this is something which could be added to this library, I'm more than happy to create a PR for it.

Play Json 2.6
Java 8

Expected:

  1. deserialise '2018-01-11T13:45:35.260+0100' to Java Time type
  2. deserialise '2018-01-11T13:45:35.260+01' to Java Time type

Actual behavior:

  1. ParseException
  2. ParseException

Play-json should be able to read java.time.Instant instances in the default readers

Currently the user has to implement java.time.* classes themselves but they have been in since JDK 1.8. I would propose the following:

  implicit val jsonWritesInstant: Writes[Instant] = {
    case i: Instant => JsString(ISO_INSTANT.format(i))
    case null => JsNull
  }

  implicit def jsonReadsInstant: Reads[Instant] = Reads[Instant] {
    case JsString(n) =>
      try {
        JsSuccess(Instant.from(ISO_INSTANT.parse(n)))
      } catch {
        case e: DateTimeParseException => JsError("error.invalid.dateformat")
      }
    case JsNull => JsSuccess(null)
    case _ => JsError("error.expected.string")
  }

The only downside i can see is that it would bind the lib to jdk8. Possibly could be an add on module for jdk8?

Create a separate module for Joda time

Considering Java8+ time API, the Joda time support can be removed from the core of Play-JSON.
In order to keep it available for project still requiring it, the existing (deprecated) code could be moved to a separate module.

Issue creating simple writes : ContravariantFunctor and Functor conflict

Play JSON Version (2.5.x / etc)

com.typesafe.play %% play-json % 2.6.8

API (Scala / Java / Neither / Both)

Scala 2.12

Operating System (Ubuntu 15.10 / MacOS 10.10 / Windows 10)

MacOSX

JDK (Oracle 1.8.0_72, OpenJDK 1.8.x, Azul Zing)

java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b16, mixed mode)

Expected Behavior

It should compile !

Actual Behavior

Output after compilation :

cmd4.sc:5: overloaded method value apply with alternatives:
  [B](f: B => (Boolean, Boolean, Boolean, Boolean, Boolean))(implicit fu: play.api.libs.functional.ContravariantFunctor[play.api.libs.json.OWrites])play.api.libs.json.OWrites[B] <and>
  [B](f: (Boolean, Boolean, Boolean, Boolean, Boolean) => B)(implicit fu: play.api.libs.functional.Functor[play.api.libs.json.OWrites])play.api.libs.json.OWrites[B]
 cannot be applied to (ammonite.$sess.cmd2.Collect => Option[(Boolean, Boolean, Boolean, Boolean, Boolean)])
  (JsPath \ "collect_email").write[Boolean] and
                                            ^

Reproducible Test Case

import $ivy.{ `com.typesafe.play::play-json:2.6.8` } // for ammonite users

import play.api.libs.json.{JsPath, Writes}

case class Collect(
    auto_redirect: Boolean,
    collect_shipping_address: Boolean,
    collect_phone_number: Boolean,
    collect_email: Boolean,
    collect_country: Boolean,
  )

import play.api.libs.functional.syntax._
lazy val writes: Writes[Collect]  = (
  (JsPath \ "auto_redirect").write[Boolean] and
  (JsPath \ "collect_shipping_address").write[Boolean] and
  (JsPath \ "collect_phone_number").write[Boolean] and
  (JsPath \ "collect_email").write[Boolean] and
  (JsPath \ "collect_country").write[Boolean]
)(Collect.unapply _)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.