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:
commit
0926b35cb3
@ -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)
|
||||
|
38
README.md
38
README.md
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace JsonMachine;
|
||||
|
||||
class Lexer implements \IteratorAggregate
|
||||
class Lexer implements \IteratorAggregate, PositionAware
|
||||
{
|
||||
/** @var resource */
|
||||
private $bytesIterator;
|
||||
|
@ -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
15
src/PositionAware.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace JsonMachine;
|
||||
|
||||
|
||||
interface PositionAware
|
||||
{
|
||||
/**
|
||||
* Returns a number of processed bytes from the beginning
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPosition();
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user