含#字符的文档ID触发OJAI解码异常,寻求替代OJAI的迁移方案
移除OJAI库的代码迁移方案及问题解决
原有代码及问题
我有一段使用OJAI库转换并存储JSON文档的旧代码,示例如下:
import org.ojai.Document; public Object get(String documentId) { final Document dto = getDocument(documentId); return convert(dto); } // 省略其他代码 public Document getDocument(String documentId) { return Json.newDocument(documentId); } // 省略其他代码 final ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setSerializationInclusion(Include.NON_NULL); objectMapper.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true); objectMapper.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true); objectMapper.convertValue(document, CarDto.class);
运行时触发如下错误:
org.ojai.exceptions.DecodingException: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('#' (code 35)): Expected space separating root-level values at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 6] at org.ojai.json.impl.JsonStreamDocumentReader.hasMoreTokens(JsonStreamDocumentReader.java:634) ~[ojai-3.0-mapr-1808.jar!/:3.0-mapr-1808]
推测是格式为12345#CAR#RED的文档ID导致报错,以下是完全移除OJAI库的迁移方案:
问题根源
Json.newDocument(documentId)会把传入的字符串当作JSON内容直接解析,但12345#CAR#RED并非合法JSON格式,OJAI底层调用Jackson解析时,因#是JSON根级非法字符触发语法错误。
迁移方案(彻底移除OJAI)
1. 移除OJAI相关依赖与代码
- 删除所有
org.ojai.Document类的导入语句 - 删除所有依赖OJAI Document对象的方法与逻辑,改用Jackson原生API处理JSON
2. 重构文档获取逻辑
根据原代码的实际业务场景,分两种情况调整:
场景1:从存储读取JSON内容
如果原getDocument是根据ID从存储(数据库/文件等)读取JSON,直接返回JSON字符串或字节流:
// 替换原有getDocument方法 public String getDocumentJson(String documentId) { // 替换为实际的存储读取逻辑,比如从数据库查询JSON字符串 return fetchJsonFromStorage(documentId); } public Object get(String documentId) { String jsonStr = getDocumentJson(documentId); return convert(jsonStr); } // 转换方法直接用Jackson解析JSON字符串 private CarDto convert(String jsonStr) throws IOException { final ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setSerializationInclusion(Include.NON_NULL); objectMapper.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true); objectMapper.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true); return objectMapper.readValue(jsonStr, CarDto.class); }
场景2:用文档ID构造业务对象
如果原Json.newDocument(documentId)是想把ID作为业务对象的一部分,直接构造DTO即可:
public CarDto get(String documentId) { // 从存储获取车辆数据,或直接构造DTO CarDto dto = fetchCarDataFromStorage(documentId); dto.setId(documentId); // 将文档ID设置到DTO对应字段 return dto; }
3. 确保特殊字符的JSON合法性
如果需要将含#的字符串作为JSON值,必须用Jackson生成合法的JSON字面量:
// 生成包含特殊字符ID的合法JSON String validJson = objectMapper.writeValueAsString(Map.of("id", documentId));
关键注意事项
- 彻底移除项目中OJAI相关的依赖包,避免类路径冲突
- 所有JSON处理统一使用Jackson API,保持逻辑一致性
- 针对含特殊字符的文档ID场景做专项测试,验证JSON解析与生成的合法性
内容的提问来源于stack exchange,提问作者Peter Penzov
相关产品推荐
相关产品推荐

