Weave Code
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 Streamer Laravel Package

symfony/json-streamer

Symfony JsonStreamer reads and writes data structures to and from JSON streams efficiently. Ideal for streaming large JSON payloads with low memory usage, integrating with Symfony Serializer to parse or generate JSON incrementally.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require symfony/json-streamer
    

    Ensure your project uses PHP 8.4+ (Laravel 12+ recommended).

  2. First Use Case: Stream a large JSON response from a Laravel controller:

    use Symfony\Component\Serializer\Encoder\JsonStreamEncoder;
    use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
    use Symfony\Component\Serializer\Serializer;
    
    public function streamJsonResponse()
    {
        $encoder = new JsonStreamEncoder();
        $normalizer = new ObjectNormalizer();
        $serializer = new Serializer([$normalizer], [$encoder]);
    
        $resource = new \stdClass(); // Replace with your data structure
        $resource->largeArray = range(1, 1_000_000); // Simulate large data
    
        return response()->stream(
            fn () => $serializer->stream($resource, 'json_stream')
        );
    }
    
  3. Key Classes to Explore:

    • JsonStreamEncoder: Core encoder for streaming JSON.
    • ObjectNormalizer: Maps objects to JSON (customize with context or groups).
    • Serializer: Combines normalizers and encoders.
    • JsonStreamer: Low-level API for advanced use cases (e.g., custom value transformers).
  4. Documentation: Start with Symfony’s official docs and focus on:

    • Streaming JSON responses (json_stream format).
    • Handling circular references (via circular_reference_handler).
    • Custom value transformers (for domain-specific logic).

Implementation Patterns

1. Streaming JSON Responses

Workflow:

  • Use json_stream format to avoid loading entire payloads into memory.
  • Ideal for APIs returning large datasets (e.g., paginated results, logs, or telemetry).

Example:

public function streamPaginatedData()
{
    $serializer = $this->getSerializer(); // Configured with JsonStreamEncoder
    $data = $this->fetchLargeDataset(); // Generator or iterable

    return response()->stream(
        fn () => $serializer->stream($data, 'json_stream', [
            'json_encode_options' => JSON_PRETTY_PRINT,
        ])
    );
}

Laravel Integration:

  • Pair with Symfony\Component\HttpFoundation\StreamedResponse for fine-grained control:
    $response = new StreamedResponse();
    $response->setCallback(fn () => $serializer->stream($data, 'json_stream'));
    return $response;
    

2. Deserializing Large JSON Streams

Workflow:

  • Stream JSON into Laravel from external sources (e.g., Kafka, S3, or HTTP).
  • Use JsonStreamEncoder in reverse to parse incrementally.

Example:

use Symfony\Component\Serializer\Encoder\JsonStreamDecoder;

public function processStreamingJson()
{
    $decoder = new JsonStreamDecoder();
    $stream = fopen('large.json', 'r'); // Or a network stream

    $decoder->decodeStream($stream, 'json_stream', function ($data) {
        // Process each chunk as it arrives
        $this->handleChunk($data);
    });
}

Use Cases:

  • ETL pipelines: Process JSON files larger than RAM.
  • Real-time APIs: Stream JSON from WebSockets or SSE.
  • Event sourcing: Deserialize event streams without loading entire history.

3. Custom Value Transformers

Workflow:

  • Extend ObjectNormalizer to handle domain-specific logic (e.g., encrypting fields, formatting dates).
  • Register transformers via normalizer->setValueTransformer().

Example:

use Symfony\Component\Serializer\Normalizer\ValueTransformerInterface;

class EncryptedFieldTransformer implements ValueTransformerInterface
{
    public function transform($value, string $format = null, array $context = [])
    {
        return $value ? base64_encode($value) : null;
    }

    public function supportsTransformation($data, string $format = null, array $context = [])
    {
        return is_string($data) && isset($context['encrypt']);
    }
}

// Usage:
$normalizer = new ObjectNormalizer();
$normalizer->setValueTransformer(new EncryptedFieldTransformer());
$serializer = new Serializer([$normalizer], [$encoder]);

Laravel-Specific:

  • Combine with Laravel’s App\Services\Encryption or Carbon for seamless integration.

4. Handling Circular References

Workflow:

  • Use circular_reference_handler to avoid infinite loops in self-referencing objects.

Example:

use Symfony\Component\Serializer\Normalizer\CircularReferenceHandlerInterface;

class LaravelCircularReferenceHandler implements CircularReferenceHandlerInterface
{
    public function getReplacement(array $data, string $format, array $context)
    {
        return ['$ref' => spl_object_hash($data)];
    }

    public function getResolved(array $data, string $format, array $context)
    {
        return null; // Or resolve via Laravel's cache
    }
}

// Usage:
$normalizer = new ObjectNormalizer();
$normalizer->setCircularReferenceHandler(new LaravelCircularReferenceHandler());

5. Synthetic Properties

Workflow:

  • Add computed properties to JSON output without modifying the original object.

Example:

use Symfony\Component\Serializer\Annotation\Synthetic;

class User
{
    #[Synthetic]
    public function getFullName(): string
    {
        return "{$this->firstName} {$this->lastName}";
    }
}

Laravel Integration:

  • Use attributes (PHP 8+) or metadata for Laravel models:
    $normalizer->setSyntheticPropertyGenerator(
        fn (object $object, string $format, array $context) => [
            'full_name' => "{$object->first_name} {$object->last_name}",
        ]
    );
    

Gotchas and Tips

Pitfalls

  1. PHP 8.4+ Requirement:

    • Fails silently on older PHP versions. Use composer.json overrides or CI checks:
      "config": {
          "platform-check": true,
          "platform": {
              "php": "8.4.0"
          }
      }
      
  2. Memory Leaks in Early Versions:

    • Pre-v8.0.4 had exponential memory growth for recursive structures. Always update:
      composer update symfony/json-streamer
      
  3. Circular References Without Handler:

    • Throws SerializationException if not configured. Test with:
      $user = new User();
      $user->self = $user; // Circular reference
      $serializer->serialize($user, 'json_stream'); // Fails without handler
      
  4. Null Property Handling:

    • Disabled by default (include_null_properties: false). Explicitly enable:
      $encoder->setIncludeNullProperties(true);
      
  5. Generator vs. Iterable:

    • Generators (yield) work best; arrays or Iterator may cause memory spikes.

Debugging Tips

  1. Validate JSON Output: Use JsonStreamEncoder::decode() to verify round-trip serialization:

    $encoded = $serializer->serialize($data, 'json_stream');
    $decoded = $serializer->deserialize($encoded, get_class($data), 'json_stream');
    
  2. Log Stream Chunks: Intercept chunks for debugging:

    $serializer->stream($data, 'json_stream', [], function ($chunk) {
        \Log::debug('Stream chunk:', ['chunk' => $chunk]);
    });
    
  3. Check Cache Issues:

    • Symfony caches generated code. Clear cache after schema changes:
      php artisan cache:clear
      
  4. Performance Profiling:

    • Compare memory usage with memory_get_usage():
      $start = memory_get_usage();
      $serializer->stream($data, 'json_stream');
      $end = memory_get_usage();
      \Log::info("Memory used: " . ($end - $start) / 1024 / 1024 . "MB");
      

Extension Points

  1. Custom Encoders: Extend JsonStreamEncoder for domain-specific formats (e.g., Avro, Protobuf):

    class CustomJsonStreamEncoder extends JsonStreamEncoder
    {
        protected function encode($data, string $format, array $context)
        {
            // Custom logic
        }
    }
    
  2. Attribute-Based Configuration: Use Symfony’s #[Groups] or #[SerializedName] with Laravel’s HasAttributes:

    use Symfony\Component\Serializer\Annotation\Groups;
    
    class Product
    {
        #[Groups(['api'])]
        public string $name;
    }
    
  3. Laravel Service Provider: Bind the serializer to the container for reuse:

    // app/Providers/AppServiceProvider.php
    public function register
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport