杰瑞科技汇

java json正则表达式

不要用正则解析复杂 JSON

最重要的一点是:正则表达式不适合用来解析或验证复杂的、嵌套的 JSON 结构。

java json正则表达式-图1
(图片来源网络,侵删)

原因如下:

  1. 复杂性:JSON 的结构(对象、数组、嵌套、转义字符)非常复杂,用正则表达式来精确描述几乎是不可能的,会导致表达式极其臃肿且难以维护。
  2. 性能:复杂的正则表达式在处理大 JSON 字符串时性能会很差。
  3. 脆弱性:你的正则表达式很可能无法覆盖所有合法的 JSON 格式(包含不同 Unicode 字符、各种转义序列的情况),导致误判。

正确的做法是:

  • 解析和验证:使用成熟的 JSON 库,如 org.json, Jackson, Gson,它们能正确处理所有边界情况,并且提供了强大的数据访问能力。
  • 提取简单数据:如果你只是想从一个格式固定且简单的 JSON 字符串中提取少量信息,正则表达式可以作为“胶水代码”使用,但前提是你完全理解其风险。

使用 JSON 库(推荐)

这是处理 JSON 的标准、正确的方式。

使用 org.json 库 (轻量级)

添加 Maven 依赖:

java json正则表达式-图2
(图片来源网络,侵删)
<dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20251013</version> <!-- 使用最新版本 -->
</dependency>

示例:解析 JSON 并提取数据

import org.json.JSONObject;
import org.json.JSONArray;
public class JsonOrgExample {
    public static void main(String[] args) {
        String jsonString = "{\"name\":\"John Doe\",\"age\":30,\"isStudent\":false,\"courses\":[\"History\",\"Math\"],\"address\":{\"street\":\"123 Main St\",\"city\":\"New York\"}}";
        try {
            // 解析整个 JSON 字符串为一个 JSONObject
            JSONObject jsonObject = new JSONObject(jsonString);
            // 获取简单值
            String name = jsonObject.getString("name");
            int age = jsonObject.getInt("age");
            System.out.println("Name: " + name);
            System.out.println("Age: " + age);
            // 获取数组
            JSONArray courses = jsonObject.getJSONArray("courses");
            System.out.print("Courses: ");
            for (int i = 0; i < courses.length(); i++) {
                System.out.print(courses.getString(i) + " ");
            }
            System.out.println();
            // 获取嵌套对象
            JSONObject address = jsonObject.getJSONObject("address");
            String city = address.getString("city");
            System.out.println("City: " + city);
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Invalid JSON string!");
        }
    }
}

使用 Jackson 库 (功能强大,业界常用)

添加 Maven 依赖:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version> <!-- 使用最新版本 -->
</dependency>

示例:将 JSON 映射到 Java 对象 这是最推荐的方式,因为它类型安全且代码清晰。

  1. 创建 Java 类 (POJO)
    // Course.java
    public class Course {
    private String name;
    // Getters and Setters
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    @Override
    public String toString() { return name; }
    }

// Address.java public class Address { private String street; private String city; // Getters and Setters public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Override public String toString() { return street + ", " + city; } }

java json正则表达式-图3
(图片来源网络,侵删)

// Person.java public class Person { private String name; private int age; private boolean isStudent; private List courses; private Address address; // Getters and Setters public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public boolean isStudent() { return isStudent; } public void setStudent(boolean student) { isStudent = student; } public List getCourses() { return courses; } public void setCourses(List courses) { this.courses = courses; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", isStudent=" + isStudent + ", courses=" + courses + ", address=" + address + '}'; } }


2.  **使用 Jackson 进行解析**:
```java
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonExample {
    public static void main(String[] args) {
        String jsonString = "{\"name\":\"John Doe\",\"age\":30,\"isStudent\":false,\"courses\":[{\"name\":\"History\"},{\"name\":\"Math\"}],\"address\":{\"street\":\"123 Main St\",\"city\":\"New York\"}}";
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            // 将 JSON 字符串直接映射到 Person 对象
            Person person = objectMapper.readValue(jsonString, Person.class);
            // 现在你可以像操作普通 Java 对象一样操作 person
            System.out.println(person);
            System.out.println("City: " + person.getAddress().getCity());
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Invalid JSON string!");
        }
    }
}

使用正则表达式(仅限特定简单场景)

如果你确定你的 JSON 非常简单,并且你只想做简单的提取或替换,可以考虑正则表达式。

提取单个键的值

目标:从 {"name":"John Doe", "age":30} 中提取 name 的值 "John Doe"

正则表达式

"name"\s*:\s*"([^"]*)"
  • "name": 匹配键名 "name"。
  • \s*: 匹配键名后的任意数量的空白字符(包括空格、制表符、换行符)。
  • 匹配冒号。
  • \s*: 匹配冒号后的任意数量的空白字符。
  • "([^"]*)":
    • 匹配值的开始引号。
    • ([^"]*): 这是一个捕获组[] 表示字符集,^" 表示“除了双引号之外的任何字符”, 表示零次或多次,所以它匹配引号内的所有内容,直到遇到下一个引号。
    • 匹配值的结束引号。

Java 代码示例

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JsonRegexSimple {
    public static void main(String[] args) {
        String jsonString = "{\"name\":\"John Doe\", \"age\":30}";
        String regex = "\"name\"\\s*:\\s*\"([^\"]*)\"";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(jsonString);
        if (matcher.find()) {
            // group(0) 是整个匹配的字符串,group(1) 是第一个捕获组
            String nameValue = matcher.group(1);
            System.out.println("Found name: " + nameValue); // 输出: Found name: John Doe
        } else {
            System.out.println("Name not found.");
        }
    }
}

提取数字类型的值

目标:从 {"name":"John", "age":30} 中提取 age 的值 30

正则表达式

"age"\s*:\s*(\d+)
  • (\d+): \d 匹配任何数字(0-9), 表示一次或多次,这会捕获一个整数。

Java 代码示例

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JsonRegexNumber {
    public static void main(String[] args) {
        String jsonString = "{\"name\":\"John\", \"age\":30}";
        String regex = "\"age\"\\s*:\\s*(\\d+)";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(jsonString);
        if (matcher.find()) {
            String ageValue = matcher.group(1);
            System.out.println("Found age: " + ageValue); // 输出: Found age: 30
        } else {
            System.out.println("Age not found.");
        }
    }
}

更健壮的键值提取(处理引号转义)

JSON 字符串中可能包含转义引号,{"message":"He said \"Hello\"."},上面的简单正则就会失败。

更健壮的正则表达式

"([^"\\]|\\.)*"\s*:\s*"([^"\\]|\\.)*"
  • ([^"\\]|\\.)*: 这个模式匹配一个不被引号或反斜杠中断的字符序列,或者匹配一个转义字符(如 \"\\)。
    • [^"\\]: 匹配任何不是双引号或反斜杠的字符。
    • 或者。
    • \\.: 匹配一个反斜杠后跟任意字符,即一个转义序列。
    • 重复零次或多次。

这个正则表达式非常复杂,并且仍然可能无法处理所有情况,这进一步证明了使用 JSON 库的必要性


总结与最佳实践

场景 推荐工具 原因
解析 JSON Jackson, Gson, org.json 唯一正确的方式,能处理所有标准、嵌套、转义等情况,安全、高效、易于维护。
验证 JSON 格式 Jackson, Gson, org.json 让库尝试解析,如果抛出异常,则说明格式无效,这是最可靠的验证方法。
从简单、固定的 JSON 中提取少量数据 正则表达式 可以作为快速“胶水代码”,但必须明确其局限性,并确保输入格式绝对可控。
修改或替换 JSON 中的特定部分 正则表达式 同上,仅适用于简单场景,对于复杂修改,解析成对象 -> 修改 -> 再序列化是更好的选择。

最终建议:除非你有一个非常非常简单且永远不会改变的 JSON 处理需求,否则请始终使用专业的 JSON 库,它们是为此而生的,能让你避免无数的潜在 bug 和性能问题。

分享:
扫描分享到社交APP
上一篇
下一篇