Problem/Motivation
JSON Schema defines the structure of a JSON document: its properties, their types, which of them are required, etc.
Today, Drupal can only generate a JSON Schema from an existing typed data object. To describe an article, it needs an actual article object.
However, some consumers need a schema before any typed data object exists, and Drupal does not support that.
Examples:
- A decoupled front end that creates articles through JSON:API needs the schema of the article's fields to validate its form before submitting it.
- An OpenAPI document needs to describe the request body for creating an article.
- A "create article" tool, invoked by a workflow like ECA/FlowDrop/Maesto or an AI agent, needs to describe an input schema. The workflow or agent constructs the tool's arguments from that schema, so the schema must be available before the tool receives any input.
This issue is a follow-up of #3627338: Serializer::getJsonSchema() throws a TypeError when passed a class name. #3627338 stopped documenting class names as input to getJsonSchema(), because generating a schema without data needs a data definition rather than a class.
In #3627338-8, d34dman, FlowDrop maintainer, wrote this about Rosetta:
Schema that needs no instance is what 'Rosetta' wants for tool schemas. Otherwise we might end up creating 'fake object' representation of a static schema for the sake of discovery.
Proposed resolution
Provide a method that generates a schema directly from a data definition. The following is just a sketch:
$definition = DataDefinition::create('string')->setRequired(TRUE);
$schema = \Drupal::service('serializer')->getJsonSchemaForDefinition($definition);
// ['type' => 'string']Related work
#2994473: [META] JSON API's normalizers support schema tracking, to guarantee comprehensive schema, a JSON:API plan open since 2018 and inactive since 2022, touched upon the same requirement. In comment #5, gabesullice noted that schema generation requires normalizers "that can be determined without an instance of the data to be normalized".
Other frameworks generate JSON Schema from a description of the data rather than from an instance:
- Python's Pydantic generates it from the model class (https://docs.pydantic.dev/latest/concepts/json_schema/).
- API Platform generates it from an API resource class (https://api-platform.com/docs/core/json-schema/).
- Symfony 8.2 adds an experimental JsonSchema component that generates it from a PHP type such as
Type::object(Book::class), and from a method's parameters for a tool's input. The schema follows the serializer metadata and the validation constraints (https://symfony.com/blog/new-in-symfony-8-2-jsonschema-component).
Drupal's contributed Tool API module already does much of this: it declares each tool's input and output as typed data definitions and generates their JSON Schema from them . But to get there, it works around many limitations and bugs in Drupal Core. I'm helping to fix those in Core one at a time; #3635315: [Prototype] Write a setting's rules once, in config schema, and use them for validation, forms and the published JSON Schema collects a number of them.
Follow-up work
JSON:API has relied on "placeholder objects" (read: hacks) since #3031367: Generate JSON schema for content entity types:
ResourceObjectNormalizercreates an unsaved entity of the bundle solely to obtain the schemas of its fields (https://git.drupalcode.org/project/drupal/-/blob/ec3bb0fd567d2801bf5169f...).EntityStorageBase::create()generates a UUID and invokeshook_entity_create()andhook_ENTITY_TYPE_create()in every implementing module.FieldNormalizerappends a sample item to the caller's field item list, generates the item's schema, and then removes the item (https://git.drupalcode.org/project/drupal/-/blob/ec3bb0fd567d2801bf5169f...). Its comment states the constraint: "Normalizers are resolved by the class/object being normalized. Even without data, we must retrieve a representative field item."
The same pattern likely exists outside of Drupal Core as well. We'd be able to clean that up after this lands.
User interface changes
None.
API changes
A new method that generates a schema from a data definition. Its name and signature are to be determined.
Data model changes
None.
Comments