1
0
mirror of https://github.com/halaxa/json-machine.git synced 2025-01-17 21:18:23 +01:00

Merge branch '0_4'

This commit is contained in:
Filip Halaxa 2020-12-01 20:58:22 +01:00
commit 0926b35cb3
8 changed files with 100 additions and 4 deletions

View File

@ -6,6 +6,13 @@
<br>
<br>
# 0.4.1
## New features
- Tracking of parsing progress
<br>
<br>
# 0.4.0
## New features
- [Custom decoder](README.md#custom-decoder)

View File

@ -89,6 +89,44 @@ use JsonMachine\JsonMachine;
$objects = JsonMachine::fromFile('path/to.json', '', new ExtJsonDecoder);
```
## Parsing JSON stream API responses
Stream API response or any other JSON stream is parsed exactly the same way as file is. The only difference
is, you use `JsonMachine::fromStream($streamResource)` for it, where `$streamResource` is the stream
resource with the JSON document. The rest is the same as with parsing files. Here are some examples of
popular http clients which support streaming responses:
### GuzzleHttp
Guzzle uses its own streams, but they can be converted back to PHP streams by calling
`\GuzzleHttp\Psr7\StreamWrapper::getResource()`. Pass the result of this function to
`JsonMachine::fromStream` function and you're set up. See working
[GuzzleHttp example](src/examples/guzzleHttp.php).
### Symfony HttpClient
A stream response of Symfony HttpClient works as iterator. And because JSON Machine is
based on iterators, the integration with Symfony HttpClient is very simple. See
[HttpClient example](src/examples/symfonyHttpClient.php).
## Tracking parsing progress
Big documents may take a while to parse. Call `JsonMachine::getPosition()` in your `foreach` to get current
count of processed bytes from the beginning. Percentage is then easy to calculate as `position / total * 100`. To get
total size of your document in bytes you may want to check:
- `strlen($document)` if you're parsing string
- `filesize($file)` if you're parsing a file
- `Content-Length` http header if you're parsing http stream response
- ... you get the point
```php
<?php
use JsonMachine\JsonMachine;
$fileSize = filesize('fruits.json');
$fruits = JsonMachine::fromFile('fruits.json');
foreach ($fruits as $name => $data) {
echo 'Progress: ' . intval($fruits->getPosition() / $fileSize * 100) . ' %';
}
```
### Parsing a subtree
If you want to iterate only `results` subtree in this `fruits.json`:
```json

View File

@ -4,7 +4,7 @@ namespace JsonMachine;
use JsonMachine\JsonDecoder\Decoder;
class JsonMachine implements \IteratorAggregate
class JsonMachine implements \IteratorAggregate, PositionAware
{
/**
* @var \Traversable
@ -21,6 +21,11 @@ class JsonMachine implements \IteratorAggregate
*/
private $jsonDecoder;
/**
* @var iterable
*/
private $parser;
/**
* @param $bytesIterator
* @param string $jsonPointer
@ -31,6 +36,8 @@ class JsonMachine implements \IteratorAggregate
$this->bytesIterator = $bytesIterator;
$this->jsonPointer = $jsonPointer;
$this->jsonDecoder = $jsonDecoder;
$this->parser = new Parser(new Lexer($this->bytesIterator), $this->jsonPointer, $this->jsonDecoder);
}
/**
@ -79,6 +86,11 @@ class JsonMachine implements \IteratorAggregate
public function getIterator()
{
return new Parser(new Lexer($this->bytesIterator), $this->jsonPointer, $this->jsonDecoder);
return $this->parser;
}
public function getPosition()
{
return $this->parser->getPosition();
}
}

View File

@ -2,7 +2,7 @@
namespace JsonMachine;
class Lexer implements \IteratorAggregate
class Lexer implements \IteratorAggregate, PositionAware
{
/** @var resource */
private $bytesIterator;

View File

@ -3,13 +3,14 @@
namespace JsonMachine;
use JsonMachine\Exception\InvalidArgumentException;
use JsonMachine\Exception\JsonMachineException;
use JsonMachine\Exception\PathNotFoundException;
use JsonMachine\Exception\SyntaxError;
use JsonMachine\Exception\UnexpectedEndSyntaxErrorException;
use JsonMachine\JsonDecoder\Decoder;
use JsonMachine\JsonDecoder\ExtJsonDecoder;
class Parser implements \IteratorAggregate
class Parser implements \IteratorAggregate, PositionAware
{
const SCALAR_CONST = 1;
const SCALAR_STRING = 2;
@ -243,4 +244,13 @@ class Parser implements \IteratorAggregate
{
throw new $exception($msg." '".$this->token."'", $this->lexer->getPosition());
}
public function getPosition()
{
if ($this->lexer instanceof PositionAware) {
return $this->lexer->getPosition();
} else {
throw new JsonMachineException('Provided lexer must implement PositionAware to call getPosition on it.');
}
}
}

15
src/PositionAware.php Normal file
View File

@ -0,0 +1,15 @@
<?php
namespace JsonMachine;
interface PositionAware
{
/**
* Returns a number of processed bytes from the beginning
*
* @return int
*/
public function getPosition();
}

View File

@ -37,4 +37,13 @@ class JsonMachineTest extends TestCase
[$passThruResult, 'fromIterable', new \ArrayIterator(['{"path": {"key', '":"value"}}']), '/path', $ptDecoder],
];
}
public function testGetPosition()
{
$expectedPosition = ['key1' => 10, 'key2' => 20];
$items = JsonMachine::fromString('{"key1":1, "key2":2} ');
foreach ($items as $key => $val) {
$this->assertSame($expectedPosition[$key], $items->getPosition());
}
}
}

View File

@ -196,6 +196,11 @@ class ParserTest extends TestCase
];
}
public function testScalarResult()
{
$result = $this->createParser('{"result":{"items": [1,2,3],"count": 3}}', '/result/count');
}
private function createParser($json, $jsonPointer = '')
{
return new Parser(new Lexer(new \ArrayIterator([$json])), $jsonPointer);