Category: Response Design
Description
Successful (2xx) responses should offer both JSON and XML content type representations to support diverse client requirements and maximize API compatibility.
What It Checks
This rule validates that all successful (2xx) responses provide both application/json and application/xml content types. It checks:
- All operations (GET, POST, PUT, PATCH, DELETE) across your API
- Only successful response codes (2xx status codes)
- The presence of both
application/jsonANDapplication/xmlin the response content types - If one content type is present in a 2xx response, the other must also be present
The rule recursively examines all path nodes and their methods to ensure consistent dual-format support across the entire API.
Why It Matters
Offering both JSON and XML representations enhances API accessibility and compatibility:
- Broader Client Support: Legacy systems and enterprise integrations often require XML, while modern applications prefer JSON
- Standards Compliance: Many industries (healthcare, finance, government) have XML-based standards and regulations
- Flexibility: Allows clients to choose the format that best suits their infrastructure and tooling
- Future-Proofing: Prevents the need for breaking changes or versioning when clients request different formats
- Interoperability: Facilitates integration with diverse technology stacks and platforms
Examples
Good ✅
paths:
/users/{userId}:
get:
summary: Get user by ID
parameters:
- name: userId
in: path
required: true
schema:
type: string
responses:
'200':
description: User found
content:
application/json:
schema:
$ref: '#/components/schemas/User'
application/xml:
schema:
$ref: '#/components/schemas/User'
'404':
description: User not found
/products:
get:
summary: List products
responses:
'200':
description: Product list
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Product'
application/xml:
schema:
type: array
items:
$ref: '#/components/schemas/Product'
xml:
name: products
wrapped: true
/orders:
post:
summary: Create an order
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
responses:
'201':
description: Order created
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
application/xml:
schema:
$ref: '#/components/schemas/Order'
'400':
description: Invalid request
Bad ❌
paths:
/users/{userId}:
get:
summary: Get user by ID
parameters:
- name: userId
in: path
required: true
schema:
type: string
responses:
'200':
description: User found
content:
application/json: # Only JSON, no XML
schema:
$ref: '#/components/schemas/User'
'404':
description: User not found
/products:
get:
summary: List products
responses:
'200':
description: Product list
content:
application/xml: # Only XML, no JSON
schema:
type: array
items:
$ref: '#/components/schemas/Product'
/orders:
post:
summary: Create an order
responses:
'201':
description: Order created
content:
application/json: # Missing XML representation
schema:
$ref: '#/components/schemas/Order'
'400':
description: Invalid request
Configuration
This rule has no configuration options. It is either enabled or disabled.
How to Fix
-
Add both content types to all 2xx responses:
# Before responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/User' # After responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/User' application/xml: schema: $ref: '#/components/schemas/User' -
Use the same schema for both formats:
# Both formats can reference the same schema components: schemas: User: type: object properties: id: type: string name: type: string email: type: string -
Add XML-specific metadata when needed:
# For arrays, add XML wrapping configuration responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/Product' application/xml: schema: type: array items: $ref: '#/components/schemas/Product' xml: name: products wrapped: true # Wraps items in <products> element -
Implement content negotiation in your server:
// Example implementation app.get('/users/:id', (req, res) => { const user = getUserById(req.params.id); const acceptHeader = req.headers.accept; if (acceptHeader.includes('application/xml')) { // Convert to XML and send res.type('application/xml'); res.send(convertToXML(user)); } else { // Default to JSON res.json(user); } }); -
Apply to all successful responses consistently:
# Ensure every 2xx response has both formats paths: /resource: get: responses: '200': # Success - needs both formats content: application/json: {...} application/xml: {...} '201': # Created - needs both formats content: application/json: {...} application/xml: {...} '204': # No Content - no body, rule doesn't apply description: Deleted successfully '400': # Error - not a 2xx, rule doesn't apply description: Bad request
Related Rules
- Rule 8: Response Error Codes - Proper HTTP status code usage for different outcomes
- Rule 53: Create/Update Return - Ensuring modify operations return resources in multiple formats
- Rule 42: Consistent Errors - Maintaining consistency across response formats
Best Practices
- Content Negotiation: Implement proper
Acceptheader handling to allow clients to request their preferred format - Schema Reuse: Use the same schema definition for both JSON and XML to ensure consistency
- XML Metadata: Add appropriate XML annotations (like
wrappedfor arrays) when necessary - Default Format: Choose a sensible default (typically JSON) when clients don't specify a preference
- Testing: Ensure both representations produce equivalent data structures
- Documentation: Clearly document that both formats are available in your API documentation
Implementation Notes
XML Array Wrapping
When returning arrays, XML requires proper wrapping:
# For a collection endpoint
schema:
type: array
items:
$ref: '#/components/schemas/User'
xml:
name: users
wrapped: true
This produces:
<users>
<user>
<id>1</id>
<name>John</name>
</user>
<user>
<id>2</id>
<name>Jane</name>
</user>
</users>
Without wrapped: true, you'd get:
<user>
<id>1</id>
<name>John</name>
</user>
<user>
<id>2</id>
<name>Jane</name>
</user>
Content Negotiation Example
Clients request their preferred format via the Accept header:
GET /users/123 HTTP/1.1
Accept: application/json
GET /users/123 HTTP/1.1
Accept: application/xml
GET /users/123 HTTP/1.1
Accept: application/json, application/xml;q=0.9
Common Violations
- JSON-only APIs: Modern APIs often forget XML support, limiting enterprise adoption
- Inconsistent format support: Some endpoints support both formats while others don't
- Missing XML configuration: Forgetting XML-specific metadata like array wrapping
- Request/response mismatch: Supporting both formats for requests but only one for responses