【问题标题】:Scala object to Json Formatter using combinators使用组合器的 Scala 对象到 Json Formatter
【发布时间】:2013-05-07 10:13:55
【问题描述】:

我已经按照 Scala documentation 实现了一个类

case class Creature(
  name: String, 
  isDead: Boolean, 
  weight: Float,
  dob: java.sql.Date
)

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

implicit val creatureFormat = (
  (__ \ "name").format[String] and
  (__ \ "isDead").format[Boolean] and
  (__ \ "weight").format[Float] and
  (__ \ "dob").format[java.sql.Date]
)(Creature.apply, unlift(Creature.unapply))

然后我像这样Json.toJson(Creature("John Doe", false, 100.0, new java.sql.Date(1363456800000))) 调用 json 包装器并期望看到类似 {"name": "John Doe", "isDead": false, "weight": 100.0, "dob": “2013-03-17”}。相反,我得到的输出类似于 {"name": "John Doe", "isDead": false, "weight": 100.0, "dob": 1363456800000} .

请注意,在数据库中,我可以看到出生日期为 2013-03-17。

【问题讨论】:

    标签: json scala playframework playframework-2.1


    【解决方案1】:

    默认情况下,java.util.Date Json serializer 会生成一个包含日期时间戳的数字。

    或者,您可以使用日期序列化程序来生成包含日期表示的字符串。但是,由于 JSON 中没有标准的日期表示,因此您必须明确提供用于生成文本表示的模式:

    implicit val creatureFormat = (
      (__ \ "name").format[String] and
      (__ \ "isDead").format[Boolean] and
      (__ \ "weight").format[Float] and
      (__ \ "dob").format(sqlDateWrites("YYYY-MM-DD"))(sqlDateReads("YYYY-MM-DD"))
    )(Creature.apply, unlift(Creature.unapply))
    

    【讨论】:

    • 非常感谢您的洞察力。这种方法的问题是,如果我的模型/类中有多个 SQL 日期字段,我将需要为所有日期字段实现格式化程序。事实上,在大多数实际项目中,都会有许多具有许多日期字段的模型/类。我不是一个自以为是的人,但我仍然认为我需要定义一次 SQL 日期格式化程序,以便任何模型都可以使用该序列化程序。
    • 您可以定义一次:val sqlDateFormat = Format(sqlDateReads("YYYY-MM-DD"), sqlDateWrites("YYYY-MM-DD"))。然后(__ \ "dob").format(sqlDateFormat)。您甚至可以将sqlDateFormat 声明为隐式值并将其导入到您的其他格式定义中,因此如果您编写( (__ \ "dob").format[java.sql.Date],它将自动使用它
    【解决方案2】:

    这是我解决它的方法(我明确定义了 apply 和 unapply 方法)

    val sdf = new java.text.SimpleDateFormat("yyyy-MM-dd")
    implicit val creatureFormat = (
      (__ \ "name").format[String] and
      (__ \ "isDead").format[Boolean] and
      (__ \ "weight").format[Float] and
      (__ \ "dob").format[String])
        (((name, isDead, weight, dob) => Creature(name, isDead, weight, new java.sql.Date(sdf.parse(dob).getTime()))),
        unlift((cr: Creature) => Some(cr.name, cr.isDead, cr.weight, sdf.format(cr.dob))))
    

    不知道有没有更好的办法。

    更新

    最后,我为 java.sql.Date 实现了一个格式化程序

    import play.api.libs.json._
    import play.api.libs.functional.syntax._
    import play.api.data.validation.ValidationError
    import play.api.libs.json.{ Json => PlayJson, _ }
    
    case class Creature(
      name: String, 
      isDead: Boolean, 
      weight: Float,
      dob: java.sql.Date
    )
    
    implicit val sqlDateWrite = new Format[SqlDate] {
      def reads(json: JsValue) = json match {
        case JsString(d) => {
          val theDate = new SqlDate(sdf.parse(d).getTime)
          if (d.matches(sdfPattern) && theDate.compareTo(new Date(0)) > 0) JsSuccess(new SqlDate(sdf.parse(d).getTime))
          else JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.date.in.format(dd-MM-yyyy)"))))
        }
        case _ => JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.date.in.String"))))
      }
    
      def writes(sd: SqlDate): JsValue = JsString(sdf.format(sd))
    }
    
    implicit val creatureFormat = PlayJson.format[Creature]
    

    现在,这两行都有效

    val mcJson = PlayJson.toJson(Creature("John Doe", false, 100, new SqlDate(1368430000000L)))
    val mcObj = PlayJson.fromJson[Creature](PlayJson.obj("name"-> "Abul Khan", "isDead"-> true, "weight"-> 115, "dob"-> "17-05-2011")).getOrElse(null)
    

    【讨论】:

    • 有一个更好的解决方案 :) 我会尽快发布。在简历中,定义你自己的 java.sql.Date writer
    • @JulienLafont 您有时间准备更好的解决方案吗?感谢期待。
    • 我忘记了日期模式val sdfPattern = "\\b(0[1-9]|[1-3][0-9])-(0[1-9]|1[0-2])-[1-9][0-9]{3}\\b" val dtFmt = new java.text.SimpleDateFormat("dd-MM-yyyy", java.util.Locale.ENGLISH)
    【解决方案3】:

    由于您需要字符串,因此您必须将所有内容都转换为字符串并丢失输入。

    Json.toJson(
      Creature(
        "John Doe", "false", "100.0",(new java.sql.Date(1363456800000)).toString
      )
    )
    

    【讨论】:

    • 您真的尝试过使用自己的代码吗?请注意我的 Creature 类及其属性的数据类型。我不能将每个参数都转换为字符串,那样我会丢失我的原始对象。无论如何感谢您的想法。
    • 道歉。您可以使用unapply 来执行此操作。由于数据类型定义了toString 方法。您可以实现unapply,在拆箱时将每个字段转换为字符串。
    猜你喜欢
    • 2015-02-06
    • 1970-01-01
    • 2016-04-20
    • 2017-01-17
    • 1970-01-01
    • 2014-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多