Skip to content

Instantly share code, notes, and snippets.

@hugowetterberg
Created November 29, 2012 09:01
Show Gist options
  • Save hugowetterberg/4167706 to your computer and use it in GitHub Desktop.
Save hugowetterberg/4167706 to your computer and use it in GitHub Desktop.
Streaming JSON serializer

The JsonStream class

Streaming JSON serializer.

Usage:

try {
  $s = new JsonStream(STDOUT);
  $s->startObject();
  $s->writeProperty('name', 'John');
  $s->writeProperty('age', 32);
  $s->writeKey('pets')
    ->startArray()
      ->write('Garfield')
        ->write('Ido')
    ->end();
  $s->writeProperty('data', array('list' => array(), 'of' => 'precompiled stuff'));
  $s->close();
}
catch (JsonStreamException $e) {
  error_log(var_export($e->getJsonState(), TRUE));
  throw $e;
}
<?php
define('JSON_STREAM_ERROR_OOO', 1);
class JsonStreamException extends Exception {
private $json_state;
public function __construct($message = "", $code = 0, $state = NULL, $previous = NULL) {
parent::__construct($message, $code, $previous);
$this->json_state = $state;
}
public function getJsonState() {
return $this->json_state;
}
}
class JsonStream {
private $fd;
private $state;
public function __construct($fd) {
$this->fd = $fd;
$this->state = array();
}
private function currentState() {
return current(array_slice($this->state, -1));
}
private function pushState($state) {
array_push($this->state, $state);
}
private function popState() {
return array_pop($this->state);
}
private function acceptsValue() {
$state = $this->currentState();
return
!$state ||
$state->type == 'array' ||
($state->type == 'object' && $state->at_key);
}
public function startArray() {
$this->writeLiteral('[');
$this->pushState((object)array(
'type' => 'array',
'has_items' => FALSE
));
return $this;
}
public function startObject() {
$this->writeLiteral('{');
$this->pushState((object)array(
'type' => 'object',
'at_key' => FALSE,
'has_items' => FALSE,
));
return $this;
}
public function end() {
$state = $this->popState();
if (!$state || ($state->type == 'object' && $state->at_key)) {
throw new JsonStreamException("Operation out of order.", JSON_STREAM_ERROR_OOO, $state);
}
if ($state->type == 'object') {
fwrite($this->fd, '}');
}
else if ($state->type == 'array') {
fwrite($this->fd, ']');
}
}
public function writeProperty($key, $value) {
$this->writeKey($key);
$this->write($value);
return $this;
}
public function writeKey($key) {
$state = $this->currentState();
if ($state->type != 'object' || $state->at_key) {
throw new JsonStreamException("Operation out of order.", JSON_STREAM_ERROR_OOO, $state);
}
if ($state->has_items) {
fwrite($this->fd, ',');
}
$state->has_items = TRUE;
$state->at_key = TRUE;
fwrite($this->fd, json_encode($key));
fwrite($this->fd, ':');
return $this;
}
public function writeLiteral($literal) {
$state = $this->currentState();
if (!$this->acceptsValue()) {
throw new JsonStreamException("Operation out of order.", JSON_STREAM_ERROR_OOO, $state);
}
if ($state) {
if ($state->type == 'object') {
$state->at_key = FALSE;
}
else if ($state->type == 'array') {
if ($state->has_items) {
fwrite($this->fd, ',');
}
$state->has_items = TRUE;
}
}
fwrite($this->fd, $literal);
return $this;
}
public function write($value) {
$this->writeLiteral(json_encode($value));
return $this;
}
public function close($newline = FALSE) {
while ($this->currentState()) {
$this->end();
}
if ($newline) {
fwrite($this->fd, "\n");
}
fclose($this->fd);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment