Gson支持toString格式而非JSON语法的原理及日期字段问题咨询
Gson解析非标准格式字符串的问题解答
我来帮你拆解这两个问题,刚好对Gson的这个“隐藏”特性比较熟悉:
1. Gson为何支持{a=5, b=field}这种非标准格式?
这其实是Gson**宽松解析(Lenient Mode)**的兼容特性——默认情况下,Gson创建的实例会自动开启宽松模式,它会容忍很多不符合JSON官方标准的写法,包括:
- 用等号
=代替冒号:分隔键值对 - 键名不包裹双引号
- 用单引号代替双引号包裹字符串
- 允许对象末尾多出来的逗号
官方文档没特意大篇幅讲这个,因为这属于边缘兼容场景,主要是为了适配Java实体类默认的toString()输出(很多类的toString就是{key=value, ...}这种格式),方便开发者在调试、快速转换时不用严格写标准JSON。
你可以试试关闭宽松模式,这时候再解析这种格式就会直接报错:
JsonReader reader = new JsonReader(new StringReader(json2)); reader.setLenient(false); // 关闭宽松模式 Pojo obj = gsonInstance.fromJson(reader, Pojo.class); // 抛出JsonSyntaxException
2. 如何解决toString格式下的日期字段解析问题?
当字段值里有冒号(比如日期2024-05-20 12:34:56)时,Gson的宽松解析会彻底混乱——它会把值里的冒号当成键值分隔符,导致无法正确识别字段值的边界。这里有两个实用的解决方案:
方案一:转成标准JSON再解析(简单高效)
用正则把非标准格式转换为符合规范的JSON,核心是给键加双引号、把等号换成冒号、给字符串类型的值(包括日期)加双引号,同时保留数字的原始格式:
String nonStandardStr = "{a=5, b=field, date=2024-05-20 12:34:56}"; // 第一步:给键加双引号,替换等号为冒号 String temp = nonStandardStr.replaceAll("(\\w+)=", "\"$1\":"); // 第二步:给非数字的字段值加双引号 String standardJson = temp.replaceAll(":([^,}\\d]+)", ":\"$1\""); // 现在standardJson是标准JSON:{"a":5, "b":"field", "date":"2024-05-20 12:34:56"} Pojo obj = gsonInstance.fromJson(standardJson, Pojo.class);
这个方案适合字段值没有特殊符号(比如逗号、等号)的场景,对于固定格式的日期完全够用。
方案二:自定义TypeAdapter(灵活通用)
如果你的字段值可能包含逗号、等号这类特殊字符,正则就hold不住了,这时候可以写一个自定义的TypeAdapter手动解析:
public class PojoTypeAdapter extends TypeAdapter<Pojo> { @Override public void write(JsonWriter out, Pojo value) throws IOException { // 如果不需要序列化,直接抛出异常或留空 throw new UnsupportedOperationException("序列化功能未实现"); } @Override public Pojo read(JsonReader in) throws IOException { in.beginObject(); Pojo.PojoBuilder builder = Pojo.builder(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); while (in.hasNext()) { String key = in.nextName(); String value = in.nextString(); switch (key) { case "a": builder.a(Integer.parseInt(value)); break; case "b": builder.b(value); break; case "date": // 解析日期字段 LocalDateTime dateTime = LocalDateTime.parse(value, formatter); builder.date(Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant())); break; } } in.endObject(); return builder.build(); } }
然后把这个适配器注册到Gson实例中:
Gson gsonInstance = new GsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .registerTypeAdapter(Pojo.class, new PojoTypeAdapter()) .create();
这种方法能处理各种复杂的非标准格式,适合需要长期维护的业务场景。
内容的提问来源于stack exchange,提问作者nikel
相关产品推荐
相关产品推荐

