mtarld/json-encoder-bundle
Symfony bundle that integrates mtarld/json-encoder into your app, letting you encode and decode PHP objects to and from JSON with support for denormalization, validation, and typed collections.
Mind the status first. This bundle is archived and has effectively no downstream usage. Treat it as a reference implementation rather than a dependency you build a product on. If you adopt it, pin an exact version in composer.json ("mtarld/json-encoder-bundle": "x.y.z") and be prepared to fork.
Install and register.
composer require mtarld/json-encoder-bundle
Add it to config/bundles.php if Flex doesn't do it:
Mtarld\JsonEncoderBundle\JsonEncoderBundle::class => ['all' => true],
Where to look first. The README is short; the real documentation is src/. Read, in order:
Attribute/ directory (what you annotate),DependencyInjection/ and services.xml/services.php (the actual service ID and default config).Confirm the service ID before writing code — small bundles change names between releases, and there are no docs to fall back on.
First use case. Encode a small read-model DTO to JSON in a controller action, bypassing serializer groups:
#[Route('/api/user/{id}', methods: ['GET'])]
public function show(User $user, JsonEncoderInterface $encoder): Response
{
return new JsonResponse($encoder->encode($user));
}
Start with a flat DTO. Only then add nested objects and collections — that's where attribute discovery and recursion issues surface.
Attribute-first mapping. The bundle's premise is annotating classes/properties with attributes so the encoder emits JSON keys without hand-writing JsonSerializable or serializer metadata. Keep mapping declarations next to the properties they describe; the win over #[Groups] is locality and a smaller mental model.
Inject the encoder, don't call it statically. Wire the encoder interface into controllers, message handlers, or a thin JsonResponder service. This keeps the (archived) bundle behind one seam in your codebase, so a future replacement — symfony/serializer, API Platform, or a hand-rolled json_encode with jsonSerialize() — only touches one class.
Prefer DTOs over entities. Encoding Doctrine entities directly invites lazy-loading surprises and circular references. Map entities to explicit DTOs and encode those. This also makes the attribute mapping auditable and testable without a database.
Nested objects and collections. For a User with Address[], verify behavior explicitly with a fixture test. Small bundles like this often handle scalars and single nested objects well but treat arrays inconsistently (list vs. map, empty array vs. null). Write one test per shape: scalar, nullable scalar, single object, list, empty collection.
Custom encoders as the extension point. If the package exposes a per-type encoder registration mechanism, use it for value objects (Money, Uuid, enums) rather than leaking formatting logic into DTO getters. This is the most likely place to hit undocumented behavior — read DependencyInjection/ for the tag name before assuming.
Integration tips.
JsonEncoder-agnostic adapter of your own; expose encode(object): array.kernel.controller listener only if you need consistent JSON across many endpoints — otherwise plain controller usage is clearer.json_decode(..., true)) rather than string equality, so key ordering and formatting don't create flaky tests.composer.json constraint block before adding it to a project on a recent Symfony; a conflict with symfony/serializer or API Platform is plausible since both claim serialization responsibilities. Verify with composer why-not symfony/serializer.bin/console cache:clear. If output doesn't change, you're looking at stale metadata, not a logic bug.User::$team ↔ Team::$members) will recurse or throw. Break cycles at the DTO layer; don't expect the encoder to implement serializer-style circular reference handlers.null vs. [] vs. omitted key explicitly. Ambiguity here breaks API consumers silently and is the most common post-adoption bug report for hand-rolled encoders.$encoder->encode($dto) before wrapping in JsonResponse, and use var_dump(get_class_methods($encoder)) to discover the real public surface — the README likely documents less than the class exposes.README note that it's vendored, and write one integration test per encoding shape. At 10 stars and 0 dependents, you are the maintainer the moment you ship it.How can I help you explore Laravel packages today?