【问题标题】:Json Writes for custom class in scalaJson 为 scala 中的自定义类编写
【发布时间】:2017-09-21 21:05:32
【问题描述】:

我有一些字符串的Seq,主要如下所示:

val states = Seq(
  "CA" -> Seq("Los Angeles" -> Seq("SunsetBlvd", "Hollywood" -> Seq("W 8th St", "W 9th St")), "Pasadena"),
  "WA" -> Seq("Seattle", "Redmond")
)

这个案例类可以是

case class State(name: String, sub: Option[Seq[State]])

隐式写入

implicit val stateWrites = Json.Writes[State]

希望将其转换为 Json 之类的

[
  {
    "name": "CA",
    "sub": [
        {
         "name": "Los Angeles",
         "sub": [
         {
           "name": "SunsetBlvd"
          },
          {
            "name": "Hollywood",
            "sub": [
              {
                "name": "W 8th St"
              },
              {
                "name": "W 9th St"
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "name": "WA",
    "sub": [
      {
        "name": "Seattle"
      },
      {
       "name": "Redmond"
      }
    ]
  }
]

如何正确建模数据并能够使用 Writes 将此 Seq 转换为 Json?

或者甚至将states val 更改为适当的格式,以便我可以轻松地将其转换为 Json?

在案例类中,其中一个字段的类型本身是错误的。在建模数据甚至Seq 时如何避免这种情况?

【问题讨论】:

  • 你的 json 应该是什么样的?
  • 类似[{"name" : "CA", "sub" : [{"name" : "Los Angeles", "sub":[{"name" : "SunsetBlvd"}, {"name" : "Hollywood", "sub": [{"name":"W 8th St"}, {"name": "W 9th St"}]}]}

标签: json scala playframework


【解决方案1】:

我想出了这样的东西:

case class State(name: String, sub: Option[Seq[State]])

import play.api.libs.json._


implicit val optWrites = new Writes[Option[Seq[State]]] {
  override def writes(o: Option[Seq[State]]) = {
    if (o.isDefined) {
      Json.toJson(o.get)(stateSeqWrites)
    } else {
      JsNull
    }
  }
}

implicit val stateWrites = new Writes[State] {
  def writes(state: State) = {
    val l: Seq[(String, JsValueWrapper)] = Seq("name" -> JsString(state.name))
    val ll: Seq[(String, JsValueWrapper)] = if (state.sub.isDefined) {
      val subValue: JsValueWrapper = Json.toJson(state.sub)(optWrites)
      l :+ ("sub" -> subValue)
    } else {
      l
    }

    Json.obj(ll : _*)
  }
}

implicit val stateSeqWrites: Writes[Seq[State]] = new Writes[Seq[State]] {
  override def writes(s: Seq[State]) = {
    JsArray(s.map(Json.toJson(_)(stateWrites)))
  }
}

val states = Seq(
  State("CA", Some(Seq(State("Los Angeles", Some(Seq(State("SunsetBlvd", None), State("Hollywood", Some(Seq(State("W 8th St", None), State("W 9th St", None)))), State("Pasadena", None))))))),
  State("WA", Some(Seq(State("Seattle", None), State("Redmond", None))))
)

val json = Json.toJson(states)

println(json.toString())

可能会被简化,但这里已经很晚了 ;) 它可以满足您的需要 :)

【讨论】:

    【解决方案2】:

    该信息在概念上具有树结构。我的建议是只用一个普通的案例类来威胁它,简化 json 格式化程序并拥有更多的语义结构:

    case class Tree(name: String, sub: Option[List[Tree]])
    

    您的格式化程序将是这样的:

    implicit val repositoryFormat: Format[Tree] = (
        (__ \ 'name).format[String] ~
          (__ \ 'sub).lazyFormatNullable(implicitly[ Format[ List[Tree] ]])
        )(Tree.apply, unlift(Tree.unapply))
    

    注意我使用lazyFormatNullable 来处理sub 中对Tree 的递归引用。

    为了模拟您发布的 Json,我将其转换为 Tree 案例类结构。

    // The tree leaves
      val hollywoodLeaves = Some( Tree("W 8th St", None) :: Tree("W 9th St", None) :: Nil )
      val losAngelesLeaves = Some( Tree("SunsetBlvd", None) :: Tree("Hollywood", hollywoodLeaves ) :: Nil  )
    
      // The two trees in your array
      val firstTree = Tree( "CA", Some( Tree("Los Angeles", losAngelesLeaves) :: Nil ) )
      val secondTree = Tree("WA", Some( Tree("Seattle", None) :: Tree("Redmond", None) :: Nil ))
    
      // Your root array
      val treeArray = firstTree :: secondTree :: Nil
    
      // Conversion to json
      val json = Json.toJson(treeArray)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多