将值映射到 json4s 中的大小写 class
Mapping values to case class in json4s
假设我有以下内容:
[ {
"job_id": "1",
"status": "running"
},
{
"job_id": "0",
"status": "finished"
}]
我可以用 json4s 做以下事情吗:
case class Job(job_id: Int, status: JobStatus)
abstract class JobStatus
case class JobFinished extends JobStatus
case class JobRunning extends JobStatus
... some magic is probably needed here
这样提取第一个片段将导致:
[ Job(1, JobRunning()), Job(0, JobFinished())]
我认为基于 JSON 创建 scala 案例 class 的最佳方法是使用此站点,这增加了魔力,我通常使用此站点,您甚至可以更改类的名称,因此在您的情况下,您可以使用该站点,然后管理 classes:
中的关系
您可以使用枚举并通过 json4s-ext 添加 EnumSerializer
到您的 formats
。
但是,您的枚举将被序列化为 int(在您的情况下为 0 或 1)。
添加到我自己的答案中,您还可以使用 EnumNameSerializer,它将序列化为您指定的枚举值(如 "running" 或 "finished")。
这是可能的,但需要一些编码。
我将尝试将其分解为一些更小的步骤。
类型/模型的定义
// Types
case class Job(jobId: Int, status: JobStatus)
// Sealed trait to make match exhaustive in helper functions
sealed trait JobStatus
// use case object to not create uneeded instances, also case class without () no longer allowed
case object JobFinished extends JobStatus
case object JobRunning extends JobStatus
“魔法”
需要进口
import org.json4s._
import org.json4s.native.Serialization
import org.json4s.native.Serialization.{read, write}
辅助函数
// helper functions, could be improved by having a mapping
implicit def stringToJobStatus(in: String) : JobStatus = in match {
case "running" => JobRunning
case "finished" => JobFinished
}
implicit def jobStatusToString(jobStatus: JobStatus) : String = jobStatus match {
case JobRunning => "running"
case JobFinished => "finished"
}
自定义序列化程序
// here is the "magic" a custom serializer
class JobSerializer extends CustomSerializer[Job](format => (
// unmarshal Function
{
case JObject( JField("job_id", JString(jobId)) :: JField("status", JString(status)) :: Nil ) => {
new Job(jobId.toInt, status)
}
},
// masrshal Function
{
case Job(jobId, status) => JObject(
JField("job_id", JString(jobId.toString)) ::
JField("status", JString(status)) :: Nil)
}
))
使序列化程序可用
// Implicit formats for serialization and deserialization
implicit val formats = Serialization.formats(NoTypeHints) + new JobSerializer
REPL 中的示例
val data = """
[
{
"job_id": "1",
"status": "running"
},
{
"job_id": "0",
"status": "finished"
}
]
"""
read[List[Job]](data)
res3: List[Job] = List(Job(1,JobRunning), Job(0,JobFinished))
尽管@Andres Neumann 的回答非常好,但它确实需要重新实现整个作业的序列化 class(这可能比示例中简化的作业 class 大很多) ,而实际需要的唯一序列化是状态。根据@Andreas 的回答,实际需要的代码要短一些,并且不需要手动序列化作业中的每个字段。
// here is the "magic" a custom serializer
class JobStatusSerializer extends CustomSerializer[JobStatus](format => (
// unmarshal Function
{
case JString(status) => {
// helper functions, could be improved by having a mapping
def stringToJobStatus(in: String): JobStatus = in match {
case "running" => JobRunning
case "finished" => JobFinished
}
stringToJobStatus(status)
}
},
// marshal Function
{
case status: JobStatus => {
def jobStatusToString(jobStatus: JobStatus): String = jobStatus match {
case JobRunning => "running"
case JobFinished => "finished"
}
JString(jobStatusToString(status))
}
}
)
)
假设我有以下内容:
[ {
"job_id": "1",
"status": "running"
},
{
"job_id": "0",
"status": "finished"
}]
我可以用 json4s 做以下事情吗:
case class Job(job_id: Int, status: JobStatus)
abstract class JobStatus
case class JobFinished extends JobStatus
case class JobRunning extends JobStatus
... some magic is probably needed here
这样提取第一个片段将导致:
[ Job(1, JobRunning()), Job(0, JobFinished())]
我认为基于 JSON 创建 scala 案例 class 的最佳方法是使用此站点,这增加了魔力,我通常使用此站点,您甚至可以更改类的名称,因此在您的情况下,您可以使用该站点,然后管理 classes:
中的关系您可以使用枚举并通过 json4s-ext 添加 EnumSerializer
到您的 formats
。
但是,您的枚举将被序列化为 int(在您的情况下为 0 或 1)。
添加到我自己的答案中,您还可以使用 EnumNameSerializer,它将序列化为您指定的枚举值(如 "running" 或 "finished")。
这是可能的,但需要一些编码。 我将尝试将其分解为一些更小的步骤。
类型/模型的定义
// Types
case class Job(jobId: Int, status: JobStatus)
// Sealed trait to make match exhaustive in helper functions
sealed trait JobStatus
// use case object to not create uneeded instances, also case class without () no longer allowed
case object JobFinished extends JobStatus
case object JobRunning extends JobStatus
“魔法”
需要进口
import org.json4s._
import org.json4s.native.Serialization
import org.json4s.native.Serialization.{read, write}
辅助函数
// helper functions, could be improved by having a mapping
implicit def stringToJobStatus(in: String) : JobStatus = in match {
case "running" => JobRunning
case "finished" => JobFinished
}
implicit def jobStatusToString(jobStatus: JobStatus) : String = jobStatus match {
case JobRunning => "running"
case JobFinished => "finished"
}
自定义序列化程序
// here is the "magic" a custom serializer
class JobSerializer extends CustomSerializer[Job](format => (
// unmarshal Function
{
case JObject( JField("job_id", JString(jobId)) :: JField("status", JString(status)) :: Nil ) => {
new Job(jobId.toInt, status)
}
},
// masrshal Function
{
case Job(jobId, status) => JObject(
JField("job_id", JString(jobId.toString)) ::
JField("status", JString(status)) :: Nil)
}
))
使序列化程序可用
// Implicit formats for serialization and deserialization
implicit val formats = Serialization.formats(NoTypeHints) + new JobSerializer
REPL 中的示例
val data = """
[
{
"job_id": "1",
"status": "running"
},
{
"job_id": "0",
"status": "finished"
}
]
"""
read[List[Job]](data)
res3: List[Job] = List(Job(1,JobRunning), Job(0,JobFinished))
尽管@Andres Neumann 的回答非常好,但它确实需要重新实现整个作业的序列化 class(这可能比示例中简化的作业 class 大很多) ,而实际需要的唯一序列化是状态。根据@Andreas 的回答,实际需要的代码要短一些,并且不需要手动序列化作业中的每个字段。
// here is the "magic" a custom serializer
class JobStatusSerializer extends CustomSerializer[JobStatus](format => (
// unmarshal Function
{
case JString(status) => {
// helper functions, could be improved by having a mapping
def stringToJobStatus(in: String): JobStatus = in match {
case "running" => JobRunning
case "finished" => JobFinished
}
stringToJobStatus(status)
}
},
// marshal Function
{
case status: JobStatus => {
def jobStatusToString(jobStatus: JobStatus): String = jobStatus match {
case JobRunning => "running"
case JobFinished => "finished"
}
JString(jobStatusToString(status))
}
}
)
)