Skip to content

Commit

Permalink
Merge pull request #1 from byjg/4.0
Browse files Browse the repository at this point in the history
Add feature to filter resulting anydataset
  • Loading branch information
byjg committed Oct 31, 2021
2 parents 6013985 + 2df4a83 commit 98843c7
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 24 deletions.
129 changes: 127 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,26 @@ See more about Anydataset [here](https://opensource.byjg.com/anydataset).

```php
<?php
$json = '[{"name":"Joao","surname":"Magalhaes","age":"38"},{"name":"John","surname":"Doe","age":"20"},{"name":"Jane","surname":"Smith","age":"18"}]';
$json = <<<EOF
[
{
"name":"Joao",
"surname":"Magalhaes",
"age":"38"
},
{
"name":"John",
"surname":"Doe",
"age":"20"
},
{
"name":"Jane",
"surname":"Smith",
"age":"18"
}
]
EOF;


$dataset = new \ByJG\AnyDataset\Json\JsonDataset($json);

Expand All @@ -33,7 +52,23 @@ foreach ($iterator as $row) {

```php
<?php
$json = '{"menu": {"header": "SVG Viewer", "items": [ {"id": "Open"}, {"id": "OpenNew", "label": "Open New"} ]}}';
$json = <<<EOF
{
"menu":{
"header":"SVG Viewer",
"items":[
{
"id":"Open"
},
{
"id":"OpenNew",
"label":"Open New"
}
]
}
}
EOF;


$dataset = new \ByJG\AnyDataset\Json\JsonDataset($json);

Expand All @@ -44,6 +79,95 @@ foreach ($iterator as $row) {
}
```

### Extracting Fields

```php
$json = <<<EOF
{
"menu":{
"header":"SVG Viewer",
"items":[
{
"id":"Open",
"metadata":{
"version":"1",
"date":"NA"
}
},
{
"id":"OpenNew",
"label":"Open New",
"metadata":{
"version":"2",
"date":"2021-10-01"
}
}
]
}
}
EOF;


$dataset = new \ByJG\AnyDataset\Json\JsonDataset($json);

$iterator = $dataset->getIterator("/menu/items")
->withFields([
"name" => "id",
"version" => "metadata/version"
]);
foreach ($iterator as $row) {
echo $row->get('name'); // Print "Open", "OpenNew"
echo $row->get('version'); // Print "1", "2"
}
```

### Extract fields with wild mask

```php
$json = <<<EOF
{
"menu":{
"header":"SVG Viewer",
"items":[
{
"id":"Open",
"metadata":[
{
"version":"1",
"date":"NA"
},
{
"version":"beta",
"date":"soon"
}
]
},
{
"id":"OpenNew",
"label":"Open New",
"metadata":[
{
"version":"2",
"date":"2021-10-01"
}
]
}
]
}
}
EOF;

$iterator = $dataset->getIterator("/menu/items")
->withFields([
"name" => "id",
"version" => "metadata/*/version"
]);
foreach ($iterator as $row) {
echo $row->get('name'); // Print "Open", "OpenNew"
echo $row->get('version'); // Print ["1", "Beta"], ["2"]
}
```

## Install

Just type: `composer require "byjg/anydataset-json=4.0.*"`
Expand All @@ -56,3 +180,4 @@ vendor/bin/phpunit

----
[Open source ByJG](http://opensource.byjg.com)
k
75 changes: 53 additions & 22 deletions src/JsonIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class JsonIterator extends GenericIterator
*/
private $current = 0;

private $fieldDefinition = [];

/**
* JsonIterator constructor.
*
Expand All @@ -36,31 +38,20 @@ public function __construct($jsonObject, $path = "", $throwErr = false)
throw new InvalidArgumentException("Invalid JSON object");
}

if ($path != "") {
if ($path[0] == "/") {
$path = substr($path, 1);
}

$pathAr = explode("/", $path);

$newjsonObject = $jsonObject;
$this->current = 0;

foreach ($pathAr as $key) {
if (array_key_exists($key, $newjsonObject)) {
$newjsonObject = $newjsonObject[$key];
} elseif ($throwErr) {
throw new IteratorException("Invalid path '$path' in JSON Object");
} else {
$newjsonObject = array();
break;
}
}
$this->jsonObject = $newjsonObject;
} else {
if (empty($path)) {
$this->jsonObject = $jsonObject;
return;
}

$this->current = 0;
$this->jsonObject = $this->parseField($jsonObject, explode("/", ltrim("$path/*", "/")));
if (is_null($this->jsonObject)) {
if ($throwErr) {
throw new IteratorException("Invalid path '$path' in JSON Object");
}
$this->jsonObject = [];
}
}

public function count()
Expand Down Expand Up @@ -93,11 +84,51 @@ public function moveNext()
throw new IteratorException("No more records. Did you used hasNext() before moveNext()?");
}

return new Row($this->jsonObject[$this->current++]);
return new Row($this->parseFields($this->jsonObject[$this->current++]));
}

private function parseFields($jsonObject) {
if (empty($this->fieldDefinition)) {
return $jsonObject;
}

$valueList = [];
foreach ($this->fieldDefinition as $field => $path) {
$pathList = explode("/", ltrim($path, "/"));
$valueList[$field] = $this->parseField($jsonObject, $pathList);
}

return $valueList;
}

private function parseField($record, $pathList)
{
$value = $record;
while($pathElement = array_shift($pathList)) {
if ($pathElement == "*") {
$result = [];
foreach ($value as $item) {
$result[] = $this->parseField($item, $pathList);
}
$value = $result;
break;
}
if (!isset($value[$pathElement])) {
$value = null;
break;
}
$value = $value[$pathElement];
}
return $value;
}

public function key()
{
return $this->current;
}

public function withFields($definition) {
$this->fieldDefinition = $definition;
return $this;
}
}
88 changes: 88 additions & 0 deletions tests/JsonDatasetWithFieldsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace Tests\AnyDataset\Json;

use ByJG\AnyDataset\Core\IteratorInterface;
use ByJG\AnyDataset\Json\JsonDataset;
use ByJG\AnyDataset\Core\Row;
use PHPUnit\Framework\TestCase;

class JsonDatasetWithFieldsTest extends TestCase
{

const JSON_OK = '{"menu": {"header": "SVG Viewer", "items": [ {"id": "Open", "metadata": {"version": "1", "date": "NA"} }, {"id": "OpenNew", "label": "Open New", "metadata": {"version": "2", "date": "2021-10-01"}} ]}}';

protected $iterator;
protected $arrTest = array();

// Run before each test case
public function setUp()
{
$this->arrTest = array();
$this->arrTest[] = array("name" => "Open", "version" => "1");
$this->arrTest[] = array("name" => "OpenNew", "version" => "2");

$jsonDataset = new JsonDataset(JsonDatasetWithFieldsTest::JSON_OK);
$this->iterator = $jsonDataset->getIterator("/menu/items")->withFields(["name" => "id", "version" => "metadata/version"]);
}

// Run end each test case
public function teardown()
{
$this->iterator = null;
}

public function testcreateJsonIterator()
{
$this->assertTrue($this->iterator instanceof IteratorInterface); //, "Resultant object must be an interator");
$this->assertTrue($this->iterator->hasNext()); // "hasNext() method must be true");
$this->assertEquals(2, $this->iterator->Count()); //, "Count() method must return 2");
}

public function testnavigateJsonIterator()
{
$count = 0;
while ($this->iterator->hasNext()) {
$this->assertSingleRow($this->iterator->moveNext(), $count++);
}

$this->assertEquals(2, $this->iterator->count(), 2); //, "Count() method must return 2");
}

public function testnavigateJsonIterator2()
{
$this->assertEquals($this->arrTest, $this->iterator->toArray());
}

public function testArrayFieldDefinition()
{
$jsonDataset = new JsonDataset('{"menu": {"header": "SVG Viewer", "items": [ {"id": "Open", "metadata": [{"version": "1", "date": "NA"}, {"version": "beta", "date": "soon"}] }, {"id": "OpenNew", "label": "Open New", "metadata": [{"version": "2", "date": "2021-10-01"}] } ]}}');

$iterator = $jsonDataset->getIterator("/menu/items")->withFields(["name" => "id", "version" => "metadata/*/version"]);

$this->assertEquals([
["name" => "Open", "version" => ["1", "beta"]],
["name" => "OpenNew", "version" => ["2"]]
], $iterator->toArray());
}

public function testNonExistentFieldDefinition()
{
$jsonDataset = new JsonDataset(JsonDatasetWithFieldsTest::JSON_OK);
$iterator = $jsonDataset->getIterator("/menu/items")->withFields(["name" => "none", "version" => "metadata/version/nonexistantfield"]);

$this->assertEquals([
["name" => null, "version" => null],
["name" => null, "version" => null]
], $iterator->toArray());
}

/**
* @param Row $sr
*/
public function assertSingleRow($sr, $count)
{
$this->assertEquals($sr->get("name"), $this->arrTest[$count]["name"]);
$this->assertEquals($sr->get("version"), $this->arrTest[$count]["version"]);
}
}

0 comments on commit 98843c7

Please sign in to comment.