REST API POST请求JSON序列化失败问题求助
解决REST API POST请求的反序列化异常问题
问题背景
有位开发者在通过Python的requests库提交POST请求到Traccar的REST API时遇到了反序列化异常,相关代码和报错信息如下:
原执行代码
def Actions(self, imei, name): global addAttribute if addAttribute == True: attributes = {"decoder.timezone":"Etc/GMT"} else: attributes = "" url = "https://example.com/api/devices/" data = {"name":name, "uniqueId":imei, "attributes":attributes} print("data not transformed:") print(data) print() data_json = json.dumps(data) print("data transformed by json.dumps():") print(data_json) print() test = requests.post(url, auth=('mylogin', 'pwd'), json=data_json) print(test.content)
控制台报错输出
data not transformed: {'name': 'Example 1', 'uniqueId': 'Example 1', 'attributes': {'decoder.timezone': 'Etc/GMT'}} data transformed by json.dumps(): {"name": "Example 1", "uniqueId": "Example 1", "attributes": {"decoder.timezone": "Etc/GMT"}} b'Cannot construct instance of `org.traccar.model.Device` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value (\'{"name": "Example 1", "uniqueId": "Example 1", "attributes": {"decoder.timezone": "Etc/GMT"}}\')\n at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 1]'
问题根源
这个错误其实很典型,核心问题出在requests.post的参数使用上:
requests库的json参数接受的是Python字典对象,它会自动帮你完成JSON序列化,同时设置Content-Type: application/json请求头。- 但你提前用
json.dumps()把字典转成了JSON字符串,再传给json参数,这就导致requests把这个字符串当成了普通数据再次序列化,服务器收到的是一个包裹着JSON字符串的字符串,而非预期的JSON对象,所以Traccar后端的Jackson序列化器无法将其解析成Device实体类。
另外还有个小坑:当addAttribute为False时,你给attributes赋值了空字符串"",但API预期的attributes应该是一个对象(字典)或null,空字符串会引发另一个序列化错误。
修正后的代码
直接去掉手动序列化的步骤,把Python字典传给json参数即可,同时修正attributes的默认值:
import json import requests def Actions(self, imei, name): global addAttribute if addAttribute: # 简化写法,无需写==True attributes = {"decoder.timezone":"Etc/GMT"} else: attributes = {} # 或者用None,根据API文档选择合适的默认值 url = "https://example.com/api/devices/" data = {"name":name, "uniqueId":imei, "attributes":attributes} print("data to be sent:") print(data) print() # 直接传字典给json参数,requests自动处理序列化和请求头 test = requests.post(url, auth=('mylogin', 'pwd'), json=data) print(test.content)
补充方案(不推荐但可了解)
如果你非要手动做JSON序列化,那应该用data参数传递序列化后的字符串,同时手动设置请求头:
data_json = json.dumps(data) headers = {"Content-Type": "application/json"} test = requests.post(url, auth=('mylogin', 'pwd'), data=data_json, headers=headers)
但显然直接用json=data更简洁,也不容易出错。
另外,如果API允许attributes字段缺失,也可以在addAttribute为False时不把attributes键加入data字典,这样更符合REST API的规范。
内容的提问来源于stack exchange,提问作者xtl
相关产品推荐
相关产品推荐

