我有以下案例类和JSON组合器:
background.jpg
我的JSON读取case class Commit(
sha: String,
username: String,
message: String
)
object Commit {
implicit val format = Json.format[Commit]
}
case class Build(
projectName: String,
parentNumber: String,
commits: List[Commit]
)
val buildReads: Reads[Build] =
for {
projectName <- (__ \ "buildType" \ "projectName").read[String]
name <- (__ \ "buildType" \ "name").read[String]
parentNumber <- ((__ \ "artifact-dependencies" \ "build")(0) \ "number").read[String]
changes <- (__ \ "changes" \ "change").read[List[Map[String, String]]]
} yield {
val commits = for {
change <- changes
sha <- change.get("version")
username <- change.get("username")
comment <- change.get("comment")
} yield Commit(sha, username, comment)
Build(s"$projectName::$name", parentNumber, commits)
}
的组合器将处理传入的JSON,例如:
Build
但是,如果缺少{
"buildType": {
"projectName": "foo",
"name": "bar"
},
"artifact-dependencies": {
"build": [{
"number": "1"
}]
},
"changes": {
"change": [{
"verison": "1",
"username": "bob",
"comment": "foo"
}]
}
}
,则会失败。我希望这是可选的。
我应该使用artifact-dependencies
吗?我试图这样做,但这失败了,因为它是一个嵌套属性。
这看起来是否实用,或者我是否滥用JSON组合器将我的JSON解析为案例类?
答案 0 :(得分:5)
目前,其配套对象中的Format[Commit]
未被使用。我们没有理由不能使用简单的组合器,并将逻辑分开。
case class Commit(sha: String, username: String, message: String)
object Commit {
implicit val reads: Reads[Commit] = (
(__ \ "version").read[String] and
(__ \ "username").read[String] and
(__ \ "comment").read[String]
)(Commit.apply _)
}
然后,如果"artifact-dependencies"
可能丢失,我们应该在parentNumber
Option[String]
Build.
case class Build(projectName: String, parentNumber: Option[String], commits: List[Commit])
Reads
我将项目名称组合成一个单独的Reads[Build]
,以使val nameReads: Reads[String] = for {
projectName <- (__ \ "projectName").read[String]
name <- (__ \ "name").read[String]
} yield s"$projectName::$name"
看起来更干净。
"artifact-dependencies"
然后,对于缺少orElse
的时候,当整个分支(或子分支)不是Reads.pure(None)
时,我们可以使用None
和implicit val buildReads: Reads[Build] = (
(__ \ "buildType").read[String](nameReads) and
((__ \ "artifact-dependencies" \ "build")(0) \ "number").readNullable[String].orElse(Reads.pure(None)) and
(__ \ "changes" \ "change").read[List[Commit]]
)(Build.apply _)
val js2 = Json.parse("""
{
"buildType": {
"projectName": "foo",
"name": "bar"
},
"changes": {
"change": [{
"version": "1",
"username": "bob",
"comment": "foo"
}]
}
}
""")
scala> js2.validate[Build]
res6: play.api.libs.json.JsResult[Build] = JsSuccess(Build(foo::bar,None,List(Commit(1,bob,foo))),)
填充$scope.addRow = function() {
$scope.rows.push(angular.copy($scope.rows[0]));
}
那里。在这种情况下,这比映射每个步骤更简单。
str_replace
答案 1 :(得分:0)
我尽量让我的格式与json尽可能匹配。不可否认,在这种情况下,它有点尴尬,但那是因为json架构有点奇怪。鉴于这些限制,我将如何做到这一点:
import play.api.libs.functional.syntax._
import play.api.libs.json._
case class Build(buildType: BuildType, `artifact-dependencies`: Option[ArtifactDependencies], changes: Changes)
case class BuildType(projectName: String, name: String)
case class ArtifactDependencies(build: List[DependencyInfo])
case class DependencyInfo(number: String)
case class Changes(change: List[Commit])
case class Commit(version: String, username: String, comment: String)
object BuildType {
implicit val buildTypeReads: Reads[BuildType] = (
(JsPath \ "projectName").read[String] and
(JsPath \ "name").read[String]
)(BuildType.apply _)
}
object ArtifactDependencies {
implicit val artifactDependencyReads: Reads[ArtifactDependencies] =
(JsPath \ "build").read[List[DependencyInfo]].map(ArtifactDependencies.apply)
}
object DependencyInfo {
implicit val dependencyInfoReads: Reads[DependencyInfo] =
(JsPath \ "number").read[String].map(DependencyInfo.apply)
}
object Changes {
implicit val changesReads: Reads[Changes] =
(JsPath \ "change").read[List[Commit]].map(Changes.apply)
}
object Commit {
implicit val commitReads: Reads[Commit] = (
(JsPath \ "version").read[String] and
(JsPath \ "username").read[String] and
(JsPath \ "comment").read[String]
)(Commit.apply _)
}
object Build {
implicit val buildReads: Reads[Build] = (
(JsPath \ "buildType").read[BuildType] and
(JsPath \ "artifact-dependencies").readNullable[ArtifactDependencies] and
(JsPath \ "changes").read[Changes]
)(Build.apply _)
def test() = {
val js = Json.parse(
"""
|{
| "buildType": {
| "projectName": "foo",
| "name": "bar"
| },
| "changes": {
| "change": [{
| "version": "1",
| "username": "bob",
| "comment": "foo"
| }]
| }
|}
""".stripMargin)
println(js.validate[Build])
val js1 = Json.parse(
"""
|{
| "buildType": {
| "projectName": "foo",
| "name": "bar"
| },
| "artifact-dependencies": {
| "build": [{
| "number": "1"
| }]
| },
| "changes": {
| "change": [{
| "version": "1",
| "username": "bob",
| "comment": "foo"
| }]
| }
|}
""".stripMargin)
println(js1.validate[Build])
}
}
输出结果为:
[info] JsSuccess(Build(BuildType(foo,bar),None,Changes(List(Commit(1,bob,foo)))),)
[info] JsSuccess(Build(BuildType(foo,bar),Some(ArtifactDependencies(List(DependencyInfo(1)))),Changes(List(Commit(1,bob,foo)))),)
注意稍微尴尬
(JsPath \ "change").read[List[Commit]].map(Changes.apply)
单个参数案例类需要。
编辑:
我错过的关键部分是parentNumber
现在成为Build
上定义的方法,如下所示:
case class Build(buildType: BuildType, `artifact-dependencies`: Option[ArtifactDependencies], changes: Changes) {
def parentNumber: Option[String] = `artifact-dependencies`.flatMap(_.build.headOption.map(_.number))
}