
json标准不允许同一对象内存在重复键,java解析器会自动覆盖前值只保留最后一个;本文详解如何识别非法json结构、修正为合法格式,并用jackson安全读取全部statements数组。
json标准不允许同一对象内存在重复键,java解析器会自动覆盖前值只保留最后一个;本文详解如何识别非法json结构、修正为合法格式,并用jackson安全读取全部statements数组。
在实际开发中,你可能会遇到类似如下结构的“伪JSON”文本:
{
"statements": [ { "kpCustomLevel": { "region": "KNW", "statementType": "Medical", "coverageType": "DHMO" } } ],
"statements": [ { "kpCustomLevel": { "region": "KNP", "statementType": "Medical1", "coverageType": "DHMN" } } ]
}⚠️ 关键问题:这不是合法JSON!根据 RFC 8259,同一JSON对象中键必须唯一。当使用 ObjectMapper(如Jackson)解析时,第二个 "statements" 会完全覆盖第一个——最终仅返回单个数组(即后声明的那组数据),导致数据丢失。
✅ 正确的JSON结构应将所有语句合并为一个 "statements" 键,其值为包含多个元素的数组:
{
"statements": [
{
"kpCustomLevel": {
"region": "KNW",
"statementType": "Medical",
"coverageType": "DHMO"
}
},
{
"kpCustomLevel": {
"region": "KNP",
"statementType": "Medical1",
"coverageType": "DHMN"
}
}
]
}✅ Java 示例:使用 Jackson 安全读取全部 statements
首先定义对应POJO类(推荐使用 Lombok 简化代码):
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class StatementResponse {
@JsonProperty("statements")
private List<Statement> statements;
// getter only
public List<Statement> getStatements() {
return statements;
}
}
class Statement {
@JsonProperty("kpCustomLevel")
private KpCustomLevel kpCustomLevel;
public KpCustomLevel getKpCustomLevel() {
return kpCustomLevel;
}
}
class KpCustomLevel {
private String region;
private String statementType;
private String coverageType;
// getters
public String getRegion() { return region; }
public String getStatementType() { return statementType; }
public String getCoverageType() { return coverageType; }
}然后使用 Jackson 解析:
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.List;
public class JsonParserExample {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
// 假设 corrected.json 是已修复的合法JSON文件
StatementResponse response = mapper.readValue(
new File("corrected.json"),
StatementResponse.class
);
List<Statement> statements = response.getStatements();
System.out.println("共解析到 " + statements.size() + " 条statements记录");
for (int i = 0; i < statements.size(); i++) {
KpCustomLevel level = statements.get(i).getKpCustomLevel();
System.out.printf("[%d] region=%s, type=%s, coverage=%s%n",
i + 1,
level.getRegion(),
level.getStatementType(),
level.getCoverageType()
);
}
}
}? 如何发现并修复原始非法JSON?
- 验证工具:将原始内容粘贴至 JSONLint 或 VS Code 中,会明确报错 Duplicate key "statements"。
-
预处理建议(若无法修改源文件):
若上游系统持续输出非法JSON,可在Java中先用正则或字符串处理临时合并(不推荐长期依赖):// ⚠️ 仅作应急方案,非健壮做法 String raw = Files.readString(Paths.get("broken.json")); String fixed = raw.replaceAll("(?s)"statements"\s*:\s*\[[^\]]*\]\s*,?\s*"statements"\s*:", ""statements": [") .replaceAll("\}\s*,\s*\{", "}, {"); // 粗略合并,需按实际结构调整
✅ 最佳实践总结
- ? 源头治理:要求数据提供方输出符合RFC标准的JSON,确保键唯一性;
- ? 强校验:在解析前用 ObjectMapper.readTree() 尝试解析,捕获 JsonProcessingException 并告警;
- ? DTO设计:优先使用不可变POJO + @JsonProperty 显式绑定,避免字段名歧义;
- ? 避免依赖“解析器宽容模式”(如Jackson的 DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY),它无法解决重复键覆盖问题。
遵循以上规范,即可稳定、完整地提取全部 statements 数据,杜绝因JSON格式违规导致的静默数据丢失。


















