LangChain output parsers sit between your model and your application logic, turning raw completions into structured data your code can trust. The three main contenders — Pydantic, JSON, and XML — each make different trade-offs around validation strictness, streaming support, error recovery, and developer ergonomics. This comparison walks through concrete dimensions so you can choose without guessing.
How each parser works
PydanticOutputParser
Wraps a Pydantic model and injects formatting instructions into the prompt. On the return path it calls model.parse() which runs model_validate_json() under the hood. You get full Pydantic validation: type coercion, custom validators, computed fields, and detailed ValidationError objects with field-level context.
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field, field_validator
class Extraction(BaseModel):
entities: list[str] = Field(min_length=1)
confidence: float = Field(ge=0.0, le=1.0)
@field_validator("entities")
@classmethod
def dedupe(cls, v):
return list(dict.fromkeys(v))
parser = PydanticOutputParser(pydantic_object=Extraction)
prompt = f"Extract entities.\n{parser.get_format_instructions()}"
The parser also exposes parser.parse_result() for handling Generation objects directly from LLMResult.
JsonOutputParser
A lighter wrapper that asks the model for valid JSON and runs json.loads() on the response. It accepts an optional pydantic_object for validation, but the default path skips schema enforcement entirely. Useful when you want structured output without committing to a Pydantic model in your dependency graph.
from langchain.output_parsers import JsonOutputParser
parser = JsonOutputParser() # no schema enforcement
# or
parser = JsonOutputParser(pydantic_object=MyModel) # validates after parse
XMLOutputParser
Requests XML with a specified root tag and optional field tags. Parses with xml.etree.ElementTree and returns a dict. No built-in validation — you validate downstream or not at all. Historically favored for models that struggle with JSON escaping but handle angle brackets cleanly.
from langchain.output_parsers import XMLOutputParser
parser = XMLOutputParser(tags=["entities", "confidence"])
# Prompt gets: "Output XML with <entities> and <confidence> tags"
Validation and type safety
Pydantic wins decisively here. You define the contract once in the model and get enforcement at parse time, including nested models, enums, constrained types, and custom validators. A failed parse raises OutputParserException wrapping a ValidationError with error.loc, error.msg, and error.input — everything you need for automated retry or structured logging.
JSON parser with a Pydantic model attached gives you the same validation, but the prompt instructions are weaker. The parser injects a generic “output valid JSON” directive rather than the detailed field-by-field instructions PydanticOutputParser generates. Models occasionally omit required fields or mismatch types because the prompt guidance is thinner.
XML parser offers no validation. You receive a dict of strings. Type coercion, presence checks, and business logic validation are entirely your responsibility.
Streaming support
PydanticOutputParser does not support incremental parsing. You must wait for the full completion, then validate. For long extractions this adds perceived latency.
JsonOutputParser can pair with JsonOutputParser.parse_stream() which yields partial dicts as the stream arrives. This works because JSON is prefix-parseable up to a point — but incomplete objects raise json.JSONDecodeError until the closing brace arrives. LangChain’s implementation buffers until it can emit a valid dict, so you still get discrete chunks rather than true token-level streaming.
XMLOutputParser has no streaming parser in LangChain core. You would need a SAX-style incremental parser (e.g., xml.sax) and custom logic to emit partial results — not provided out of the box.
If you need token-level streaming with structured output, consider function calling / tool use APIs instead of output parsers. The model emits structured arguments incrementally, and the provider handles the framing.
Error recovery and retry strategies
Pydantic’s ValidationError is a rich diagnostic object. You can extract the failed field, the raw value, and the expected type, then feed that back to the model in a retry prompt:
from langchain.output_parsers import OutputFixingParser
from langchain_openai import ChatOpenAI
fixing_parser = OutputFixingParser.from_llm(
parser=parser,
llm=ChatOpenAI(model="gpt-4o-mini")
)
result = fixing_parser.parse(bad_completion)
OutputFixingParser works with any base parser but shines with Pydantic because the error context is precise. With JSON or XML parsers, the error is usually “invalid syntax” — less actionable for the fixing model.
JSON parser failures are typically malformed JSON (trailing commas, unescaped newlines, truncated output). The fixing parser can often repair these, but semantic errors (wrong keys, type mismatches) only surface if you attached a Pydantic model.
XML parser failures tend to be unclosed tags or mismatched nesting. The fixing parser handles these reasonably well since XML structure is explicit.
Prompt overhead and token cost
PydanticOutputParser.get_format_instructions() emits a detailed schema description — field names, types, constraints, descriptions, and examples. For a model with 10 fields this can be 300-500 tokens. Worth it for correctness; painful if you’re optimizing for context window or per-token cost.
JsonOutputParser injects a short generic instruction: “Return a JSON object.” ~20 tokens. You save prompt tokens but spend more on retries when the model drifts.
XMLOutputParser sits in between: “Output XML with tags X, Y, Z.” ~50 tokens. Tag names act as implicit schema, giving the model more signal than bare JSON without the verbosity of Pydantic’s full instructions.
Ecosystem integration
Pydantic is the de facto standard for Python data validation. Models integrate with FastAPI, SQLAlchemy, Pydantic Settings, and countless libraries. You can reuse the same model for API contracts, database rows, and LLM output — single source of truth.
JSON parser output is a plain dict. Universal, but you lose type hints and IDE support unless you manually annotate or convert to a model afterward.
XML parser output is a dict of strings. Niche. Useful when downstream systems consume XML (legacy enterprise, some document pipelines) or when the model genuinely produces better XML than JSON. Rare in modern stacks.
Failure modes in production
| Scenario | Pydantic | JSON (no model) | JSON + Pydantic | XML |
|---|---|---|---|---|
| Missing required field | Caught, precise error | Silently returns partial dict | Caught, precise error | Silently returns partial dict |
| Type mismatch (string vs int) | Coerces or errors with context | Silently keeps string | Coerces or errors with context | Silently keeps string |
| Extra unexpected fields | extra="forbid" rejects |
Accepts silently | extra="forbid" rejects |
Accepts silently |
| Truncated output | ValidationError with location |
JSONDecodeError |
ValidationError |
ParseError (line/col) |
| Unicode / escaping issues | Handled by JSON parser | JSONDecodeError |
Handled by JSON parser | Rare (CDATA helps) |
| Nested object validation | Full recursive validation | No validation | Full recursive validation | No validation |
When to use each
PydanticOutputParser — Default choice for production pipelines. You need guaranteed schema compliance, rich error context for retries, and integration with the broader Python type system. Accept the prompt token cost and lack of streaming. Ideal for extraction, classification, agent tool arguments, and any output that feeds typed downstream code.
JsonOutputParser (no model) — Prototyping, exploratory analysis, or when the schema changes per request and defining Pydantic models dynamically is awkward. Also fine for non-critical paths where “best effort” structured output is acceptable and you have a separate validation layer.
JsonOutputParser + Pydantic model — Middle ground. You want validation but prefer writing the schema once in Pydantic and letting the parser handle prompt instructions. Slightly weaker prompt guidance than PydanticOutputParser; marginally fewer prompt tokens. Valid choice if you’re already using JsonOutputParser elsewhere and want consistency.
XMLOutputParser — Two legitimate cases: (1) the model you’re using demonstrably produces better-structured output with XML than JSON (some smaller open models), or (2) you’re piping output directly into an XML-consuming system (XSLT pipelines, legacy document generators). Otherwise, avoid — the validation gap and lack of tooling make it a liability.
Practical pattern: parser as a contract, not a suggestion
Treat the output parser as the boundary contract between your prompt and your application. Version it alongside your prompt template. When the prompt changes, regenerate the parser’s format instructions and run your eval suite.
# parser_registry.py
from functools import lru_cache
from langchain.output_parsers import PydanticOutputParser
from myapp.schemas import ExtractionV1, ExtractionV2
@lru_cache
def get_parser(version: str) -> PydanticOutputParser:
model = {"v1": ExtractionV1, "v2": ExtractionV2}[version]
return PydanticOutputParser(pydantic_object=model)
# In your chain factory
def build_chain(version: str):
parser = get_parser(version)
prompt = PromptTemplate(
template="Extract...\n{format_instructions}",
partial_variables={"format_instructions": parser.get_format_instructions()}
)
return prompt | llm | parser
This gives you reproducible parses, testable parser versions, and a clear upgrade path when schemas evolve.
Verdict
- Default to
PydanticOutputParserfor any production workload where correctness matters. The validation, error context, and ecosystem integration pay for themselves in reduced retry logic and fewer silent data bugs. - Use
JsonOutputParserwithout a model only for throwaway scripts, rapid prototyping, or highly dynamic schemas where Pydantic model generation is more friction than it’s worth. - Use
JsonOutputParserwith a Pydantic model if you want a single parser class across your codebase and can tolerate slightly weaker prompt instructions. - Avoid
XMLOutputParserunless you have a measured, documented reason — model behavior or downstream system — that justifies the validation gap.
The parser choice is a contract decision, not a formatting preference. Choose the one that lets you catch failures at the boundary, not in your business logic.