我试图使用asOpt [T]提取值,因为我可能没有密钥。虽然我的字符串中有值,但我得到了回报。
val jsonString = {"data": {"operation":"+", "value":"1"},"right": {"data":{"operation":"-", "value":"2"}},"left":{"data":{"operation":"-", "value":"4"}}}
buildFromJson(jsonString)
代码:
import play.api.libs.json.Json
...
def buildFromJson(jsonString: String) : TreeNode = {
val nodeData = json.\("data")
val nodeValue = nodeData.\("operation").as[String] //This works
println("left= " + json.\("left").asOpt[String]) //output= None
println("left= " + json.\("left").as[String]) // throws exception
println("left= " + json.\("left").toString) //output= {"data":{"operation":"-", "value":"4"}}
解析可能不存在的JSON密钥的最佳方法是什么?正如我所读到的那样,它已经开始了,但它并没有起作用。
更新 - 我如何克服这个问题:
1)首先m-z是对的,它确实是对象,不能作为String返回。
2)我无法找到一种提取可能不存在的字段的好方法(就像刚开始时我只在正确的子节点存在时才添加'右边的json)。现在我添加到json'右边'如果字段不存在,则为空值。
答案 0 :(得分:0)
你可以检查一个值是否为None,但这通常是一个坏主意。它导致模糊代码与大量的模式匹配/错误处理代码/ if / etc ...好消息是Option
和JsResult
都是monad!你可以使用你喜欢的那个,但我更喜欢JsResult
,因为它会给出错误消息,而Option
只是给你None
(知道在哪里寻找错误总是比不得不更好测试每一行)。
假设您有一个alegraic数据类型TreeNode:
abstract class TreeNode
case class Leaf(operation : String, value : String) extends TreeNode
case class Node(l : Leaf, left : TreeNode, right : TreeNode) extends TreeNode
解析功能可以是:
def buildTreeNode(json: JsValue) : JsResult[TreeNode] =
for {operation <- (json \ "data" \ "operation").validate[String]
value <- (json \ "data" \ "value").validate[String]
leaf = Leaf(operation, value)
left <- (json \ "left").validate[Option[JsObject]]
right <- (json \ "right").validate[Option[JsObject]]
result <- (left , right) match {
case (Some(l), Some(r)) => for { leftnode <- buildTreeNode(l)
rightNode <- buildTreeNode(r)
} yield Node(leaf, leftnode, rightNode)
case (None , None ) => JsSuccess(leaf)
case (_ , _ ) => JsError("left or right is missing")
}
} yield result
代码尝试在operation
下找到value
和data
字段。如果这些行失败,则计算停止并返回指示erorr的错误消息。成功后,计算继续并尝试查找左侧和右侧字段。如果它们存在并且有效JsObject
,它们将被递归地解析。如果不存在,则此节点是叶子。否则,出现错误。
这段代码虽然易于阅读,但却执行了大量的错误处理,没有异常并且返回的错误消息并不是很糟糕