Json为scala中的自定义类写

时间:2017-09-21 21:05:32

标签: json scala playframework

我有一些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?

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

2 个答案:

答案 0 :(得分:0)

我想出了类似的东西:

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())

可能会简化,但是在这里深夜;)它可以满足你的需要:)

答案 1 :(得分:0)

该信息在概念上具有树结构。我的建议是用一个普通的case类威胁它,简化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来处理Tree中对sub的递归引用。

为了模拟你发布的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)