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

原因如下:
- 复杂性:JSON 的结构(对象、数组、嵌套、转义字符)非常复杂,用正则表达式来精确描述几乎是不可能的,会导致表达式极其臃肿且难以维护。
- 性能:复杂的正则表达式在处理大 JSON 字符串时性能会很差。
- 脆弱性:你的正则表达式很可能无法覆盖所有合法的 JSON 格式(包含不同 Unicode 字符、各种转义序列的情况),导致误判。
正确的做法是:
- 解析和验证:使用成熟的 JSON 库,如
org.json,Jackson,Gson,它们能正确处理所有边界情况,并且提供了强大的数据访问能力。 - 提取简单数据:如果你只是想从一个格式固定且简单的 JSON 字符串中提取少量信息,正则表达式可以作为“胶水代码”使用,但前提是你完全理解其风险。
使用 JSON 库(推荐)
这是处理 JSON 的标准、正确的方式。
使用 org.json 库 (轻量级)
添加 Maven 依赖:

<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 对象 这是最推荐的方式,因为它类型安全且代码清晰。
- 创建 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; } }

// Person.java
public class Person {
private String name;
private int age;
private boolean isStudent;
private List
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 和性能问题。
