Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Json Decoder Laravel Package

karriere/json-decoder

JsonDecoder maps JSON into typed PHP objects, handling nested objects, arrays, scalar casting, required fields, and custom decoders. It turns API responses and config files into predictable, IDE-friendly classes for safer, cleaner PHP code.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Confirm your runtime first: as of v5.1.0 the package requires PHP 8.2+ (PHP 8.0 and 8.1 support was removed) and explicitly supports PHP 8.5. If you are still on 8.0/8.1, pin to the last 5.0.x release or upgrade PHP before adopting.
  • Install with Composer: composer require karriere/json-decoder. Check what you actually resolved with composer show karriere/json-decoder and treat the PHP constraint as part of your upgrade planning.
  • Start in the README examples, then inspect Karriere\JsonDecoder\JsonDecoder, ClassBinding, the Bindings\* classes, and the transformer contract/classes.
  • The basic workflow is: define a DTO, register field bindings for that DTO, then decode a JSON string into the DTO.
use Karriere\JsonDecoder\ClassBinding;
use Karriere\JsonDecoder\JsonDecoder;

class UserDto
{
    public string $id;
    public string $email;
    public ?string $name = null;
}

$decoder = new JsonDecoder();

$decoder->register(UserDto::class, function (ClassBinding $binding) {
    $binding->bindField('id');
    $binding->bindField('email');
    $binding->bindField('name');
});

$user = $decoder->decode($json, UserDto::class);

First real use case: put decoding at the boundary of your application. For example, decode a third-party API response or webhook payload into a typed DTO inside an adapter/service, then pass that DTO into your domain code instead of passing raw arrays around.

Implementation Patterns

One DTO per payload boundary. Create a DTO for each external payload you care about: API responses, webhook bodies, queue messages, import rows. Keep the DTO close to the payload shape and keep domain entities separate.

Register bindings centrally. In Laravel, configure a singleton decoder in a service provider so bindings are not rebuilt per request.

use Karriere\JsonDecoder\ClassBinding;
use Karriere\JsonDecoder\JsonDecoder;

$this->app->singleton(JsonDecoder::class, function () {
    $decoder = new JsonDecoder();

    $decoder->register(OrderDto::class, function (ClassBinding $binding) {
        $binding->bindField('id');
        $binding->bindField('created_at', 'createdAt', new DateTimeTransformer());
        $binding->bindClass('customer', 'customer', CustomerDto::class);
        $binding->bindArray('items', 'items', OrderItemDto::class);
    });

    return $decoder;
});

Use transformers for non-trivial values. Dates, enums, money objects, booleans encoded as 0/1, and IDs that need casting should be handled in transformers rather than in controllers.

class DateTimeTransformer implements Transformer
{
    public function transform($value)
    {
        return new DateTimeImmutable($value);
    }
}

Be explicit about nested objects and collections. Do not expect the decoder to infer nested DTO types from JSON shape. Register bindClass/bindArray-style bindings for nested objects and arrays of objects. If a nested payload is polymorphic, decode to a base DTO first, inspect a discriminator field, then decode again to the concrete DTO.

Validate before decoding. The decoder maps structure; it does not validate business rules. Validate required keys, types, and formats with Laravel validation or a small boundary validator before calling decode(). Treat a decode failure as an integration failure, not as a user validation error.

Keep decoding out of controllers. Put it in a service, action, or adapter:

class StripeCustomerAdapter
{
    public function __construct(private JsonDecoder $decoder) {}

    public function fromApiResponse(string $body): StripeCustomerDto
    {
        return $this->decoder->decode($body, StripeCustomerDto::class);
    }
}

Test with fixtures. Store representative JSON fixtures and assert that the decoded DTO has the expected values. Test transformers independently and add contract tests for third-party APIs so schema changes fail loudly.

Laravel integration tips. With Http::get(...), pass the raw body ($response->body()) to the decoder if the decoder expects a JSON string. For queued jobs, prefer keeping the raw payload in the job and decoding when the job handles it, unless your DTO is safely serializable.

Keep the PHP matrix aligned. Since v5.1.0 dropped 8.0/8.1, make sure your composer.json require.php, your CI matrix, your Docker base images, and any Laravel versions still supporting EOL PHP move together. Running composer why-not karriere/json-decoder 5.1 is the fastest way to see which constraint is blocking the update.

Gotchas and Tips

  • v5.1.0 is a runtime-compatibility breaking change, not an API one. No new decoder features and no deprecations — only the PHP floor moved. The breakage shows up at install time (composer update fails) rather than at decode time. Budget for it as an infrastructure upgrade, not a code upgrade.
  • PHP 8.5 support is declared, not proven by your suite. Add 8.5 to your test matrix in a separate branch before making it your production runtime, and watch for deprecation notices from reflection-heavy decoding code.
  • Small ecosystem: 140 stars and 0 dependents means you should pin the version, read the changelog, and cover mappings with tests. The license is reported as NOASSERTION, so do a legal/license check before shipping in a commercial product.
  • Name mismatches are common: JSON snake_case versus PHP camelCase is handled by the binding's property argument, e.g. bindField('created_at', 'createdAt'). Do not rename DTO properties just to match the wire format.
  • Missing fields can leave typed properties uninitialized: give optional DTO properties defaults (?string $name = null) or validate required fields before decoding. Extra JSON fields are usually ignored, which can hide upstream schema changes.
  • No automatic type coercion: "123" will not become an int unless the binding/transformer handles it. Use transformers for dates, enums, money, and boolean flags.
  • Nested data needs explicit configuration: if a nested object or array comes back empty or as an array, check that you registered the correct nested binding and that the target class is decodable.
  • Private/readonly/constructor-promoted properties: confirm behavior for your package version. On PHP 8.2/8.3, readonly properties are stricter about initialization; if decoding fails silently or properties stay unset, switch to mutable DTO properties or add the appropriate binding/constructor support.
  • Reuse the decoder: bindings are configuration. Rebuilding a JsonDecoder and re-registering bindings for every payload is unnecessary and makes debugging harder.
  • Debug from the outside in: first json_decode($json, true) or log the raw payload to confirm the shape, then add bindings one field at a time. Log the raw body and endpoint on decode failures.
  • Wrap boundary exceptions: catch decoder/JSON exceptions in the adapter and rethrow with context such as endpoint, status code, and payload hash. Do not let third-party payload issues surface as generic 500s.
  • Extension points: custom transformers, binding callbacks, and central decoder registration are the main customization levers. Keep custom logic there rather than scattering json_decode() calls through the codebase.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource