鲜为人知的 jackson Pointer 语法,超好用!

一、语法JSON Pointer 是一个包含零个或多个引用标记的 Unicode 字符串,每个引用标记以 “/” (%x2F) 字符为前缀 。如果引用标记包含 “~” (%x7E) 或 “/” (%x2F) 字符,则它们必须分别被编码为 “~0” 和 “~1” 。它的 ABNF 语法如下:
json-pointer = *( "/" reference-token )reference-token = *( unescaped / escaped )unescaped = %x00-2E / %x30-7D / %x7F-10FFFFescaped = "~" ( "0" / "1" )如果一个 JSON 指针值不符合这个语法,则属于错误的条件 。
二、语法示例JSON Pointer 语法所有引号“"” (%x22)、反斜杠“” (%x5C)和控制字符(%x00-1F)的实例必须被转义 。例如,给定以下JSON文档
{"foo": ["bar", "baz"],"": 0,"a/b": 1,"c%d": 2,"e^f": 3,"g|h": 4,"i\j": 5,"k"l": 6," ": 7,"m~n": 8}那么以下 JSON 字符串应用标记和对应的值:
""// 读取整个文档"/foo"["bar", "baz"]"/foo/0""bar" // 读取数组、集合中的第0个元素"/"0"/a~1b"1"/c%d"2"/e^f"3"/g|h"4"/i\j"5"/k"l"6"/ "7"/m~0n"8三、复杂 Json 示例【鲜为人知的 jackson Pointer 语法,超好用!】示例 json 文本(节选自笔者掘金用户信息):
{"err_no": 0,"err_msg": "success","data": {"user_name": "如梦技术","description": "生活不止眼前的苟且,还有诗和远方的田野 。","blog_address": "https://www.dreamlu.NET","user_growth_info": {"user_id": 1591748566975837,"jpower": 4056,"jscore": 1208.1,"jpower_level": 4,"jscore_level": 5,"jscore_title": "先锋掘友","author_achievement_list": [],"vip_level": 1,"vip_title": "初学乍练","jscore_next_level_score": 2000,"jscore_this_level_mini_score": 500,"vip_score": 0}}}需求是只需要获取 “jscore_title” 字段,获取这个值按照上文的语法我们的完整引用标记为:/data/user_growth_info/jscore_title 。笔者采用 mica 中的 mica-core 中的 Jsonutil作为 jackson 的辅助工具类(这应该也是市面上最好用和最全的一个 jackson json 工具类) 。示例 JAVA 代码:
// 读取 json 为 JsonNodeJsonNode jsonNode = JsonUtil.readTree(json);// 调用 at 方法,传入 JSON Pointer 引用标记JsonNode titleNode = jsonNode.at("/data/user_growth_info/jscore_title");// 读取节点文本String jsCoreTitle = titleNode.asText();System.out.println(jsCoreTitle); // 先锋掘友注意:使用 JSON Pointer 语法获取不存在的节点时也不会报错,在使用 asText、asInt 等方法获取节点的值时会默认返回 null , 当然这些方法也都有个带默认值的方法 , 非常好用 。另外我们也可以将某个节点转换成 Java Bean,例如上面的 user_growth_info节点,示例代码如下:UserGrowthInfo Bean(使用 idea GsonFormatPlus 插件生成)
@Datapublic class UserGrowthInfo {@JsonProperty("user_id")private Long userId;@JsonProperty("jpower")private Integer jpower;@JsonProperty("jscore")private Double jscore;@JsonProperty("jpower_level")private Integer jpowerLevel;@JsonProperty("jscore_level")private Integer jscoreLevel;@JsonProperty("jscore_title")private String jscoreTitle;@JsonProperty("author_achievement_list")private List<?> authorAchievementList;@JsonProperty("vip_level")private Integer vipLevel;@JsonProperty("vip_title")private String vipTitle;@JsonProperty("jscore_next_level_score")private Integer jscoreNextLevelScore;@JsonProperty("jscore_this_level_mini_score")private Integer jscoreThisLevelMiniScore;@JsonProperty("vip_score")private Integer vipScore;}读取 json 并转换成 UserGrowthInfo Bean:
// 读取 json 为 JsonNodeJsonNode jsonNode = JsonUtil.readTree(json);// 读取 user_growth_info 节点JsonNode userGrowthInfoNode = jsonNode.at("/data/user_growth_info");// 转换成 UserGrowthInfo beanUserGrowthInfo userGrowthInfo = JsonUtil.treeToValue(userGrowthInfoNode, UserGrowthInfo.class);System.out.println(userGrowthInfo);// 输出结果:UserGrowthInfo(userId=1591748566975837, jpower=4056, jscore=1208.1, jpowerLevel=4, // jscoreLevel=5, jscoreTitle=先锋掘友, authorAchievementList=[], vipLevel=1, vipTitle=初学乍练, // jscoreNextLevelScore=2000, jscoreThisLevelMiniScore=500, vipScore=0)


推荐阅读