You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
OpenAPI: Non-JSON formats should not generate JSON schemas #7802
When an API Platform operation defines non-JSON output formats (e.g., text/html, text/xml), the OpenAPI factory generates JSON schemas for these formats, which is semantically incorrect.
Example
#[ApiResource(
operations: [
newGet(
uriTemplate: '/unsubscribe/{token}',
formats: [
'jsonld' => ['application/ld+json'],
'json' => ['application/json'],
'html' => ['text/html'], // This causes the issue
],
),
],
)]
class UnsubscribedEmail {}
Current Behavior
The OpenAPI export generates:
paths:
/api/unsubscribe/{token}:
get:
responses:
'200':
content:
application/ld+json:
schema:
$ref: '#/components/schemas/UnsubscribedEmail.jsonld'application/json:
schema:
$ref: '#/components/schemas/UnsubscribedEmail'text/html:
schema:
$ref: '#/components/schemas/UnsubscribedEmail.html'# Makes no sensecomponents:
schemas:
UnsubscribedEmail.html: # JSON schema for HTML format - meaninglesstype: objectproperties:
status:
type: string
Expected Behavior
Non-JSON formats should either:
Not have a schema reference in OpenAPI
Or be excluded from the OpenAPI spec entirely (content negotiation still works at runtime)
Root Cause
In src/OpenApi/Factory/OpenApiFactory.php, the getMimeTypes() method returns ALL output formats, and the schema generation loop creates schemas for each:
// Around line 267foreach ($responseMimeTypesas$mimeType => $operationFormat) {
// Skip formats that don't have JSON schema supportif (!isset($this->jsonSchemaFormats[$operationFormat])) {
continue;
}
$operationOutputSchema = $this->jsonSchemaFactory->buildSchema(
$resourceClass,
$operationFormat,
Schema::TYPE_OUTPUT,
$operation,
$schema,
null,
$forceSchemaCollection
);
$operationOutputSchemas[$operationFormat] = $operationOutputSchema;
}
Step 4: Update buildContent to handle missing schemas
privatefunctionbuildContent(array$responseMimeTypes, array$operationSchemas): \ArrayObject
{
$content = new \ArrayObject();
foreach ($responseMimeTypesas$mimeType => $format) {
// Only add content entry if we have a schema for this formatif (isset($operationSchemas[$format])) {
$content[$mimeType] = newMediaType(
schema: new \ArrayObject($operationSchemas[$format]->getArrayCopy(false))
);
}
// Non-JSON formats without schemas are simply not included in OpenAPI content
}
return$content;
}
Alternative Consideration
The buildContent method could instead add the content type without a schema reference:
if (isset($operationSchemas[$format])) {
$content[$mimeType] = newMediaType(schema: new \ArrayObject($operationSchemas[$format]->getArrayCopy(false)));
} else {
// Include the content type but without a schema (valid in OpenAPI 3.1)$content[$mimeType] = newMediaType();
}
This preserves the information that the endpoint supports HTML responses, just without a JSON schema.
Testing
Add a test case to tests/OpenApi/Factory/OpenApiFactoryTest.php:
publicfunctiontestNonJsonFormatsDoNotGenerateSchemas(): void
{
// Create a resource with html format// Assert that UnsubscribedEmail.html schema does not exist// Assert that text/html content type either has no schema or is not present
}
Configuration Reference
The jsonschema_formats is already computed in ApiPlatformExtension.php:
$jsonSchemaFormats = $config['jsonschema_formats'];
if (!$jsonSchemaFormats) {
foreach (array_merge(array_keys($formats), array_keys($errorFormats)) as$f) {
// Only JSON-based formats get schemas by defaultif (str_starts_with($f, 'json')) {
$jsonSchemaFormats[$f] = true;
}
}
}
This logic already correctly identifies JSON formats - it just needs to be used in OpenApiFactory.
Before picking this up: step 1 relies on %api_platform.jsonschema_formats%, but since 79edced ("share invariable sub-schemas") the extension sets that parameter to [] unconditionally, and the formats computed just above it are no longer used anywhere. So injecting it as-is would filter out every format, JSON ones included, and the api_platform.jsonschema_formats config option currently has no effect.
Would you rather restore the computed value into the parameter (which would also make the option meaningful again), or filter the formats in OpenApiFactory some other way? Happy to send the PR either way. 🙂
Good point, and it actually narrows this one down nicely: an OpenAPI Schema Object is perfectly valid for application/xml, it just needs the xml hints (name, attribute, wrapped) to describe the payload accurately.
So I'd split it in two:
this issue: drop the schema only for formats with no structured representation (text/html and friends), and leave XML alone;
a follow-up for proper XML description (xml.name from the short name, wrapped for collections, attributes), which is a feature of its own.
Does that split work for you? For part 1, the question about jsonschema_formats above still stands. 🙂
Makes sense, and it would even be free: OpenApiFactory already receives %api_platform.formats%, it just never uses it.
One catch though: by default formats only holds jsonld, so filtering the operation formats against it would also drop the schema of the json in the example above, since that one is only declared on the operation. Anyone who sets formats per operation would lose schemas they have today.
Two ways around it that I can see:
filter against the global formats anyway, and document that a format needs to be declared there to get a schema;
decide per operation format instead: keep the schema for the JSON-based ones (the json* naming the extension already relies on) and for xml, and drop it for the rest (html, csv, …).
I'd lean towards 2, since it doesn't change anything for existing setups except the html case, but happy to go either way. Should it target 4.4 as a fix, or main?
Activity
Amoifr commentedon Sep 24, 2026
Before picking this up: step 1 relies on
%api_platform.jsonschema_formats%, but since 79edced ("share invariable sub-schemas") the extension sets that parameter to[]unconditionally, and the formats computed just above it are no longer used anywhere. So injecting it as-is would filter out every format, JSON ones included, and theapi_platform.jsonschema_formatsconfig option currently has no effect.Would you rather restore the computed value into the parameter (which would also make the option meaningful again), or filter the formats in
OpenApiFactorysome other way? Happy to send the PR either way. 🙂dunglas commentedon Sep 25, 2026
XML is supported by OpenAPI. Maybe should we improve our support to allow describing XML APIs?
https://swagger.io/docs/specification/v3_0/data-models/representing-xml/
Amoifr commentedon Sep 27, 2026
Good point, and it actually narrows this one down nicely: an OpenAPI Schema Object is perfectly valid for
application/xml, it just needs thexmlhints (name,attribute,wrapped) to describe the payload accurately.So I'd split it in two:
text/htmland friends), and leave XML alone;xml.namefrom the short name,wrappedfor collections, attributes), which is a feature of its own.Does that split work for you? For part 1, the question about
jsonschema_formatsabove still stands. 🙂soyuka commentedon Sep 29, 2026
Maybe we can widen to
formatsinstead of jsonschema_formats ?Amoifr commentedon Sep 30, 2026
Makes sense, and it would even be free:
OpenApiFactoryalready receives%api_platform.formats%, it just never uses it.One catch though: by default
formatsonly holdsjsonld, so filtering the operation formats against it would also drop the schema of thejsonin the example above, since that one is only declared on the operation. Anyone who sets formats per operation would lose schemas they have today.Two ways around it that I can see:
formatsanyway, and document that a format needs to be declared there to get a schema;json*naming the extension already relies on) and forxml, and drop it for the rest (html,csv, …).I'd lean towards 2, since it doesn't change anything for existing setups except the
htmlcase, but happy to go either way. Should it target4.4as a fix, ormain?