使用jakarta.json进行json序列化和反序列化

引言

java里,json框架何其多,常见的有jackson、fastjson、gson等。各自的api互不相通,与代码耦合度高,切换json库的代码修改工作量非常大。如果使用json的api为统一的入口,各家再进行实现,代码与实现类不耦合,像slf4j一样,切换log框架(log4j/logback等)也不用改代码,代码只对api依赖,不对具体的实现依赖,那就很好了。
jakarta.json项目就是为了解决这个问题的。自从javax改名jakarta,并全部给eclipse基金会运营,jakarta.json作为Jakarta EE子项目之一也得到了支持。

引入依赖

        <dependency>
            <groupId>jakarta.json.bind</groupId>
            <artifactId>jakarta.json.bind-api</artifactId>
            <version>3.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse</groupId>
            <artifactId>yasson</artifactId>
            <version>3.0.3</version>
        </dependency>

jakarta.json.bind-api是jsonb的接口包,里面只有对象定义、接口定义等,没有代码实现。yasson则对jsonb规范进行了完整实现。

将一个JsonObject序列化为字符串(JSON)

        JsonObject jsonObject = Json.createObjectBuilder()
                .add("name", "John")
                .add("age", 12)
                .add("remark", "This is a test")
                .add("birthDate", LocalDate.parse("2020-01-01").toString())
                .add("hobbies", Json.createArrayBuilder().add("football").add("basketball"))
                .add("members", Json.createArrayBuilder().add(Json.createObjectBuilder().add("rel", "son").add("name", "Lilei").build()))
                .build();
        System.out.println(jsonObject.toString());

注:JsonObject的带包名类名是jakarta.json.JsonObject,位于

        <dependency>
            <groupId>jakarta.json</groupId>
            <artifactId>jakarta.json-api</artifactId>
            <version>2.1.3</version>
        </dependency>

以上代码打印结果:

{"name":"John","age":12,"remark":"This is a test","birthDate":"2020-01-01","hobbies":["football","basketball"],"members":[{"rel":"son","name":"Lilei"}]}

将一个对象序列化为json字符串(JSONB)

    @Test
    void serialize() {
        User user = new User("John", 12, "This is a test", LocalDate.parse("2020-01-01"));
        String json = JsonbBuilder.create().toJson(user);
        System.out.println(json);
    }

注:JsonbBuilderjakarta.json.bind.JsonbBuilder,位于:

        <dependency>
            <groupId>jakarta.json.bind</groupId>
            <artifactId>jakarta.json.bind-api</artifactId>
            <version>3.0.1</version>
        </dependency>

将一个对象序列化为json字符串(格式化)

    @Test
    void prettySerialize() {
        User user = new User("John", 12, "This is a test", LocalDate.parse("2020-01-01"));
        String json = JsonbBuilder.create(new JsonbConfig().withFormatting(true)).toJson(user);
        System.out.println(json);
    }

将一个json字符串反序列化为一个对象

    public record User(String name, int age, String remark, LocalDate birthDate) { }

    @Test
    void deserialize() {
        String json = """
                {
                    "name": "John",
                    "age": 12,
                    "remark": "This is a test",
                    "birthDate": "2020-01-01"
                }
                """;
        User user = JsonbBuilder.create().fromJson(json, User.class);
        System.out.println("user = " + user);
    }

面向接口编程

以上代码,去掉

        <dependency>
            <groupId>org.eclipse</groupId>
            <artifactId>yasson</artifactId>
            <version>3.0.3</version>
        </dependency>

代码编译不会报错,但运行时会提示没有有效的provider,因为代码依赖的是jakarta.json、jakarta.json.bind两个api包,不影响代码编译。
但api只定义了接口、方法入参及返回类型、数据对象结构,自身并没有逻辑代码,所以无法实现对象的序列化和反序列化。

探密yasson

yasson之所以能在代码不依赖org.eclipse.yasson.*的情况下实现对对象进行JSON序列化和反序列化。是因为
yasson包里定义jakarta.json.bind的SPI

SPI的写法(Java8及之前)

定义SPI文件META-INF/services/jakarta.json.bind.spi.JsonbProviderMETA-INF/services/是固定写法,jakarta.json.bind.spi.JsonbProvider是接口/抽象类的类名,作为该文件名。

org.eclipse.yasson.JsonBindingProvider

文件里只有一行代码,指定了org.eclipse.yasson.JsonBindingProviderjakarta.json.bind.spi.JsonbProvider的实现类。

SPI的写法(Java9及之后)

使用module-info.java来定义依赖的模块、对外开放的模块、SPI,其中SPI使用以下语法来对接口进行指定实现类:
写法:

provides 接口类 with 实现类;

yasson的完整module-info

module org.eclipse.yasson {
    requires jakarta.json;
    requires jakarta.json.bind;
    requires java.logging;
    requires static java.xml;
    requires static java.naming;
    requires static java.sql;
    requires static java.desktop;
    requires static jakarta.cdi;

    exports org.eclipse.yasson;
    exports org.eclipse.yasson.spi;
    provides jakarta.json.bind.spi.JsonbProvider with org.eclipse.yasson.JsonBindingProvider;
    uses org.eclipse.yasson.spi.JsonbComponentInstanceCreator;
}

手写一个json实现库

一样的写法,依赖的接口和数据对象是jakarta.json、jakarta.json.bind,实现逻辑则由自己进行实现。
实现后,按上面的SPI方法定义实现类,然后把pom里的yasson依赖去掉,加上自己的依赖。
系统加载时,会自动发现SPI的实现类。

posted @   漠孤烟  阅读(339)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示