JSONOutputParser (json_parser.py)
On this page
class output_parser.json_parser.JSONOutputParser(BaseOutputParser)
Overview
Extracts and parses a JSON object or array from raw LLM text, with optional validation against a Pydantic model. This is the standalone-toolkit counterpart to the orchestrator's internal _parse_json function in parser.py — the extraction algorithm is similar (markdown fence first, then raw_decode scanning) but this class additionally supports Pydantic casting and raises ParsingError with rich context instead of a bare ValueError.
Depends on base_parser.py (BaseOutputParser, ParsingError) and the standard library (json, re); optionally pydantic (imported lazily inside _validate_pydantic_model, not at module load time).
Constructor
JSONOutputParser(pydantic_model: Optional[Type] = None)
Public Methods
parse
def parse(self, text: str) -> Union[Dict[str, Any], list, Any]
Parameters: text: str — raw text containing JSON somewhere.
Returns: a dict or list (if no pydantic_model), or an instance of pydantic_model (if one was configured).
Raises:
ParsingError("Cannot parse empty text", ...)iftextis empty/whitespace.ParsingError("No valid JSON found in text. ...", ...)if_extract_jsonreturnsNone.ParsingError(f"Invalid JSON: {e.msg} at line {e.lineno}, col {e.colno}", ...)if the extracted candidate string failsjson.loads— this wraps the underlyingjson.JSONDecodeErrorascause.ParsingError(f"Expected JSON object for Pydantic model, got {type}", ...)ifpydantic_modelis set but the extracted JSON is a list/scalar instead of an object.ParsingError(f"JSON does not match schema for {name}: {e}", ...)if Pydantic construction (pydantic_model(**data)) raises.
Internal Workflow / Algorithm: _extract_json(text) tries two strategies in order:
- Regex-search for a
```json ... ```or bare``` ... ```fence; if found, triesjson.loads()on the inner content — if that succeeds, returns it immediately. - Falls through to a
json.JSONDecoder().raw_decodescan: for each of{and[, finds every occurrence intextand attemptsraw_decodestarting at that index, advancing to the next occurrence on failure, until one succeeds. This handles JSON embedded anywhere in surrounding prose, not just at the very start of the string.
If neither strategy finds anything, _extract_json returns None.
Complexity: O(n·m) worst case, where n is text length and m is the number of {/[ occurrences scanned before a valid one is found (each raw_decode attempt itself is O(k) in the length of the JSON candidate). In typical well-formed LLM output, this resolves on the first or second candidate.
Thread Safety: Safe to share one instance across threads for concurrent parse() calls — no mutable state beyond the read-only pydantic_model attribute.
Side Effects: None beyond raising exceptions on failure — no logging, no caching, no I/O.
get_format_instructions
def get_format_instructions(self) -> str
If pydantic_model is set, returns instructions embedding self.pydantic_model.model_json_schema() as a fenced JSON block. Otherwise returns a generic "double quotes for keys/strings" instruction with a hardcoded example.
Private Helpers
_extract_json(text) -> Optional[str]— see Algorithm above._validate_pydantic_model(model) -> None— raisesImportErrorif pydantic missing,ValueErrorifmodelisn't aBaseModelsubclass.
Examples
# Basic
parser = JSONOutputParser()
result = parser.parse('Here is the data: {"name": "John", "age": 30}')
# {'name': 'John', 'age': 30}
# Intermediate: JSON array
result = parser.parse('[1, 2, 3]')
# [1, 2, 3]
# Advanced: Pydantic validation
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
parser = JSONOutputParser(pydantic_model=User)
user = parser.parse('{"name": "John", "age": 30}')
# User(name='John', age=30)
# Error handling
try:
parser.parse("no json here at all")
except ParsingError as e:
print(e.original_text) # "no json here at all"
# Production usage: fenced code block from a chat-style LLM response
raw = '''Sure, here's the JSON:
```json
{"status": "ok", "items": [1, 2, 3]}
Let me know if you need anything else.''' result = JSONOutputParser().parse(raw)
## Best Practices
- Prefer this over the orchestrator's internal `_parse_json` when you specifically want Pydantic validation baked into the parse call itself, or when you want `ParsingError`'s rich `original_text`/`cause` context for debugging.
- Combine with `AutoFixingParser` (from `base_parser.py`) if you want automatic retry-with-fix behavior without pulling in the full `OutputParser` orchestrator.
## Common Mistakes
- Passing a non-`BaseModel` class as `pydantic_model` — this raises `ValueError` immediately at construction time, not at parse time, so the error surfaces early (which is generally desirable, but can be surprising if you're constructing the parser far from where you use it).
- Expecting `parse()` to extract the *last* or *largest* JSON block in text with multiple candidates — it returns the *first* one found by the scanning order described above (fence first, then leftmost `{`/`[` in the raw text).
## Related APIs
- [`PydanticOutputParser`](pydantic_parser.md) — wraps this class internally for the Pydantic-first use case.
- [`BaseOutputParser`, `ParsingError`](base_parser.md)
- [`OutputParser`](output_parser.md) — the orchestrator's own internal `_parse_json` function is a simpler, non-class variant of this same extraction algorithm.
## Version History
Not determinable from source.