All Downloads are FREE. Search and download functionalities are using the official Maven repository.

play.api.libs.json.Json.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc. 
 */

package play.api.libs.json

import java.io.InputStream

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

import scala.util.Try

/**
 * @define jsonParam @param json the JsValue to convert
 * @define parseDescription Parses the input JSON, and returns it as a [[JsValue]]
 * @define stringRepr String with the json representation
 */
sealed trait JsonFacade {

  /**
   * $parseDescription (use `tryParse` to be safe).
   *
   * @param input the $stringRepr
   */
  def parse(input: String): JsValue

  /**
   * $parseDescription.
   * @param input the $stringRepr
   */
  def tryParse(input: String): Try[JsValue]

  /**
   * $parseDescription (use `tryParse` to be safe).
   *
   * @param input the InputStream to parse
   */
  def parse(input: InputStream): JsValue

  /**
   * $parseDescription.
   */
  def tryParse(input: InputStream): Try[JsValue]

  /**
   * $parseDescription (use `tryParse` to be safe).
   *
   * The character encoding used will be automatically detected as UTF-8,
   * UTF-16 or UTF-32, as per the heuristics in RFC-4627.
   *
   * @param input the byte array to parse
   */
  def parse(input: Array[Byte]): JsValue

  /**
   * $parseDescription.
   */
  def tryParse(input: Array[Byte]): Try[JsValue]

  /**
   * Converts a [[JsValue]] to its string representation.
   *
   * {{{
   * import play.api.libs.json.Json
   *
   * val input = Json.obj(
   *   "field1" -> Json.obj(
   *     "field11" -> "value11",
   *     "field12" -> Json.arr("alpha", 123L)
   *   )
   * )
   *
   * Json.stringify(input)
   * // => {"field1":{"field11":"value11","field12":["alpha",123]}}
   * }}}
   *
   * $jsonParam
   * @return a String with the json representation
   */
  def stringify(json: JsValue): String

  /**
   * Converts a [[JsValue]] to bytes (using UTF-8 encoding).
   *
   * $jsonParam
   * @return an `Array[Byte]` representing the UTF-8-encoded JSON
   */
  def toBytes(json: JsValue): Array[Byte]

  /**
   * Converts a [[JsValue]] to its string representation,
   * escaping all non-ascii characters using `\u005CuXXXX` syntax.
   *
   * This is particularly useful when the output JSON will be executed as javascript, since JSON is not a strict
   * subset of javascript
   * (see JSON: The JavaScript subset that isn't).
   *
   * {{{
   * import play.api.libs.json.{ Json, JsString }
   *
   * Json.asciiStringify(JsString("some\\u005Ctext\\u005C"))
   * // => "some\\u005Ctext\\u005C"
   *
   * Json.stringify(JsString("some\\u005Ctext\\u005C"))
   * // => "sometext"
   * }}}
   *
   * $jsonParam
   * @return A $stringRepr with all non-ascii characters escaped.
   */
  def asciiStringify(json: JsValue): String

  /**
   * Converts a [[JsValue]] to its pretty string representation using default
   * pretty printer (line feeds after each fields and 2-spaces indentation).
   *
   * {{{
   * import play.api.libs.json.Json
   *
   * val res0 = Json.obj(
   *   "field1" -> Json.obj(
   *     "field11" -> "value11",
   *     "field12" -> Json.arr("alpha", 123L)
   *   )
   * )
   * // => {"field1":{"field11":"value11","field12":["alpha",123]}}
   *
   * Json.prettyPrint(res0)
   * // =>
   * // {
   * //   "field1" : {
   * //     "field11" : "value11",
   * //     "field12" : [ "alpha", 123 ]
   * //   }
   * // }
   * }}}
   *
   * $jsonParam
   * @return A $stringRepr.
   */
  def prettyPrint(json: JsValue): String

  /**
   * Converts any writeable value to a [[JsValue]].
   *
   * A value is writeable if a [[Writes]] implicit is available for its type.
   *
   * @tparam T the type of the value to be written as JSON
   * @param o the value to convert as JSON
   */
  def toJson[T](o: T)(implicit tjs: Writes[T]): JsValue

  /**
   * Converts any object writeable value to a [[JsObject]].
   *
   * A value is writeable as an object,
   * if a [[OWrites]] implicit is available for its type.
   *
   * @tparam T the type of the value to be written as `JsObject`
   * @param o the value to convert as JSON object
   */
  def toJsObject[T](o: T)(implicit tjs: OWrites[T]): JsObject

  /**
   * Converts a [[JsValue]] to a value of requested type `T`.
   *
   * @tparam T The type of conversion result,
   * only supported if a [[Reads]] implicit is available for.
   *
   * $jsonParam
   */
  def fromJson[T](json: JsValue)(implicit fjs: Reads[T]): JsResult[T]

  /**
   * Returns a [[JsObject]] with given fields.
   *
   * @param fields the object fields specified as pairs of name and value
   */
  def obj(fields: (String, Json.JsValueWrapper)*): JsObject

  /** Returns a [[JsArray]] with given items. */
  def arr(items: Json.JsValueWrapper*): JsArray
}

/**
 * Helper functions to handle JsValues.
 *
 * @define macroOptions @tparam Opts the compile-time options
 * @define macroTypeParam @tparam A the type for which the handler must be materialized
 * @define macroWarning If any missing implicit is discovered, compiler will break with corresponding error.
 */
object Json extends JsonFacade with JsMacros with JsValueMacros {
  def parse(input: String): JsValue = StaticBinding.parseJsValue(input)

  def tryParse(input: String): Try[JsValue] = Try(StaticBinding.parseJsValue(input))

  def parse(input: InputStream): JsValue = StaticBinding.parseJsValue(input)

  def tryParse(input: InputStream): Try[JsValue] = Try(StaticBinding.parseJsValue(input))

  def parse(input: Array[Byte]): JsValue = StaticBinding.parseJsValue(input)

  def tryParse(input: Array[Byte]): Try[JsValue] = Try(StaticBinding.parseJsValue(input))

  def stringify(json: JsValue): String =
    StaticBinding.generateFromJsValue(json, false)

  def toBytes(json: JsValue): Array[Byte] = StaticBinding.toBytes(json)

  // We use unicode \u005C for a backlash in comments, because Scala will replace unicode escapes during lexing
  // anywhere in the program.
  def asciiStringify(json: JsValue): String =
    StaticBinding.generateFromJsValue(json, true)

  def prettyPrint(json: JsValue): String = StaticBinding.prettyPrint(json)

  def toJson[T](o: T)(implicit tjs: Writes[T]): JsValue = tjs.writes(o)

  def toJsObject[T](o: T)(implicit
      tjs: OWrites[T]
  ): JsObject = tjs.writes(o)

  def fromJson[T](json: JsValue)(implicit fjs: Reads[T]): JsResult[T] = fjs.reads(json)

  /**
   * Next is the trait that allows Simplified Json syntax :
   *
   * Example:
   *
   * {{{
   * import play.api.libs.json._
   *
   * JsObject(Seq(
   *    "key1" -> JsString("value"),
   *    "key2" -> JsNumber(123),
   *    "key3" -> JsObject(Seq("key31" -> JsString("value31")))
   * )) == Json.obj(
   *   "key1" -> "value", "key2" -> 123, "key3" -> Json.obj("key31" -> "value31"))
   *
   * JsArray(Seq(JsString("value"), JsNumber(123), JsBoolean(true))) == Json.arr("value", 123, true)
   * }}}
   *
   * There is an implicit conversion from any Type with a Json Writes to JsValueWrapper
   * which is an empty trait that shouldn't end into unexpected implicit conversions.
   */
  sealed trait JsValueWrapper

  private case class JsValueWrapperImpl(field: JsValue) extends JsValueWrapper

  import scala.language.implicitConversions

  implicit def toJsFieldJsValueWrapper[T](field: T)(implicit w: Writes[T]): JsValueWrapper =
    JsValueWrapperImpl(w.writes(field))

  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" -> "[email protected]")
   *   builder += ("address" -> "123 Street")
   *
   *   // Build the final JsObject
   *   val result: JsObject = builder.result()
   *
   *   // Print the resulting JsObject
   *   println(result)
   * }}}
   *
   * This will output:
   * {{{
   *   {"email":"[email protected]","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[json] def unwrap(wrapper: JsValueWrapper) = wrapper match {
    case null                      => JsNull
    case JsValueWrapperImpl(value) => value
  }

  /**
   * Creates a `Format[E]` by automatically creating Reads[E] and Writes[E] for any Enumeration E
   *
   * {{{
   * import play.api.libs.json.{ Format, Json }
   *
   * object DayOfWeek extends Enumeration {
   *
   *  type DayOfWeek = Value
   *
   *  val Mon = Value("Monday")
   *  val Tue = Value("Tuesday")
   *  val Wed = Value("Wednesday")
   *  // etc.
   *
   *   implicit val format1: Format[DayOfWeek] = Json.formatEnum(DayOfWeek)
   *   // or 'this' if defining directly in Enum
   *   implicit val format2: Format[DayOfWeek] = Json.formatEnum(this)
   * }
   * }}}
   *
   * `Json.toJson(Mon)` will produce `"Monday"`.
   *
   * @param enum Enumeration object
   * @tparam E type of Enum
   */
  def formatEnum[E <: Enumeration](`enum`: E): Format[`enum`.Value] =
    Format(Reads.enumNameReads(`enum`), Writes.enumNameWrites[`enum`.type])

  /**
   * JSON facade with some macro options.
   *
   * $macroOptions
   *
   * @define macroWarning If any missing implicit is discovered, compiler will break with corresponding error.
   * @define macroTypeParam @tparam A the type for which the handler must be materialized
   */
  final class WithOptions[Opts <: MacroOptions](val config: JsonConfiguration.Aux[Opts])
      extends JsonFacade
      with JsMacrosWithOptions[Opts] {

    def this() = this(JsonConfiguration.default)

    @inline def parse(input: String): JsValue = Json.parse(input)

    @inline def tryParse(input: String): Try[JsValue] = Json.tryParse(input)

    @inline def parse(input: InputStream): JsValue = Json.parse(input)

    @inline def tryParse(input: InputStream): Try[JsValue] = Json.tryParse(input)

    @inline def parse(input: Array[Byte]): JsValue = Json.parse(input)

    @inline def tryParse(input: Array[Byte]): Try[JsValue] = Json.tryParse(input)

    @inline def stringify(json: JsValue): String = Json.stringify(json)

    @inline def toBytes(json: JsValue): Array[Byte] = Json.toBytes(json)

    @inline def asciiStringify(json: JsValue): String =
      Json.asciiStringify(json)

    @inline def prettyPrint(json: JsValue): String = Json.prettyPrint(json)

    @inline def toJson[T](o: T)(implicit tjs: Writes[T]): JsValue =
      Json.toJson[T](o)

    @inline def toJsObject[T](o: T)(implicit tjs: OWrites[T]): JsObject =
      Json.toJsObject[T](o)

    @inline def fromJson[T](json: JsValue)(implicit fjs: Reads[T]): JsResult[T] = Json.fromJson[T](json)

    @inline def obj(fields: (String, JsValueWrapper)*): JsObject = Json.obj(fields: _*)

    @inline def arr(items: JsValueWrapper*): JsArray = Json.arr(items: _*)
  }

  /**
   * Returns a [[JsonFacade]] using the current JSON configuration.
   *
   * @tparam C the type of compile-time configuration
   *
   * {{{
   * import play.api.libs.json.{ Json, Reads }
   *
   * case class Foo(v: String)
   *
   * // Materializes a `Reads[Foo]`,
   * // with the configuration resolved at compile time
   * val r: Reads[Foo] = Json.configured.reads[Foo]
   * }}}
   */
  def configured[Opts <: MacroOptions](implicit config: JsonConfiguration.Aux[Opts]) = new WithOptions[Opts](config)

  /**
   * Returns an inference context to call the JSON macros,
   * using explicit compile-time options.
   *
   * $macroOptions
   */
  def using[Opts <: MacroOptions] = new WithOptions[Opts](JsonConfiguration[Opts]())

  /**
   * Compile-time base options for macro usage.
   *
   * {{{
   * import play.api.libs.json.Json
   *
   * case class Foo(v: String)
   *
   * Json.using[Json.MacroOptions].format[Foo]
   * // equivalent to Json.format[Foo]
   * }}}
   */
  sealed trait MacroOptions

  object MacroOptions {

    /**
     * Defines the default macro options if no type is supplied.
     *
     * Since you can't have defaults for a type parameter (unless it's a contravariant type, it will default to
     * Nothing), we supply the default via an implicit parameter.
     */
    trait Default[O <: Json.MacroOptions]

    trait LowPriorityDefaultImplicits {

      /**
       * Low priority implicit used when some explicit Json.MacroOptions instance is passed.
       */
      implicit def lowPriorityDefault[O <: Json.MacroOptions]: Default[O] = new Default[O] {}
    }

    object Default extends LowPriorityDefaultImplicits {

      /**
       * This will be the default that's passed when no MacroOptions is passed.
       */
      implicit object macroOptionsDefault extends Default[Json.MacroOptions]
    }
  }

  /**
   * Flag to indicate the macros can use the type default values
   * (e.g. default values for the case class parameters)
   * when applicable.
   *
   * {{{
   * import play.api.libs.json._, Json._
   *
   * type Opts = MacroOptions with DefaultValues
   * }}}
   */
  trait DefaultValues { self: MacroOptions => }

  /**
   * Alias for `MacroOptions with DefaultValues`
   *
   * {{{
   * import play.api.libs.json.Json
   *
   * Json.using[Json.WithDefaultValues]
   * }}}
   */
  type WithDefaultValues = MacroOptions with DefaultValues
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy