MongoDB GridFS存储大对象及Python提取异常问题排查
Java序列化日志到GridFS后,Python解析JSON失败
背景
每日生成外部REST调用的应用日志,需解析提取StartTime、响应EndTime、payload等信息,按会话整理后生成瀑布图可视化各调用耗时。单个会话的整理数据超过16MB,因此采用GridFS存储序列化后的自定义REST调用对象。
问题
在Jupyter Notebook中用Python提取数据时,返回内容带有String 7e0000前缀及大量转义斜杠,导致json.loads解析失败。
Python提取代码及输出
提取代码
import javaobj.v2 as javaobj pObj = javaobj.loads(fs.get(record['calls']).read()) pObj.dump()
输出示例
'[String 7e0000: \'[{"tokenId":"a6fc52b3-0c60-4332-b866-09f3fe8b243f","responseTime":540.2587,"requestPayload":null,"responsePayload":"{\\"Identifiers\\":[{\\"Name\\":\\"XXXX MANAGEMENT LLC-CISA\\",\\"Number\\":\\"0012345\\",\\"SubNumber\\":\\"00099\\",\\"Sourceode\\":null,\\"SourceGroups\\":null,\\"IsPrimary\\":false}],\\"serviceConsumerId\\":\\"YYY\\",\\"serviceConsumerSystemId\\":\\"001\\",\\"service ....
Java端序列化及存储代码
自定义对象转String方法(未实际使用)
public static String convertToString(ArrayList<LogStructure> logs){ StringBuilder sBuilder = new StringBuilder(); sBuilder.append("["); for (LogStructure log: logs){ sBuilder.append(getString(log)); sBuilder.append(","); } return sBuilder.toString(); } public static String getString(LogStructure log){ StringBuilder sBuilder = new StringBuilder(); sBuilder.append("{logLevel:"); sBuilder.append(log.logLevel); sBuilder.append(",logInjestionDateTime:"); sBuilder.append(log.logInjestionDateTime); sBuilder.append(",serverName:"); sBuilder.append(log.serverName); sBuilder.append(",serviceName:"); sBuilder.append(log.serviceName); sBuilder.append(",serviceEndPoint:"); sBuilder.append(log.serviceEndPoint); sBuilder.append(",startTime:"); sBuilder.append(log.startTime); sBuilder.append(",endTime:"); sBuilder.append(log.endTime); sBuilder.append(",responseTime:"); sBuilder.append(log.responseTime); sBuilder.append(",message:"); sBuilder.append(log.message); sBuilder.append(",endTime:"); sBuilder.append(log.endTime); sBuilder.append(",requestId:"); sBuilder.append(log.requestId); sBuilder.append(",tokenId:"); sBuilder.append(log.tokenId); sBuilder.append(",userId:"); sBuilder.append(log.userId); sBuilder.append(",requestPayload:"); sBuilder.append(log.requestPayload); sBuilder.append(",responsePayload:"); sBuilder.append(log.responsePayload); sBuilder.append(",payloadType:"); sBuilder.append(log.payloadType); sBuilder.append("}"); return sBuilder.toString(); }
上传至GridFS代码(当前使用)
JSONArray list = new JSONArray(); for(LogStructure temp: tempObject){ totalCalls++; JSONObject obj = new JSONObject(); obj.put("logLevel", temp.logLevel); obj.put("logInjestionDateTime", temp.logInjestionDateTime); obj.put("serverName", temp.serverName); obj.put("serviceName", temp.serviceName); obj.put("serviceEndPoint", temp.serviceEndPoint); obj.put("startTime", temp.startTime); obj.put("endTime", temp.endTime); obj.put("responseTime", temp.responseTime); obj.put("message", temp.message); obj.put("requestId", temp.requestId); obj.put("tokenId", temp.tokenId); obj.put("requestPayload", temp.requestPayload); obj.put("responsePayload", temp.responsePayload); obj.put("payloadType", temp.payloadType); list.add(obj); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos; try { oos = new ObjectOutputStream(baos); oos.writeObject(list.toJSONString()); oos.flush(); oos.close(); } catch (IOException e) { e.printStackTrace(); } InputStream is = new ByteArrayInputStream(baos.toByteArray()); GridFSBucket gridFSBucket = mongo.getGridFsBucket(mDb, "Logs"); ObjectId objectId = mongo.uploadToGrid(is, gridFSBucket); doc.append("totalCalls", totalCalls); doc.append("calls", objectId); mongo.insertDocument(mCollection, doc);
已尝试的方案
- 直接序列化ArrayList:Python提取时出现
System.*.ArrayList标识,无法解析 - 改用simple.json存储JsonObject:默认序列化后出现内存地址,无效
- 使用
toJSONString转成字符串存储:仍出现解析异常
问题原因分析
- 核心错误:Java端将
list.toJSONString()生成的标准JSON字符串,通过ObjectOutputStream.writeObject()做了Java对象序列化,导致存储到GridFS的是Java序列化后的String对象,而非原始JSON文本。 - Python用
javaobj读取后得到的是Java的String实例,因此输出带有String 7e0000这类Java对象标识,同时字符串内部的转义符被双重转义,导致json.loads无法识别。
解决方案
步骤1:修改Java端存储逻辑,直接存储原始JSON字符串
无需使用Java对象序列化,直接将JSON字符串写入GridFS:
JSONArray list = new JSONArray(); // 省略构建JSONArray的代码... String jsonStr = list.toJSONString(); InputStream is = new ByteArrayInputStream(jsonStr.getBytes(StandardCharsets.UTF_8)); GridFSBucket gridFSBucket = mongo.getGridFsBucket(mDb, "Logs"); ObjectId objectId = mongo.uploadToGrid(is, gridFSBucket); doc.append("totalCalls", totalCalls); doc.append("calls", objectId); mongo.insertDocument(mCollection, doc);
步骤2:Python端直接读取JSON字符串并解析
无需使用javaobj库,直接读取GridFS中的原始文本:
# 读取GridFS中的原始JSON内容 json_data = fs.get(record['calls']).read().decode('utf-8') # 解析JSON data = json.loads(json_data)
步骤3:处理残留转义问题(如果存在)
如果responsePayload这类字段内部还有转义的JSON字符串,可以二次解析:
for item in data: if item['responsePayload'] is not None: item['responsePayload'] = json.loads(item['responsePayload'])
内容的提问来源于stack exchange,提问作者Tushar Saurabh
相关产品推荐
相关产品推荐

