From 0035352ff45bff7a01ea5cdcd0105920cf3c34fa Mon Sep 17 00:00:00 2001 From: Andrey Lebedev Date: Wed, 8 Oct 2025 09:48:44 +0100 Subject: [PATCH] Add json_safe method to Response class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new json_safe() method that provides graceful error handling when parsing JSON responses. This method offers several advantages over the standard json() method: - Returns a configurable default value instead of raising exceptions - Optionally checks HTTP status before parsing (raise_for_status parameter) - Handles empty responses gracefully - Catches JSONDecodeError, UnicodeDecodeError, and ValueError This is particularly useful for: - APIs that may return malformed JSON - Scenarios requiring default fallback values - Graceful degradation in error conditions - Rate limiting and service unavailability handling Example usage: data = response.json_safe(default={}) data = response.json_safe(default=[], raise_for_status=False) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- httpx/_models.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/httpx/_models.py b/httpx/_models.py index 2cc86321a4..257e43ab88 100644 --- a/httpx/_models.py +++ b/httpx/_models.py @@ -831,6 +831,67 @@ def raise_for_status(self) -> Response: def json(self, **kwargs: typing.Any) -> typing.Any: return jsonlib.loads(self.content, **kwargs) + def json_safe( + self, + default: typing.Any = None, + *, + raise_for_status: bool = True, + **kwargs: typing.Any, + ) -> typing.Any: + """ + Safely parse JSON response content with error handling. + + Unlike the standard `json()` method, this method provides graceful error + handling for common failure cases when parsing JSON responses. + + Args: + default: Value to return if JSON parsing fails or response is empty. + Defaults to None. + raise_for_status: If True (default), raises HTTPStatusError for 4xx/5xx + responses before attempting to parse JSON. If False, attempts + to parse JSON regardless of status code. + **kwargs: Additional arguments passed to json.loads() + + Returns: + The parsed JSON data, or the default value if parsing fails. + + Example: + >>> response = httpx.get("https://api.example.com/data") + >>> data = response.json_safe(default={}) # Returns {} if parsing fails + >>> # Handle rate limiting gracefully + >>> response = httpx.get("https://api.example.com/endpoint") + >>> data = response.json_safe(default={"error": "rate limited"}, raise_for_status=False) + + Note: + This method is particularly useful when: + - Dealing with unreliable APIs that may return malformed JSON + - You want a default value instead of raising exceptions + - You need to handle both HTTP errors and JSON parse errors uniformly + """ + # Check status code if requested + if raise_for_status and self.is_error: + request = self._request + if request is None: + raise RuntimeError( + "Cannot call `json_safe` with raise_for_status=True " + "as the request instance has not been set on this response." + ) + raise HTTPStatusError( + f"HTTP error {self.status_code} while requesting {request.url}", + request=request, + response=self, + ) + + # Return default for empty content + if not self.content: + return default + + # Try to parse JSON + try: + return jsonlib.loads(self.content, **kwargs) + except (jsonlib.JSONDecodeError, UnicodeDecodeError, ValueError): + return default + @property def cookies(self) -> Cookies: if not hasattr(self, "_cookies"):