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

2023年 12月 7日 56.1k 0

一、语法

JSON Pointer 是一个包含零个或多个引用标记的 Unicode 字符串,每个引用标记以 “/” (%x2F) 字符为前缀。如果引用标记包含 “~” (%x7E) 或 “/” (%x2F) 字符,则它们必须分别被编码为 “~0” 和 “~1”。它的 ABNF 语法如下:

json-pointer = *( "/" reference-token )
reference-token = *( unescaped / escaped )
unescaped = %x00-2E / %x30-7D / %x7F-10FFFF
escaped = "~" ( "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 示例

示例 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 为 JsonNode
JsonNode 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 插件生成)

@Data
public 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 为 JsonNode
JsonNode jsonNode = JsonUtil.readTree(json);
// 读取 user_growth_info 节点
JsonNode userGrowthInfoNode = jsonNode.at("/data/user_growth_info");
// 转换成 UserGrowthInfo bean
UserGrowthInfo 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)

四、总结

Jackson JSON Pointer 语法非常简单易用,Jackson 官网文档改版之后这个文档很难找到了。笔者从14年开始使用,并且将她融入到很多 mica 组建中。例如使用 mica-http 来读取我们想要的结果:

// 读取 linkedin 邮箱
private String getUserEmail(String accessToken) {
    return HttpRequest.get("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))")
            .addHeader("Host", "api.linkedin.com")
            .addHeader("Connection", "Keep-Alive")
            .addHeader("Authorization", "Bearer " + accessToken)
            .execute()
            .asJsonNode()
            .at("/elements/0/handle~0/emailAddress")
            .asText();
}

Jackson 还是非常好用的,希望此篇文章对大家有所帮助!更多精彩好文敬请关注我们!!!

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论