使用存储过程向Cosmos DB写入数据时遇文档格式错误求助
问题排查:Azure Cosmos DB存储过程批量写入ChargeTransaction数据报错
错误信息
{"Errors":["Encountered exception while executing Javascript. Exception = Error: The document body must be an object or a string representing a JSON-serialized object.\r\nStack trace: Error: The document body must be an object or a string representing a JSON-serialized object.\n at createDocument (script.js:644:21)\n at tryCreate (script.js:20:9)\n at createToDoItems (script.js:15:5)\n at __docDbMain (script.js:37:5)\n at Global code (script.js:1:2)"]},"statusCode":400,"subStatusCode":400,"isGone":false,"isNotFound":false,"isInvalidPartition":false,"isThroughputControlRequestRateTooLarge":false,"requestCharge":3.45,"itemLSN":-1,"sessionToken":"0:-1#244878","backendLatencyInMs":2.457,"exceptionMessage":"[\"Encountered exception while executing Javascript. Exception = Error: The document body must be an object or a string representing a JSON-serialized object.\\r\\nStack trace: Error: The document body must be an object or a string representing a JSON-serialized object.\\n at createDocument (script.js:644:21)\\n at tryCreate (script.js:20:9)\\n at createToDoItems (script.js:15:5)\\n at __docDbMain (script.js:37:5)\\n at Global code (script.js:1:2)\"]"
相关代码
Java调用代码
List<ChargeTransaction> chargeTransactionList = new ArrayList<>(); for (ChargeTransaction chargeTransaction : getChargeTransactions(event, chargeTransactionList)) { list.add(chargeTransaction); } CosmosStoredProcedureProperties definition; try { ClassPathResource resource = new ClassPathResource("createEvent.js"); InputStream inputStream = resource.getInputStream(); String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8); definition = new CosmosStoredProcedureProperties( "spCreateEvent", result ); CosmosStoredProcedureResponse response = container .getScripts() .createStoredProcedure(definition); CosmosStoredProcedure sproc = container .getScripts() .getStoredProcedure("spCreateEvent"); CosmosStoredProcedureRequestOptions options = new CosmosStoredProcedureRequestOptions(); options.setPartitionKey( new PartitionKey(event.getEventPayload().getOriginOrderNo()) ); CosmosStoredProcedureResponse response = sproc.execute( list, options ); System.out.println(response.getStatusCode()); }catch(Exception e) { e.printStackTrace(); }
存储过程代码(createEvent.js)
function createToDoItems(items) { var collection = getContext().getCollection(); var collectionLink = collection.getSelfLink(); var count = 0; if (!items) throw new Error("The array is undefined or null."); var numItems = items.length; if (numItems == 0) { getContext().getResponse().setBody(0); return; } tryCreate(items[count], callback); function tryCreate(item, callback) { var options = { disableAutomaticIdGeneration: false }; var isAccepted = collection.createDocument(collectionLink, item, options, callback); if (!isAccepted) getContext().getResponse().setBody(count); } function callback(err, item, options) { if (err) throw err; count++; if (count >= numItems) { getContext().getResponse().setBody(count); } else { tryCreate(items[count], callback); } } }
问题根因
错误提示明确指出文档主体必须是对象或JSON序列化对象的字符串,问题出在Java代码向存储过程传递参数的环节:直接传递List<ChargeTransaction>对象列表时,Cosmos DB Java SDK没有将其正确序列化为存储过程可识别的JSON对象数组,导致存储过程中items参数不是合法的JSON结构,调用collection.createDocument时触发校验错误。
解决方案
1. 修改Java代码:将对象序列化为JSON字符串传递
使用JSON序列化库(如Jackson)将ChargeTransaction列表转换为JSON数组字符串,再传递给存储过程。
修改后的Java关键代码:
// 导入Jackson的ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper; // ... 原有代码 ... // 将ChargeTransaction列表序列化为JSON字符串 ObjectMapper objectMapper = new ObjectMapper(); String itemsJson = objectMapper.writeValueAsString(list); // 调用存储过程时传入JSON字符串 CosmosStoredProcedureResponse response = sproc.execute( itemsJson, options );
2. 修改存储过程:解析JSON字符串为对象数组
存储过程接收JSON字符串后,先解析为对象数组再进行批量写入操作。
修改后的createEvent.js:
function createToDoItems(itemsJson) { var collection = getContext().getCollection(); var collectionLink = collection.getSelfLink(); var count = 0; if (!itemsJson) throw new Error("The JSON string is undefined or null."); // 解析JSON字符串为对象数组 var items = JSON.parse(itemsJson); var numItems = items.length; if (numItems == 0) { getContext().getResponse().setBody(0); return; } tryCreate(items[count], callback); function tryCreate(item, callback) { var options = { disableAutomaticIdGeneration: false }; var isAccepted = collection.createDocument(collectionLink, item, options, callback); if (!isAccepted) getContext().getResponse().setBody(count); } function callback(err, item, options) { if (err) throw err; count++; if (count >= numItems) { getContext().getResponse().setBody(count); } else { tryCreate(items[count], callback); } } }
额外注意事项
- 确保
ChargeTransaction类的字段都能被Jackson正确序列化(如无私有字段未提供getter,无循环引用等)。 - 存储过程创建时需确保使用正确的分区键上下文,本次调用已设置
PartitionKey,需保证所有ChargeTransaction对象的分区键字段值与调用时的OriginOrderNo一致,避免跨分区写入错误。
内容的提问来源于stack exchange,提问作者Loren
相关产品推荐
相关产品推荐

