前端视角 Java Web 入门手册 3.5:常用工具—— Fastjson2

Fastjson2 是阿里巴巴开源的一个高性能、功能丰富的 JSON 解析和生成库,是原有 Fastjson 的升级版。相比于 Fastjson1,Fastjson2 在性能、安全性、可扩展性等方面进行了全面优化和改进,以满足现代 Java 应用对高效数据处理的需求。

核心优势 ------ fast

天下武功,唯快不破,Fastjson2 正如其名称暗示的,在序列化、反序列化时候相对其它库有明显性能优势。在最常用的将 JSON 格式字符串反序列化为 Java 对象场景 Fastjson2 的性能差不多是jackson/gson 的 3~4 倍

  • fastjson1 只相当于只相当于 Fastjson2 的 54.2%
  • jackson 则只相当于Fastjson2 的 26.74%
  • gson 是 Fastjson2 的 27.09%
plain 复制代码
// aliyun.c8i.large jdk-11.0.19

      fastjson2 > fastjson1 > jackson > gson
jdk8  100%        69.63%      34.43%    31.42%
jdk11 100%        54.20%      26.74%    27.09%
jdk17 100%        66.08%      25.64%    25.66%

详细测试数据:github.com/alibaba/fas...

在项目中使用 Fastjson2

添加 maven 依赖

xml 复制代码
<dependency>
  <groupId></groupId>
  <artifactId>fastjson2</artifactId>
  <version>2.0.51</version>
</dependency>

定义一个 Java Bean

java 复制代码
public class User {
    private Long id;
    private String name;
    private String email;

    // 构造函数
    public User() {}

    public User(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    // Getter 和 Setter
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }

    // toString 方法
    @Override
    public String toString() {
        return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
    }
}

序列化 Java 对象为 JSON

java 复制代码
import com.alibaba.fastjson2.JSON;

public class SerializeExample {
    public static void main(String[] args) {
        User user = new User(1L, "Alice", "alice@example.com");
        String jsonString = JSON.toJSONString(user);
        System.out.println("Serialized JSON:");
        System.out.println(jsonString);
    }
}

输出

plain 复制代码
Serialized JSON:
{"id":1,"name":"Alice","email":"alice@example.com"}

反序列化 JSON 为 Java 对象

java 复制代码
import com.alibaba.fastjson2.JSON;

public class DeserializeExample {
    public static void main(String[] args) {
        String jsonString = "{"id":1,"name":"Alice","email":"alice@example.com"}";
        User user = JSON.parseObject(jsonString, User.class);
        System.out.println("Deserialized Object:");
        System.out.println(user);
    }
}

输出

plain 复制代码
Deserialized Object:
User{id=1, name='Alice', email='alice@example.com'}

高级用法

Fastjson2 提供了多种高级功能

自定义序列化和反序列化

通过实现 ObjectWriterObjectReader 接口,可以自定义序列化和反序列化逻辑

java 复制代码
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.writer.ObjectWriter;
import com.alibaba.fastjson2.writer.ObjectWriterCreator;
import com.alibaba.fastjson2.writer.ObjectWriterFactory;
import com.alibaba.fastjson2.writer.WriterFeature;

public class CustomSerializeExample {
    public static void main(String[] args) {
        User user = new User(2L, "Bob", "bob@example.com");
        ObjectWriter<User> objectWriter = JSON.writerFor(User.class);
        String jsonString = JSON.toJSONString(user, objectWriter, WriterFeature.PrettyFormat);
        System.out.println("Custom Serialized JSON:");
        System.out.println(jsonString);
    }
}

输出

plain 复制代码
Custom Serialized JSON:
{
    "id": 2,
    "name": "Bob",
    "email": "bob@example.com"
}

使用注解控制序列化行为

Fastjson2 支持通过注解来控制序列化和反序列化的行为,例如 @JSONField 注解。

java 复制代码
import com.alibaba.fastjson2.annotation.JSONField;

public class User {
    private Long id;

    @JSONField(name = "username")
    private String name;
    private String email;

    // 构造函数、Getter、Setter和toString方法同上
}

序列化后的 JSON:

plain 复制代码
{"id":3,"username":"Charlie","email":"charlie@example.com"}

处理集合和泛型

Fastjson2 可以高效地处理集合类型和带有泛型的类

java 复制代码
import com.alibaba.fastjson2.JSON;
import java.util.List;
import java.util.ArrayList;

public class CollectionExample {
    public static void main(String[] args) {
        List<User> users = new ArrayList<>();
        users.add(new User(1L, "Alice", "alice@example.com"));
        users.add(new User(2L, "Bob", "bob@example.com"));

        String jsonString = JSON.toJSONString(users);
        System.out.println("Serialized List:");
        System.out.println(jsonString);

        List<User> deserializedUsers = JSON.parseArray(jsonString, User.class);
        System.out.println("Deserialized List:");
        deserializedUsers.forEach(System.out::println);
    }
}

输出:

plain 复制代码
Serialized List:
[{"id":1,"name":"Alice","email":"alice@example.com"},{"id":2,"name":"Bob","email":"bob@example.com"}]
Deserialized List:
User{id=1, name='Alice', email='alice@example.com'}
User{id=2, name='Bob', email='bob@example.com'}

使用流式 API 进行大数据处理

Fastjson2 支持流式 API,可以高效地处理大规模 JSON 数据,减少内存消耗。

java 复制代码
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import java.io.FileReader;
import java.io.IOException;

public class StreamingExample {
    public static void main(String[] args) throws IOException {
        try (JSONReader reader = JSON.createReader(new FileReader("users_large.json"))) {
            reader.startArray();
            while (reader.hasNext()) {
                User user = reader.readObject(User.class);
                // 处理每个用户对象
                System.out.println(user);
            }
            reader.endArray();
        }
    }
}

JSONB 支持

JSONB(Binary JSON)是一种二进制格式的 JSON 数据表示方式。与传统的文本格式 JSON 相比,JSONB 格式有几个优势:

  1. 更高的解析速度:二进制格式减少了解析过程中的字符转换,提高了解析效率。
  2. 更小的数据体积:通过压缩和优化存储结构,JSONB 通常比同等的文本 JSON 更小。
  3. 更高的查询效率:适用于需要频繁查询和处理大规模 JSON 数据的场景。

JSON 插入速度快、查询速度慢,是因为处理函数必须在每次执行时重新解析该数据。JSONB 插入速度慢、查询速度快,原因是 JSONB 数据被存储在一种分解好的二进制格式中,因为需要做附加的转换,它在输入时要稍慢一些。但因为不需要重新解析,JSONB 在查询数据时快很多

java 复制代码
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONB;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class SerializeToJSONBExample {
    public static void main(String[] args) throws IOException {
        User user = new User(1L, "Alice", "alice@example.com");
        
        // 将对象序列化为 JSONB 字节数组
        byte[] jsonbBytes = JSONB.toBytes(user);
        
        // 将 JSONB 写入文件
        Files.write(Paths.get("user.jsonb"), jsonbBytes);
        
        System.out.println("Serialized JSONB:");
        for(byte b : jsonbBytes){
            System.out.printf("0x%02X ", b);
        }
    }
}

输出:

plain 复制代码
Serialized JSONB:
0x5B 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x02 0x00 0x00 0x00 0x05 0x69 0x64 0xFFFF 0x00 0x00 0x00 0x08 0x41 0x6C 0x69 0x63 0x65 0x00 0x00 0x13 0x61 0x6C 0x69 0x63 0x65 0x40 0x65 0x78 0x61 0x6D 0x70 0x6C 0x65 0x2E 0x63 0x6F 0x6D

反序列化 JSONB 为 Java 对象

java 复制代码
import com.alibaba.fastjson2.JSONB;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class DeserializeFromJSONBExample {
    public static void main(String[] args) throws IOException {
        // 读取 JSONB 字节数组
        byte[] jsonbBytes = Files.readAllBytes(Paths.get("user.jsonb"));

        // 将 JSONB 字节数组反序列化为 User 对象
        User user = JSONB.parseObject(jsonbBytes, User.class);

        System.out.println("Deserialized User Object:");
        System.out.println(user);
    }
}

输出:

plain 复制代码
Deserialized User Object:
User{id=1, name='Alice', email='alice@example.com'}

JSONPath 支持

JSONPath 是一种用于从 JSON 数据中提取数据的查询语言,类似于 XPath 用于 XML。通过 JSONPath 可以使用表达式来轻松访问和解析 JSON 文档中的数据,从而实现复杂的数据查询和过滤。

java 复制代码
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONPath;

public class JSONPathExample {
    public static void main(String[] args) {
        String json = """
            {
              "store": {
                "book": [
                  { "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 },
                  { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 },
                  { "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "price": 8.99 }
                ],
                "bicycle": { "color": "red", "price": 19.95 }
              }
            }
        """;

        Object document = JSON.parse(json);
        // 提取所有书籍的价格
        Object prices = JSONPath.eval(document, "$.store.book[*].price");

        System.out.println(prices); // 输出: [8.95, 12.99, 8.99]
    }
}

Java 13 引入了文本块(Text Blocks)功能,这使得编写多行字符串变得更加简单。可以用三个双引号 """ 来定义多行字符串,这样可以避免在每一行末尾添加 + 号,并且可以更好地维护和阅读长字符串。

安全控制

随着 Fastjson 的流行,暴露出一些安全性和扩展性的问题。Fastjson2 致力于在保持高性能的同时,通过多项措施增强了库的安全防护能力

  • Fastjson 默认启用了自动类型解析,这导致了多个严重的安全漏洞。而 Fastjson2 默认关闭了自动类型解析,避免了潜在的安全风险。开发者需要显式地启用此功能并严格控制允许的类型。
  • 在需要启用自动类型解析的情况下,Fastjson2 提供了白名单机制,仅允许指定的类进行反序列化。通过设置白名单,开发者可以限制反序列化过程中的可控类,防止恶意代码执行。
  • Fastjson2 强化了对输入 JSON 数据的验证,避免恶意构造的数据导致反序列化漏洞。通过严格的语法检查和类型验证,减少了潜在的安全隐患。
  • Fastjson2 提供了多种安全配置选项,允许开发者根据具体需求灵活调整库的行为

为了最大限度地利用 Fastjson2 的安全特性,开发者应遵循以下最佳实践:

1. 禁用自动类型解析,除非必要

默认情况下,Fastjson2 已禁用自动类型解析,开发者应保持这一配置,只有在确实需要反序列化特定类型时,才启用并严格控制允许的类型。

java 复制代码
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.parser.ParserConfig;

public class Fastjson2SecurityConfig {
    public static void main(String[] args) {
        String jsonString = "{"id":1,"name":"Alice","email":"alice@example.com"}";

        // 默认情况下,自动类型解析是关闭的
        User user = JSON.parseObject(jsonString, User.class);
        System.out.println(user);
    }
}

2. 配置白名单机制

在需要启用自动类型解析的情况下,务必配置白名单,仅允许可信的类进行反序列化。

java 复制代码
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.parser.ParserConfig;

public class Fastjson2WhitelistExample {
    public static void main(String[] args) {
        // 配置 ParserConfig 并设置白名单
        ParserConfig parserConfig = ParserConfig.getGlobalInstance();
        parserConfig.addAccept("com.example.model."); // 仅允许反序列化指定包下的类

        String jsonString = "{"@type":"com.example.model.User","id":2,"name":"Bob","email":"bob@example.com"}";

        User user = JSON.parseObject(jsonString, User.class, parserConfig);
        System.out.println(user);
    }
}

3. 严格输入数据的验证和清洗

在反序列化之前,对输入的 JSON 数据进行严格的验证和清洗,确保数据的合法性和安全性。

java 复制代码
public boolean isValidJson(String json) {
    try {
        JSON.parse(json);
        return true;
    } catch (JSONException e) {
        return false;
    }
}

if (isValidJson(inputJson)) {
    User user = JSON.parseObject(inputJson, User.class);
    // 处理用户对象
} else {
    // 处理无效的 JSON 数据
}

4. 限制反序列化的类和包

通过严格配置反序列化的类和包,防止未经授权的类被反序列化,降低安全风险。

java 复制代码
ParserConfig parserConfig = new ParserConfig();
parserConfig.addAccept("com.company.project.model.");

String jsonString = "{"@type":"com.company.project.model.User","id":10,"name":"Jane","email":"jane@example.com"}";
User user = JSON.parseObject(jsonString, User.class, parserConfig);
System.out.println(user);

通过以上措施,可以在享受 Fastjson2 高性能和丰富功能的同时,确保应用程序的数据处理过程安全可靠,防范潜在的安全威胁和攻击。

相关推荐
yngsqq2 小时前
c# —— StringBuilder 类
java·开发语言
Asthenia04123 小时前
浏览器缓存机制深度解析:电商场景下的性能优化实践
后端
星星点点洲3 小时前
【操作幂等和数据一致性】保障业务在MySQL和COS对象存储的一致
java·mysql
xiaolingting3 小时前
JVM层面的JAVA类和实例(Klass-OOP)
java·jvm·oop·klass·instanceklass·class对象
风口上的猪20153 小时前
thingboard告警信息格式美化
java·服务器·前端
databook4 小时前
『Python底层原理』--Python对象系统探秘
后端·python
追光少年33224 小时前
迭代器模式
java·迭代器模式
超爱吃士力架5 小时前
MySQL 中的回表是什么?
java·后端·面试
扣丁梦想家5 小时前
设计模式教程:装饰器模式(Decorator Pattern)
java·前端·装饰器模式
drebander5 小时前
Maven 构建中的安全性与合规性检查
java·maven