scala upickle / ujson 中 JSON null 的惯用处理
Idiomatic handling of JSON null in scala upickle / ujson
我是 Scala 的新手,想学习解决常见问题的惯用方法,就像 Python 的 pythonic 一样。我的问题是关于使用 upickle 读取 JSON 数据,其中 JSON 值在存在时包含一个字符串,在不存在时包含 null。我想使用自定义值来替换 null。一个简单的例子:
import upickle.default._
val jsonString = """[{"always": "foo", "sometimes": "bar"}, {"always": "baz", "sometimes": null}]"""
val jsonData = ujson.read(jsonString)
for (m <- jsonData.arr) {
println(m("always").str.length) // this will work
println(m("sometimes").str.length) // this will fail, Exception in thread "main" ujson.Value$InvalidData: Expected ujson.Str (data: null)
}
问题出在字段 "sometimes"
上:当为 null 时,我们无法应用 .str
(或任何其他映射到除 null 之外的静态类型的函数)。我正在寻找类似 m("sometimes").str("DEFAULT").length
的内容,其中 "DEFAULT"
是 null 的替代品。
想法 1
使用模式匹配,以下工作:
val sometimes = m("sometimes") match {
case s: ujson.Str => s.str
case _ => "DEFAULT"
}
println(sometimes.length)
考虑到 Scala 简洁的语法,这看起来有点复杂,并且在对多个值完成时会重复。
想法 2
related question 的答案提到用默认值创建案例 class。对于我的问题,当根据上下文需要不同的替换值时,创建案例 class 对我来说似乎不灵活。
想法 3
的答案讨论使用 Try().getOrElse()
,即:
import scala.util.Try
// ...
println(Try(m("sometimes").str).getOrElse("DEFAULT").length)
但是,讨论中提到为常规程序路径抛出异常是昂贵的。
有哪些惯用而又简洁的方法可以解决这个问题?
通过使用 Scala 的 Option.
来执行此操作的惯用或 Scala 方法
幸运的是,upickle Values 提供了它们。参考 this 源代码中的 strOpt 方法。
您的代码问题是 m("always").str 和 m("sometimes").str 中的 str 方法
使用此代码,您过早地假设所有值都是字符串。这就是 strOpt 方法的来源。如果它的值是一个字符串,它要么输出一个字符串,否则输出一个 None 类型。如果值为 None.
,我们可以使用 getOrElse 方法和它来决定抛出什么
以下将是处理此问题的最佳方式。
val jsonString = """[{"always": "foo", "sometimes": "bar"}, {"always": "baz", "sometimes": null}]"""
for (m <- jsonData.arr) {
println(m("always").strOpt.getOrElse("").length)
println(m("sometimes").strOpt.getOrElse("").length)
}
输出:
3
3
3
0
这里如果我们得到除字符串(null、float、int)以外的任何值,代码会将其输出为空字符串。并且它的长度将被计算为0.
基本上,这类似于您的“Idea1”方法,但这是 Scala 方法。我抛出一个空字符串而不是“DEFAULT”,因为您不希望空值的长度为 7(字符串“DEFAULT”的长度)。
我是 Scala 的新手,想学习解决常见问题的惯用方法,就像 Python 的 pythonic 一样。我的问题是关于使用 upickle 读取 JSON 数据,其中 JSON 值在存在时包含一个字符串,在不存在时包含 null。我想使用自定义值来替换 null。一个简单的例子:
import upickle.default._
val jsonString = """[{"always": "foo", "sometimes": "bar"}, {"always": "baz", "sometimes": null}]"""
val jsonData = ujson.read(jsonString)
for (m <- jsonData.arr) {
println(m("always").str.length) // this will work
println(m("sometimes").str.length) // this will fail, Exception in thread "main" ujson.Value$InvalidData: Expected ujson.Str (data: null)
}
问题出在字段 "sometimes"
上:当为 null 时,我们无法应用 .str
(或任何其他映射到除 null 之外的静态类型的函数)。我正在寻找类似 m("sometimes").str("DEFAULT").length
的内容,其中 "DEFAULT"
是 null 的替代品。
想法 1 使用模式匹配,以下工作:
val sometimes = m("sometimes") match {
case s: ujson.Str => s.str
case _ => "DEFAULT"
}
println(sometimes.length)
考虑到 Scala 简洁的语法,这看起来有点复杂,并且在对多个值完成时会重复。
想法 2 related question 的答案提到用默认值创建案例 class。对于我的问题,当根据上下文需要不同的替换值时,创建案例 class 对我来说似乎不灵活。
想法 3
Try().getOrElse()
,即:
import scala.util.Try
// ...
println(Try(m("sometimes").str).getOrElse("DEFAULT").length)
但是,讨论中提到为常规程序路径抛出异常是昂贵的。
有哪些惯用而又简洁的方法可以解决这个问题?
通过使用 Scala 的 Option.
来执行此操作的惯用或 Scala 方法幸运的是,upickle Values 提供了它们。参考 this 源代码中的 strOpt 方法。
您的代码问题是 m("always").str 和 m("sometimes").str 中的 str 方法 使用此代码,您过早地假设所有值都是字符串。这就是 strOpt 方法的来源。如果它的值是一个字符串,它要么输出一个字符串,否则输出一个 None 类型。如果值为 None.
,我们可以使用 getOrElse 方法和它来决定抛出什么以下将是处理此问题的最佳方式。
val jsonString = """[{"always": "foo", "sometimes": "bar"}, {"always": "baz", "sometimes": null}]"""
for (m <- jsonData.arr) {
println(m("always").strOpt.getOrElse("").length)
println(m("sometimes").strOpt.getOrElse("").length)
}
输出:
3
3
3
0
这里如果我们得到除字符串(null、float、int)以外的任何值,代码会将其输出为空字符串。并且它的长度将被计算为0.
基本上,这类似于您的“Idea1”方法,但这是 Scala 方法。我抛出一个空字符串而不是“DEFAULT”,因为您不希望空值的长度为 7(字符串“DEFAULT”的长度)。