在 JSON 字段(json4s 框架)的情况下,Scala 在模式匹配中的奇怪行为
Strange behaviour of Scala in pattern matching in case of JSON fields (json4s framework)
我正在尝试遍历 JSON 并提取有关字段中对象类型的信息:
import org.json4s._
import org.json4s.JsonAST.{JArray, JString, JObject, JInt, JBool, JDouble}
import org.json4s.JsonDSL._
import org.json4s.native.JsonMethods._
def guessTypes(example: JValue): JObject = example match {
case JObject(lst) => JObject(lst.map {
case (f, JObject(nested)) => JField(f, ("type" -> "object") ~ ("properties" -> guessTypes(nested)))
case (f, JString(s)) => JField(f, "type" -> "string")
case (f, JInt(num)) => JField(f, "type" -> "integer")
case (f, JDouble(double)) => JField(f, "type" -> "double")
case (f, JBool(b)) => JField(f, "type" -> "bool")
case (f, JArray(jarray: List[JInt])) => JField(f, ("type" -> "array") ~ ("items" -> "integer"))
case (f, JArray(jarray: List[JString])) => JField(f, ("type" -> "array") ~ ("items" -> "string"))
case (f, JArray(jarray: List[JObject])) => JField(f, ("type" -> "array") ~ ("items" -> "object")) ~ ("properties" -> jarray.map{ x => guessTypes(x)}))
})}
如果是:
def example = """
|{
| "partners_data": [
| {
| "info": {
| "label": "partner45"
| },
| "partner_id": "partner45",
| "data": {
| "field": 24
| }
| }
| ],
| "name": "*****(",
| "location": [
| 1,
| 2
| ],
| "is_mapped": false
|}
""".stripMargin
得到结果:
{"data":{"type":"array","items":"object"},"name":{"type":"string"},"location":{"type":"array","items":"object"},"is_mapped":{"type":"bool"}}
这不是合适的结果,因为对于 "location" 中的键 "items",预期值 "integer"。
看起来 Scala 无法区分 JArrays 中除了 JValue 之外的任何东西。 如果我替换
case (f, JArray(jarray: List[JInt]))
通过最后一个字符串,对于 "location" 中的键 "items" 将获得值 "object",但对于其他字段,值将是错误的。
如何绕过 scala 模式匹配和 json4s 框架的这种特性?
最后三个模式基本相同,因为 type erasure 在 JVM 上。
由于 JSON 数组可以包含多个 (Scala/Java/...) 类型,因此很难匹配列表元素的类型。
您只能检查数组的第一项:
case (f, JArray(JString(_) :: tail)) =>
JField(f, ("type" -> "array") ~ ("items" -> "string"))
case (f, JArray(jarray @ JObject(_) :: tail)) =>
JField(f, ("type" -> "array") ~ ("items" -> "object") ~ ("properties" -> jarray.map(guessTypes)))
或者检查数组中的每一项:
case (f, JArray(list : List[JValue])) if list forall { case JInt(_) => true; case _ => false } =>
JField(f, ("type" -> "array") ~ ("items" -> "integer"))
我正在尝试遍历 JSON 并提取有关字段中对象类型的信息:
import org.json4s._
import org.json4s.JsonAST.{JArray, JString, JObject, JInt, JBool, JDouble}
import org.json4s.JsonDSL._
import org.json4s.native.JsonMethods._
def guessTypes(example: JValue): JObject = example match {
case JObject(lst) => JObject(lst.map {
case (f, JObject(nested)) => JField(f, ("type" -> "object") ~ ("properties" -> guessTypes(nested)))
case (f, JString(s)) => JField(f, "type" -> "string")
case (f, JInt(num)) => JField(f, "type" -> "integer")
case (f, JDouble(double)) => JField(f, "type" -> "double")
case (f, JBool(b)) => JField(f, "type" -> "bool")
case (f, JArray(jarray: List[JInt])) => JField(f, ("type" -> "array") ~ ("items" -> "integer"))
case (f, JArray(jarray: List[JString])) => JField(f, ("type" -> "array") ~ ("items" -> "string"))
case (f, JArray(jarray: List[JObject])) => JField(f, ("type" -> "array") ~ ("items" -> "object")) ~ ("properties" -> jarray.map{ x => guessTypes(x)}))
})}
如果是:
def example = """
|{
| "partners_data": [
| {
| "info": {
| "label": "partner45"
| },
| "partner_id": "partner45",
| "data": {
| "field": 24
| }
| }
| ],
| "name": "*****(",
| "location": [
| 1,
| 2
| ],
| "is_mapped": false
|}
""".stripMargin
得到结果:
{"data":{"type":"array","items":"object"},"name":{"type":"string"},"location":{"type":"array","items":"object"},"is_mapped":{"type":"bool"}}
这不是合适的结果,因为对于 "location" 中的键 "items",预期值 "integer"。
看起来 Scala 无法区分 JArrays 中除了 JValue 之外的任何东西。 如果我替换
case (f, JArray(jarray: List[JInt]))
通过最后一个字符串,对于 "location" 中的键 "items" 将获得值 "object",但对于其他字段,值将是错误的。
如何绕过 scala 模式匹配和 json4s 框架的这种特性?
最后三个模式基本相同,因为 type erasure 在 JVM 上。
由于 JSON 数组可以包含多个 (Scala/Java/...) 类型,因此很难匹配列表元素的类型。
您只能检查数组的第一项:
case (f, JArray(JString(_) :: tail)) =>
JField(f, ("type" -> "array") ~ ("items" -> "string"))
case (f, JArray(jarray @ JObject(_) :: tail)) =>
JField(f, ("type" -> "array") ~ ("items" -> "object") ~ ("properties" -> jarray.map(guessTypes)))
或者检查数组中的每一项:
case (f, JArray(list : List[JValue])) if list forall { case JInt(_) => true; case _ => false } =>
JField(f, ("type" -> "array") ~ ("items" -> "integer"))