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

play.api.libs.json.EnvWrites.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.time.format.DateTimeFormatter
import java.time.temporal.Temporal
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.OffsetDateTime
import java.time.Period
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.ZonedDateTime
import java.time.{ Duration => JDuration }
import java.util.Locale

import com.fasterxml.jackson.databind.JsonNode
import play.api.libs.json.jackson.JacksonJson

trait EnvWrites {
  import scala.language.implicitConversions

  @deprecated("Use `jsonNodeWrites`", "2.8.0")
  object JsonNodeWrites extends Writes[JsonNode] {
    def writes(o: JsonNode): JsValue = JacksonJson.get.jsonNodeToJsValue(o)
  }

  /**
   * Serializer for Jackson JsonNode
   */
  implicit def jsonNodeWrites[T <: JsonNode]: Writes[T] =
    Writes[T](JacksonJson.get.jsonNodeToJsValue)

  /** Typeclass to implement way of formatting of Java8 temporal types. */
  trait TemporalFormatter[T <: Temporal] {
    def format(temporal: T): String
  }

  /** Formatting companion */
  object TemporalFormatter {
    implicit def DefaultLocalDateTimeFormatter(formatter: DateTimeFormatter): TemporalFormatter[LocalDateTime] =
      new TemporalFormatter[LocalDateTime] {
        def format(temporal: LocalDateTime): String = formatter.format(temporal)
      }

    implicit def PatternLocalDateTimeFormatter(pattern: String): TemporalFormatter[LocalDateTime] =
      DefaultLocalDateTimeFormatter(DateTimeFormatter.ofPattern(pattern))

    implicit def DefaultOffsetDateTimeFormatter(formatter: DateTimeFormatter): TemporalFormatter[OffsetDateTime] =
      new TemporalFormatter[OffsetDateTime] {
        def format(temporal: OffsetDateTime): String = formatter.format(temporal)
      }

    implicit def PatternOffsetDateTimeFormatter(pattern: String): TemporalFormatter[OffsetDateTime] =
      DefaultOffsetDateTimeFormatter(DateTimeFormatter.ofPattern(pattern))

    implicit def DefaultZonedDateTimeFormatter(formatter: DateTimeFormatter): TemporalFormatter[ZonedDateTime] =
      new TemporalFormatter[ZonedDateTime] {
        def format(temporal: ZonedDateTime): String = formatter.format(temporal)
      }

    implicit def PatternZonedDateTimeFormatter(pattern: String): TemporalFormatter[ZonedDateTime] =
      DefaultZonedDateTimeFormatter(DateTimeFormatter.ofPattern(pattern))

    implicit def DefaultDateFormatter(formatter: DateTimeFormatter): TemporalFormatter[LocalDate] =
      new TemporalFormatter[LocalDate] {
        def format(temporal: LocalDate): String = formatter.format(temporal)
      }

    implicit def PatternDateFormatter(pattern: String): TemporalFormatter[LocalDate] =
      DefaultDateFormatter(DateTimeFormatter.ofPattern(pattern))

    implicit def DefaultInstantFormatter(formatter: DateTimeFormatter): TemporalFormatter[Instant] =
      new TemporalFormatter[Instant] {
        def format(temporal: Instant): String = formatter.format(temporal)
      }

    implicit def PatternInstantFormatter(pattern: String): TemporalFormatter[Instant] =
      DefaultInstantFormatter(DateTimeFormatter.ofPattern(pattern).withZone(ZoneOffset.UTC))

    implicit def DefaultLocalTimeFormatter(formatter: DateTimeFormatter): TemporalFormatter[LocalTime] =
      new TemporalFormatter[LocalTime] {
        def format(temporal: LocalTime): String = formatter.format(temporal)
      }

    implicit def PatternLocalTimeFormatter(pattern: String): TemporalFormatter[LocalTime] =
      DefaultLocalTimeFormatter(DateTimeFormatter.ofPattern(pattern).withZone(ZoneOffset.UTC))
  }

  /**
   * Serializer for Java8 temporal types (e.g. `java.time.LocalDateTime`)
   * to be written as JSON string.
   *
   * @tparam A the Java8 temporal type to be considered: LocalDateTime, ZonedDateTime, Instant
   * @tparam B Type of formatting argument
   *
   * @param formatting an argument to instantiate formatter
   *
   * {{{
   * import java.time.LocalDateTime
   * import java.time.format.DateTimeFormatter
   *
   * import play.api.libs.json.Writes
   *
   * implicit val w: Writes[LocalDateTime] =
   *   Writes.temporalWrites[LocalDateTime, DateTimeFormatter](
   *     DateTimeFormatter.ISO_LOCAL_DATE_TIME)
   * }}}
   */
  def temporalWrites[A <: Temporal, B](formatting: B)(implicit f: B => TemporalFormatter[A]): Writes[A] =
    new Writes[A] {
      def writes(temporal: A): JsValue = JsString(f(formatting).format(temporal))
    }

  /**
   * The default typeclass to write a `java.time.LocalDateTime`,
   * using '2011-12-03T10:15:30' format.
   */
  implicit val DefaultLocalDateTimeWrites: Writes[LocalDateTime] =
    temporalWrites[LocalDateTime, DateTimeFormatter](
      DateTimeFormatter.ISO_LOCAL_DATE_TIME
    )

  /**
   * The default typeclass to write a `java.time.OffsetDateTime`,
   * using '2011-12-03T10:15:30+02:00' format.
   */
  implicit val DefaultOffsetDateTimeWrites: Writes[OffsetDateTime] =
    temporalWrites[OffsetDateTime, DateTimeFormatter](
      DateTimeFormatter.ISO_OFFSET_DATE_TIME
    )

  /**
   * The default typeclass to write a `java.time.ZonedDateTime`,
   * using '2011-12-03T10:15:30+01:00[Europe/Paris]' format.
   */
  implicit val DefaultZonedDateTimeWrites: Writes[ZonedDateTime] =
    temporalWrites[ZonedDateTime, DateTimeFormatter](
      DateTimeFormatter.ISO_ZONED_DATE_TIME
    )

  /**
   * The default typeclass to write a `java.time.LocalDate`,
   * using '2011-12-03' format.
   */
  implicit val DefaultLocalDateWrites: Writes[LocalDate] =
    temporalWrites[LocalDate, DateTimeFormatter](
      DateTimeFormatter.ISO_LOCAL_DATE
    )

  /**
   * The default typeclass to write a `java.time.Instant`,
   * using '2011-12-03T10:15:30Z' format.
   */
  implicit val DefaultInstantWrites: Writes[Instant] =
    Writes[Instant] { i =>
      JsString(i.toString)
    }

  /**
   * The default typeclass to write a `java.time.LocalTime`,
   * using '10:15:30' format.
   */
  implicit val DefaultLocalTimeWrites: Writes[LocalTime] =
    temporalWrites[LocalTime, DateTimeFormatter](DateTimeFormatter.ISO_TIME)

  /**
   * Serializer for `java.time.LocalTime` as JSON number.
   * The nano of day is written.
   *
   * {{{
   * import java.time.LocalTime
   * import play.api.libs.json.Writes
   *
   * implicit val ltnWrites = Writes.LocalTimeNanoOfDayWrites
   * }}}
   */
  val LocalTimeNanoOfDayWrites: Writes[LocalTime] = Writes[LocalTime] { t =>
    JsNumber(BigDecimal.valueOf(t.toNanoOfDay))
  }

  /**
   * Serializer for `java.time.ZoneId` as JSON string.
   */
  implicit val ZoneIdWrites: Writes[ZoneId] =
    Writes[ZoneId](zone => JsString(zone.getId))

  /**
   * Serializer for `java.time.LocalDateTime` as JSON number.
   * The number of milliseconds since epoch is used.
   *
   * {{{
   * import java.time.LocalDateTime
   * import play.api.libs.json.Writes
   *
   * implicit val ldtnWrites = Writes.LocalDateTimeEpochMilliWrites
   * }}}
   */
  val LocalDateTimeEpochMilliWrites: Writes[LocalDateTime] =
    Writes[LocalDateTime] { t =>
      JsNumber(
        BigDecimal.valueOf(
          t.toInstant(ZoneOffset.UTC).toEpochMilli
        )
      )
    }

  /**
   * Serializer for `java.time.ZonedDateTime` as JSON number.
   * The number of milliseconds since epoch is used.
   *
   * {{{
   * import java.time.ZonedDateTime
   * import play.api.libs.json.Writes
   *
   * implicit val zdtnWrites = Writes.ZonedDateTimeEpochMilliWrites
   * }}}
   */
  val ZonedDateTimeEpochMilliWrites: Writes[ZonedDateTime] =
    new Writes[ZonedDateTime] {
      def writes(t: ZonedDateTime): JsValue =
        JsNumber(BigDecimal.valueOf(t.toInstant.toEpochMilli))
    }

  /**
   * Serializer for `java.time.LocalDate` as JSON number.
   * The number of milliseconds since epoch is used.
   *
   * {{{
   * import java.time.LocalDate
   * import play.api.libs.json.Writes
   *
   * implicit val ldnWrites: Writes[LocalDate] =
   *   Writes.LocalDateEpochMilliWrites
   * }}}
   */
  val LocalDateEpochMilliWrites: Writes[LocalDate] = Writes[LocalDate] { t =>
    JsNumber(
      BigDecimal.valueOf(
        t.atStartOfDay.toInstant(ZoneOffset.UTC).toEpochMilli
      )
    )
  }

  /**
   * Serializer for `java.time.Instant` as JSON number.
   * The number of milliseconds since epoch is used.
   *
   * {{{
   * import java.time.Instant
   * import play.api.libs.json.Writes
   *
   * implicit val inWrites: Writes[Instant] = Writes.InstantEpochMilliWrites
   * }}}
   */
  val InstantEpochMilliWrites: Writes[Instant] = new Writes[Instant] {
    def writes(t: Instant): JsValue =
      JsNumber(BigDecimal.valueOf(t.toEpochMilli))
  }

  /** Serializer for a `Locale` using the IETF BCP 47 string representation */
  implicit val localeWrites: Writes[Locale] =
    Writes[Locale] { l =>
      JsString(KeyWrites.LanguageTagWrites.writeKey(l))
    }

  /** Serializer for a `Locale` using a object representation */
  val localeObjectWrites: OWrites[Locale] = {
    import scala.collection.JavaConverters.asScalaSetConverter

    OWrites[Locale] { l =>
      val fields = Map.newBuilder[String, JsValue]

      fields += "language" -> Json.toJson(l.getLanguage)

      Option(l.getCountry).filter(_.nonEmpty).foreach { country =>
        fields += "country" -> Json.toJson(country)
      }

      Option(l.getVariant).filter(_.nonEmpty).foreach { variant =>
        fields += "variant" -> Json.toJson(variant)
      }

      Option(l.getScript).filter(_.nonEmpty).foreach { script =>
        fields += "script" -> Json.toJson(script)
      }

      val attrs = l.getUnicodeLocaleAttributes.asScala
      if (attrs.nonEmpty) {
        fields += "attributes" -> Json.toJson(attrs.toSet)
      }

      val keywords = l.getUnicodeLocaleKeys.asScala
      if (keywords.nonEmpty) {
        fields += "keywords" -> Json.toJson {
          val ks = Map.newBuilder[String, String]

          keywords.foreach { key =>
            Option(l.getUnicodeLocaleType(key)).foreach { typ =>
              ks += (key -> typ)
            }
          }

          ks.result()
        }
      }

      val extension = l.getExtensionKeys.asScala
      if (extension.nonEmpty) {
        fields += "extension" -> Json.toJson {
          val ext = Map.newBuilder[String, String]

          extension.foreach { key =>
            Option(l.getExtension(key)).foreach { v =>
              ext += (key.toString -> v)
            }
          }

          ext.result()
        }
      }

      JsObject(fields.result())
    }
  }

  /** Serializer of Java Duration as a number of milliseconds. */
  val javaDurationMillisWrites: Writes[JDuration] =
    Writes[JDuration] { d =>
      JsNumber(d.toMillis)
    }

  /**
   * Serializer of Java Duration using ISO representation
   * (e.g. PT1S for 1 second).
   */
  implicit val javaDurationWrites: Writes[JDuration] =
    Writes[JDuration] { d =>
      JsString(d.toString)
    }

  /**
   * Serializer of Java Period using ISO representation
   * (e.g. P2D for 2 days).
   */
  implicit val javaPeriodWrites: Writes[Period] =
    Writes[Period] { d =>
      JsString(d.toString)
    }
}

trait EnvKeyWrites {
  implicit object LanguageTagWrites extends KeyWrites[Locale] {
    def writeKey(locale: Locale): String = locale.toLanguageTag
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy