Python 3.9环境下使用POST请求发送JSON数据时出现JSONDecodeError问题求助
先帮你定位下错误根源:你看到的JSONDecodeError: Expecting value: line 1 column 1 (char 0),本质是你尝试解析一个空的响应内容。当Callminer服务器返回的响应没有JSON数据(比如返回204 No Content状态码,或者响应体为空)时,调用response.json()就会触发这个错误——因为没有任何可解析的JSON值。
另外,我注意到你代码里的json.loads(metadata_body)步骤也有潜在问题:你打印的内容是Python字典的字符串表示(用单引号包裹键和值),但标准JSON要求必须用双引号。如果metadata_body就是这种单引号格式的字符串,json.loads()其实会直接报错,只是当前的响应解析错误先触发了。
下面是具体的解决方案和修改后的代码:
核心修复点
1. 先处理HTTP请求状态,再安全解析响应JSON
不要直接调用response.json(),应该先检查请求是否成功,再判断响应体是否非空,最后用异常处理包裹JSON解析逻辑:
- 用
response.raise_for_status()主动抛出HTTP错误(比如4xx/5xx状态码),提前捕获请求失败的情况。 - 先判断
response.text是否非空,再尝试解析JSON,避免空内容导致的解码错误。
2. 修正metadata_body的处理逻辑
如果self.__fill_metadata()返回的是Python字典列表,你完全不需要把它转成字符串再用json.loads()解析回来——requests.post的json参数可以直接接收字典,它会自动帮你转成标准JSON格式发送。
如果metadata_body确实是单引号格式的字符串(比如你打印的那种),不要用json.loads(),改用ast.literal_eval()来解析Python风格的字典字符串(这个方法支持单引号)。
修改后的完整代码
import json import ast import requests from requests.structures import CaseInsensitiveDict from base64 import b64encode # 你的其他变量定义(CONSUMER_USERNAME、CONSUMER_PASSWORD等) consumer_identifiers = bytes(f"{CONSUMER_USERNAME}:{CONSUMER_PASSWORD}", encoding='utf-8') headers: requests.structures.CaseInsensitiveDict = CaseInsensitiveDict() headers["Basic-Authorization"] = f"Basic {b64encode(consumer_identifiers).decode('ascii')}" headers["Authorization"] = f"Bearer {self.__token['access_token']}" metadata_body_list = self.__fill_metadata() logger.info("Start send metadata to Callminer...") logger.info(f"The number of metadata sent is : {len(metadata_body_list)}") assert METADATA_INGESTION_URL is not None for metadata_body in metadata_body_list: # 情况1:如果fill_metadata返回的是Python字典,直接使用 json_metadata_body = metadata_body # 情况2:如果metadata_body是单引号格式的字符串,启用下面这行替换上面的内容 # json_metadata_body = ast.literal_eval(metadata_body) try: response = requests.post(METADATA_INGESTION_URL, headers=headers, json=json_metadata_body) # 先检查HTTP状态码,触发4xx/5xx错误 response.raise_for_status() # 安全解析响应JSON response_json = None if response.text.strip(): try: response_json = response.json() except json.JSONDecodeError: logger.warning("Server returned non-JSON response body") if response_json: # 注意:这里的CorrelationId首字母大写,你打印的字典里是Correlationid(小写id),要匹配服务器返回的字段名 if "CorrelationId" in response_json and "MiningId" in response_json and \ response_json["CorrelationId"] in str(metadata_body): logger.debug(f"Response POST request {response_json}") else: raise RuntimeError( f"Error occurred when sending metadata to Callminer.\n Response: {response_json}.") else: logger.debug("Received valid empty response from Callminer") except requests.exceptions.HTTPError as e: raise RuntimeError(f"HTTP Error occurred: {str(e)}") except Exception as e: raise RuntimeError(f"Unexpected error: {str(e)}") logger.info("End of sending metadata.")
额外提示
我注意到你打印的请求体里是Correlationid(小写id),但代码里判断的是response_json["CorrelationId"](大写Id),要确保字段名和服务器返回的完全一致,避免误判错误。
内容的提问来源于stack exchange,提问作者LilyAZ

