Skip to content

Commit

Permalink
Add Json.newBuilder utility
Browse files Browse the repository at this point in the history
  • Loading branch information
cchantep committed May 7, 2023
1 parent 3c4df1d commit 72070d9
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

package scalaguide.json

import play.api.libs.json.Json
import org.specs2.mutable.Specification

//#valueClass
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
*/

package play.api.libs.json

import scala.collection.mutable.{ Builder => MBuilder }

private[json] final class JsObjectBuilder
extends MBuilder[(String, Json.JsValueWrapper), JsObject] {

private val fs = Map.newBuilder[String, JsValue]

def addOne(elem: (String, Json.JsValueWrapper)): this.type = {
val (name, wrapped) = elem

fs += (name -> Json.unwrap(wrapped))

this
}

override def addAll(xs: IterableOnce[(String, Json.JsValueWrapper)]): this.type = {
xs.iterator.foreach(addOne)

this
}

override def knownSize: Int = fs.knownSize

override def sizeHint(size: Int): Unit = {
fs.sizeHint(size)
}

def clear(): Unit = {
fs.clear()
}

def result(): JsObject = JsObject(fs.result())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
*/

package play.api.libs.json

import scala.collection.mutable.{ Builder => MBuilder }

private[json] final class JsObjectBuilder
extends MBuilder[(String, Json.JsValueWrapper), JsObject] {

private val fs = Map.newBuilder[String, JsValue]

def +=(elem: (String, Json.JsValueWrapper)): this.type = {
val (name, wrapped) = elem

fs += (name -> Json.unwrap(wrapped))

this
}

override def ++=(xs: TraversableOnce[(String, Json.JsValueWrapper)]): this.type = {
xs.foreach(`+=`)

this
}

def knownSize: Int = fs.result().size

def clear(): Unit = {
fs.clear()
}

def result(): JsObject = JsObject(fs.result())
}
48 changes: 45 additions & 3 deletions play-json/shared/src/main/scala/play/api/libs/json/Json.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

package play.api.libs.json

import scala.collection.mutable.{ Builder => MBuilder }

import java.io.InputStream

/**
Expand Down Expand Up @@ -226,12 +228,52 @@ object Json extends JsonFacade with JsMacros with JsValueMacros {
implicit def toJsFieldJsValueWrapper[T](field: T)(implicit w: Writes[T]): JsValueWrapper =
JsValueWrapperImpl(w.writes(field))

def obj(fields: (String, JsValueWrapper)*): JsObject = JsObject(fields.map(f => (f._1, unwrap(f._2))))
def obj(fields: (String, JsValueWrapper)*): JsObject = JsObject(fields.map { case (name, wrapped) =>
name -> unwrap(wrapped)
})

/**
* Returns a JSON object builder.
*
* {{{
* import play.api.libs.json.{ Json, JsObject }
*
* // Create a new builder
* val builder: JsObjectBuilder = JsObject.newBuilder
*
* // Add key-value pairs to the builder
* builder += ("name" -> "John Doe")
* builder += ("age" -> 25)
*
* // Clear the builder
* builder.clear()
*
* // Add more key-value pairs
* builder += ("email" -> "john.doe@example.com")
* builder += ("address" -> "123 Street")
*
* // Build the final JsObject
* val result: JsObject = builder.result()
*
* // Print the resulting JsObject
* println(result)
* }}}
*
* This will output:
* {{{
* {"email":"john.doe@example.com","address":"123 Street"}
* }}}
*/
def newBuilder: MBuilder[(String, Json.JsValueWrapper), JsObject] =
new JsObjectBuilder()

def arr(items: JsValueWrapper*): JsArray = JsArray(items.iterator.map(unwrap).toArray[JsValue])

// Passed nulls will typecheck without needing the implicit conversion, so they need to checked at runtime
private def unwrap(wrapper: JsValueWrapper) = wrapper match {
/*
* Passed nulls will typecheck without needing the implicit conversion,
* so they need to checked at runtime.
*/
private[json] def unwrap(wrapper: JsValueWrapper) = wrapper match {
case null => JsNull
case JsValueWrapperImpl(value) => value
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,22 @@ class JsonSharedSpec extends AnyWordSpec with Matchers with org.scalatestplus.sc
"js.toJsObject(peach)(writes)".mustNot(typeCheck)
}

"create object using builder" in {
val builder = Json.newBuilder

builder += ("name" -> "John Doe")
builder += ("age" -> 25)

val result: JsObject = builder.result()

result.mustEqual(
Json.obj(
"name" -> "John Doe",
"age" -> 25
)
)
}

"convert to a byte array containing the UTF-8 representation" in json { js =>
val json = js.parse("""
|{
Expand Down

0 comments on commit 72070d9

Please sign in to comment.