今天才发现 Jackson 其实是支持 Json-Path 的,但以前一直不知道,关键是 Jackson 的文档也没见提到。所以很久以前是自己给 Jackson 写了一个简陋的 Json-Path 支持类,这个是为测试代码用的,所以基本够用。想要更全面的功能可使用 https://github.com/jayway/JsonPath 这个项目。对于 Jackson 中如何使用 Json-Path, 我还会进一步研究下。
我写的 RichJsonNode 类是一个 Scala 版本,最早也写过一个 Java 版本,还有一个基于 GSON 的 Java 版本的。这里只贴出 Scala 版源码。支持的基本方法及语法如下:
a/b, a/b[2], a/b[1]/c, a[1]/b, $[0]
selectNode(path), selectString(path), selectInt(path), selectDouble(path), selectBollean(path), hasField(path) 和 arrayLength(path)
源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
package cc.unmi import com.fasterxml.jackson.databind.JsonNode /** * JSON PATH, Inspired by XPath, need an exact path relative to root element * Can't start with "/", of course, won't support "//" fuzzy query * json-path examples: * <ul> * <li>a/b</li> * <li>a/b[2]</li> * <li>a/b[1]/c</li> * <li>a[1]/b</li> * <li>$[0] if root element is an Array, can't use [0]/a</li> * </ul> */ class RichJsonNode(json: JsonNode) { def selectNode(jsonPath: String) = getTailElement(jsonPath) def selectString(jsonPath: String) = getTailElement(jsonPath).ensuring(t=>t.isNull||t.isTextual, "not a text node").textValue def selectInt(jsonPath: String) = getTailElement(jsonPath).ensuring(_.isInt, "not an int node").intValue() def arrayLength(jsonPath: String) = getTailElement(jsonPath).size def selectDouble(jsonPath: String) = getTailElement(jsonPath).ensuring(_.isNumber, "not a double node").doubleValue() def selectBoolean(jsonPath: String) = getTailElement(jsonPath).ensuring(_.isBoolean, "not a boolean node").booleanValue def isJsonNull(jsonPath: String) = getTailElement(jsonPath).isNull def hasField(jsonPath: String): Boolean = { try { selectNode(jsonPath) true } catch { case _: Throwable => false } } private def getTailElement(jsonPath: String): JsonNode = { assume(jsonPath != null && jsonPath.trim.length > 0) assume(!jsonPath.startsWith("/"), "doesn't support /a/b, or //b, path is relative") jsonPath.split("/").foldLeft(json)((tmpJson, key) => { if(key == "$"){ tmpJson }else if (key.contains("[")) { val newKey = key.substring(0, key.indexOf('[')) val index = """\[\d+\]""".r.findFirstIn(key).get.replaceAll("""\[|\]""","").toInt if (newKey == "$") { tmpJson.get(index) } else { tmpJson.get(newKey).get(index) } } else { tmpJson.get(key) } }); } ensuring( _ != null) } object RichJsonNode { implicit def enrich(json: JsonNode):RichJsonNode = new RichJsonNode(json) } |
因为做成了一个用于隐式转型的类,所以在 Scala 中使用起来比较简单
1 2 3 4 5 6 7 8 9 |
import cc.unmi.RichJsonNode.enrich import com.fasterxml.jackson.databind.{JsonNode, ObjectMapper} object TestClient { val jsonString = """{ "a": { "b": 123, "c": [1, 2, { "d": "456" } ] } }""" val json = new ObjectMapper().readValue(jsonString, classOf[JsonNode]) System.out.println(json.selectInt("a/c[1]")) //2 System.out.println(json.selectString("a/b/c[2]/d") //456 } |
如果顶级是一个数组,如 [1, 2, {"a": 3}],那么可以用
1 2 |
json.selectInt("$[0]") //1 json.selectint("$[2]/a") //3 |
对于 Jackson 的 Json-Path 支持还正在研习当中,如果 Jackson 已足够好,我将摈弃这里的实现。
本文链接 https://yanbin.blog/jackson-json-path-support-before/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。