symfony/json-streamer
Stream JSON efficiently with Symfony JsonStreamer. Read and write large JSON structures incrementally from streams to reduce memory usage, with powerful helpers for streaming serialization/deserialization and handling big payloads in real time.
Installation:
composer require symfony/serializer symfony/json-streamer
Serializer component.First Use Case:
Replace memory-intensive json_decode() with streaming for large JSON files:
use Symfony\Component\Serializer\JsonStreamer;
$streamer = new JsonStreamer();
$streamer->read('large_dataset.json', function ($data) {
// Process each chunk incrementally
// Example: Save to DB, transform, or emit via WebSocket
});
Where to Look First:
JsonStreamer::read() and JsonStreamer::write() methods for core functionality.StreamedResponse for HTTP streaming integration.$streamer = new JsonStreamer();
$streamer->read('data.json', function ($item) {
// Process $item (e.g., store in DB, emit via Pusher)
}, [
'include_null_properties' => true, // Optional: Include null values
]);
// app/Services/JsonStreamerService.php
class JsonStreamerService {
public function streamFile(string $path, callable $callback): void {
$streamer = new JsonStreamer();
$streamer->read($path, $callback);
}
}
use Symfony\Component\HttpFoundation\StreamedResponse;
return new StreamedResponse(function () use ($data) {
$streamer = new JsonStreamer();
$streamer->write($data, $this->getEmitter());
}, 200, ['Content-Type' => 'application/json']);
Http\StreamedResponse (Laravel 10+) or Illuminate\Http\StreamedResponse.$streamer = new JsonStreamer();
$streamer->setValueTransformer('App\\Entity\\User', function ($value, $class, $format) {
return $value->toArray(); // Convert entity to array
});
$streamer = new JsonStreamer();
$streamer->write($rootObject, $emitter, [
'ignore_circular_references' => false, // Default: true
]);
// In an Artisan command
$streamer = new JsonStreamer();
$streamer->read('input.json', function ($item) {
// Process and save to CSV/DB
});
Service Container Binding:
// config/app.php
'bindings' => [
Symfony\Component\Serializer\JsonStreamer::class => function () {
return new JsonStreamer();
},
];
Queue Jobs for Large Files:
// app/Jobs/ProcessJsonStream.php
public function handle() {
$streamer = resolve(JsonStreamer::class);
$streamer->read(storage_path('large.json'), [$this, 'processChunk']);
}
public function processChunk($data) {
// Process incrementally
}
Middleware for API Streaming:
// app/Http/Middleware/StreamJsonResponse.php
public function handle($request, Closure $next) {
if ($request->wantsJson() && $request->query('stream')) {
return $this->streamResponse($request);
}
return $next($request);
}
private function streamResponse($request) {
$data = $this->fetchLargeData();
return new StreamedResponse(function () use ($data) {
$streamer = new JsonStreamer();
$streamer->write($data, $this->getEmitter());
});
}
JsonStreamer with Symfony’s Serializer to cache class metadata:
$serializer = Serializer::create([], [new JsonStreamer()]);
$streamer->write($object, $emitter, ['lazy' => true]);
Memory Leaks:
$streamer->read($file, function ($data) { /* ... */ });
$streamer->close(); // Manually close if needed
Circular References:
ignore_circular_references: true).ignore_circular_references: false and handle cycles manually:
$streamer->write($object, $emitter, ['ignore_circular_references' => false]);
Null Properties:
JsonStreamer skips null properties.include_null_properties:
$streamer->read($file, $callback, ['include_null_properties' => true]);
PHP 8.4+ Compatibility:
Generator Overhead:
Validate JSON Structure:
Use JsonStreamer::read() with a debug callback to inspect chunks:
$streamer->read('data.json', function ($chunk) {
\Log::debug('Chunk:', ['data' => $chunk]);
});
Check for Lazy Loading Issues: If objects aren’t hydrated correctly, ensure:
Serializer is properly configured.Normalizer interferes with lazy loading.Monitor Memory Usage:
$streamer->read($file, function ($data) {
\Log::info('Memory usage:', memory_get_usage(true));
});
Custom Emitters:
Extend JsonStreamer to support custom output (e.g., WebSocket, Kafka):
$streamer = new JsonStreamer();
$streamer->write($data, new CustomEmitter());
Value Transformers: Override default serialization/deserialization:
$streamer->setValueTransformer('App\\Model\\User', function ($value) {
return ['id' => $value->id, 'name' => $value->name];
});
Event Listeners:
Use Symfony’s EventDispatcher to hook into streaming events (e.g., pre/post chunk processing).
Custom Metadata:
Annotate classes with #[Serializer\Groups] or #[Serializer\Ignore] to control serialization:
#[Serializer\Ignore]
private $sensitiveData;
Service Provider Setup:
If using Symfony’s Serializer, register it in AppServiceProvider:
public function register() {
$this->app->singleton(Symfony\Component\Serializer\Serializer::class, function () {
return Serializer::create([], [new JsonStreamer()]);
});
}
Caching:
Laravel’s cache can interfere with JsonStreamer's internal caching. Use null driver for testing:
$streamer = new JsonStreamer();
$streamer->setCache(new \Symfony\Component\Cache\NullCache());
Testing:
Mock JsonStreamer
How can I help you explore Laravel packages today?