serialization-groups Skill
Configures serialization and denormalization contexts using groups to control field visibility and relation embedding in API Platform.
- GitHub Stars
- 29
- Dateien
- 1
- Letzter Commit
- 22. Okt. 2026
- Lizenz
- —
- Erlaubte Tools
- —
Was macht der serialization-groups Skill?
serialization-groups ist ein Agent Skill. Configures serialization and denormalization contexts using groups to control field visibility and relation embedding in API Platform.
Zusammenfassung erzeugt aus der SKILL.md des Skills.
Über den serialization-groups skill
Managing field visibility and relation embedding in API Platform is what this skill enables for developers working with Symfony or Laravel. It provides the logic to configure normalization and denormalization contexts, allowing you to control which properties appear in JSON outputs during reads or inputs during writes. By using the #[Groups] attribute, you can define specific visibility rules for different operations, such as showing more detail in a single item view than in a collection list.
An agent should load this skill when a developer needs to debug missing JSON fields, implement write-only properties like passwords, or manage how related entities are embedded as objects versus IRIs. The skill handles complex scenarios like per-property overrides using the #[Context] attribute to change date formats or nested serialization groups. It also provides specific guidance for Laravel users who must use #[ApiProperty] because Eloquent models lack typed properties. This approach is intended for maintaining consistency in existing codebases where a DTO split might be unnecessary.
Was ein agent damit machen kann
- 1Control field visibility per operationConfigure a lean collection view while providing a richer data set for individual item requests.
- 2Manage write-only propertiesEnsure sensitive data like passwords can be received during input but never exposed in API responses.
- 3Toggle between embedded objects and IRIsDecide whether a related entity should appear as a full nested object or a simple string reference.
- 4Apply property-specific formattingOverride default serialization settings for a single field, such as changing a date format.
Geschrieben basierend auf dem SKILL.md des skills. Jeder use case wurde gegen ein zitat daraus geprüft.
So installierst du den serialization-groups Skill
Jeder Befehl kopiert den Skill-Ordner in das Skills-Verzeichnis des Clients. Starte den Client neu oder beginne eine neue Session, um ihn zu laden.Den serialization-groups Skill in Plugin installieren
/plugin marketplace add api-platform/skillset /plugin install api-platform@api-platform-skillset
Den serialization-groups Skill in Claude Code installieren
~/.claude/skills/serialization-groups/SKILL.md
$ npx degit api-platform/skillset/skills/serialization-groups ~/.claude/skills/serialization-groupsDen serialization-groups Skill in Claude Code (project) installieren
.claude/skills/serialization-groups/SKILL.md
$ npx degit api-platform/skillset/skills/serialization-groups .claude/skills/serialization-groupsDen serialization-groups Skill in Codex installieren
~/.agents/skills/serialization-groups/SKILL.md
$ npx degit api-platform/skillset/skills/serialization-groups ~/.agents/skills/serialization-groupsDen serialization-groups Skill in Gemini CLI installieren
~/.gemini/skills/serialization-groups/SKILL.md
$ npx degit api-platform/skillset/skills/serialization-groups ~/.gemini/skills/serialization-groupsDen serialization-groups Skill in Claude.ai installieren
npx degit api-platform/skillset/skills/serialization-groups serialization-groups && zip -r serialization-groups.zip serialization-groups # claude.ai → Customize → Skills → + → Create skill → Upload a skill → serialization-groups.zip
SKILL.md
Auf GitHub ansehenSerialization Groups
Prefer one resource class per representation over groups for new designs. Before reaching for groups, consider a DTO split: one class per output shape and per input shape, each with a flat, explicit set of properties (see api-resource). Reasons groups are a poor default:
- The public shape becomes implicit and scattered. To know what
GET /booksreturns you must mentally union every#[Groups]tag across the class hierarchy and cross-reference each operation'snormalizationContext. A dedicatedBookOutputDTO states the shape in one place.- They complicate the metadata. Per-operation contexts, per-property
#[Context], and group inheritance interact in ways that are hard to predict and hard to test.- They block streaming serialization. API Platform's JsonStreamer path needs one static shape per class; a class whose output varies by group can't be streamed and falls back to the slower object normalizer.
Use groups when: you're working in an existing codebase already built on them (stay consistent), or a DTO split is genuinely overkill (one tiny read/write asymmetry on an internal resource). Otherwise, split the class.
The two contexts
normalizationContext governs reading (entity → JSON). denormalizationContext
governs writing (JSON → entity). A property is only serialized/deserialized if
one of its #[Groups] is listed in the active context.
use ApiPlatform\Metadata\ApiResource;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
normalizationContext: ['groups' => ['book:read']],
denormalizationContext: ['groups' => ['book:write']],
)]
class Book
{
#[Groups(['book:read'])] // appears in output, ignored on input
public ?int $id = null;
#[Groups(['book:read', 'book:write'])] // read and write
public string $title = '';
#[Groups(['book:write'])] // write-only, e.g. a password
public ?string $secret = null;
}
A property with no group in the active context is invisible: a field present on the entity but absent from the response is almost always a missing read group.
Naming convention
Use resource:operation-style group names — book:read, book:write,
book:item:read. This keeps groups namespaced per resource so a read group on
one class never accidentally pulls properties on another. Pairing a broad
book:read with a narrower book:item:read lets item operations expose more than
collections (see next section).
Per-operation contexts
Override the resource-level context on a single operation. Common pattern: a lean collection, a richer item view.
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
#[ApiResource(normalizationContext: ['groups' => ['book:read']])]
#[GetCollection] // book:read only
#[Get(normalizationContext: ['groups' => ['book:read', 'book:item:read']])]
#[Patch(denormalizationContext: ['groups' => ['book:write']])]
class Book
{
#[Groups(['book:read'])]
public string $title = '';
#[Groups(['book:item:read'])] // only on the single-item GET
public ?string $description = null;
}
Controlling embedded relations
A relation is embedded as a full object when the related class has a property in a group shared with the parent's context; otherwise it serializes as an IRI/string. This is how you choose embed-vs-reference:
#[ApiResource(normalizationContext: ['groups' => ['book:read']])]
class Book
{
#[Groups(['book:read'])]
public ?Author $author = null; // embedded only if Author has a 'book:read' property
}
class Author
{
#[Groups(['book:read'])]
public string $name = ''; // this property pulls Author inline
}
Drop the book:read group from Author::$name and author collapses back to an
IRI. This implicit coupling is exactly the kind of thing a DTO split makes explicit.
Per-property context override with #[Context]
#[Context] overrides serializer options for one property without a new group —
useful for date formats or switching the normalization group on a single relation
(embed the same related type at two depths):
use Symfony\Component\Serializer\Attribute\Context;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
class Book
{
#[Groups(['book:read'])]
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
public ?\DateTimeInterface $publishedAt = null;
#[Groups(['book:read'])]
#[Context(['normalization' => ['groups' => ['author:summary']]])]
public ?Author $author = null; // serialized with author:summary, not book:read
}
The normalization/denormalization keys scope the override to one direction. See
the parallel example in state-provider (Per-Property Serialization Context).
Interaction with other features
- Validation groups are separate from serialization groups — don't reuse the
same names expecting them to align. See operations for
validationContext. securityexpressions on properties (security: "is_granted(...)") gate fields independently of groups; both must pass.
Laravel (Eloquent)
API Platform uses the Symfony Serializer on Laravel too, so normalizationContext /
denormalizationContext, the two-context model, per-operation contexts and embed-vs-IRI
behaviour are identical. The one difference: Eloquent models have no typed
properties, so #[Groups] can't be attached per property. Declare groups at the
class level via #[ApiProperty(serialize: …, property: …)]:
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(normalizationContext: ['groups' => ['book:read']])]
#[ApiProperty(serialize: new Groups(['book:read']), property: 'title')]
#[ApiProperty(serialize: new Groups(['book:read']), property: 'author')]
class Book extends Model {}
DTO/ApiResource classes (not extending Model) use property-level #[Groups] as
shown above. The name_converter (default SnakeCaseToCamelCaseNameConverter) is set
in config/api-platform.php.
Checklist
- Considered a DTO-per-shape split before adding groups (see api-resource)
- Group names namespaced per resource (
book:read, not bareread) - Write-only secrets carry only the write group
- Embedded-vs-IRI relations verified against actual JSON, not assumed
-
#[Context]used for one-off format/group tweaks instead of a whole new group
Dateien in diesem Skill
1serialization-groups Skill-Fragen
Wie installiere ich den serialization-groups Skill in Claude Code?
Führe npx degit api-platform/skillset/skills/serialization-groups ~/.claude/skills/serialization-groups aus. Claude Code lädt Skills aus ~/.claude/skills (alle Projekte) oder .claude/skills (ein Projekt) und nutzt einen Skill, wenn seine Beschreibung zur Aufgabe passt.
Welche Clients können den serialization-groups Skill nutzen?
Jeder Client, der das Agent Skills-Format liest: Claude Code, Codex, Gemini CLI und claude.ai (als ZIP hochladen). Skripte im Skill laufen nur dort, wo der Client Codeausführung erlaubt.
What is the difference between normalizationContext and denormalizationContext?
normalizationContext governs reading (entity to JSON), while denormalizationContext governs writing (JSON to entity).
How can I control whether a relation is embedded as an object or returned as an IRI?
A relation is embedded as a full object if the related class has a property in a group shared with the parent's context; otherwise, it serializes as an IRI.
Should I always use serialization groups for new designs?
No, you should prefer one resource class per representation (DTO split) over groups for new designs to avoid implicit shapes and metadata complexity.
How do I apply specific serialization settings to a single property?
You can use the #[Context] attribute to override serializer options for a single property, such as date formats or specific normalization groups.