scala upickle / ujson中JSON null的惯用处理

Posted

技术标签:

【中文标题】scala upickle / ujson中JSON null的惯用处理【英文标题】:Idiomatic handling of JSON null in scala upickle / ujson 【发布时间】:2021-07-01 14:17:25 【问题描述】:

我是 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":当为空时,我们不能应用.str(或任何其他函数映射到除空之外的静态类型)。我正在寻找类似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 的回答提到使用默认值创建案例类。对于我的问题,当根据上下文需要不同的替换值时,案例类的创建对我来说似乎不灵活。

想法 3 回复another question (not specific to upickle) 讨论使用Try().getOrElse(),即:

import scala.util.Try
// ...
println(Try(m("sometimes").str).getOrElse("DEFAULT").length)

但是,讨论中提到为常规程序路径抛出异常是昂贵的。

解决这个问题的惯用但简洁的方法是什么?

【问题讨论】:

【参考方案1】:

使用 scala 的 Option 来执行此操作的惯用或 scala 方式。

幸运的是,upickle Values 提供了它们。参考this源码中的strOpt方法。

您在代码中的问题是 m("always").str 和 m("sometimes").str 中的 str 方法 使用此代码,您过早地假设所有值都是字符串。这就是 strOpt 方法的来源。如果它的值是一个字符串,它要么输出一个字符串,要么输出一个 None 类型,如果不是。并且我们可以使用 getOrElse 方法加上它来决定如果值为 None 时抛出什么。

以下将是处理此问题的最佳方法。

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”的长度)。

【讨论】:

感谢 Nihad 提供有用且内容丰富的答案并指出实际来源!

以上是关于scala upickle / ujson中JSON null的惯用处理的主要内容,如果未能解决你的问题,请参考以下文章

在Scala中解析内容为Json格式的文件

AWS Lambda -- 无法为 SpaCy 导入 srsly.ujson.ujson

如何在我的 ujson 转储中包含私有属性?

如何使用 ujson 作为 Flask 编码器/解码器?

Python中的ujson包安装错误

在 ubuntu 中安装 ujson 的问题