From a38c723ab7b4a8bea62c0aeeb6769f5fd6217a79 Mon Sep 17 00:00:00 2001 From: Markus Lanthaler Date: Mon, 4 Feb 2013 20:06:03 +0100 Subject: [PATCH] Introduce JsonLdSerializable interface and make everything serializable This addresses #15. --- Document.php | 28 +++++++++- Graph.php | 19 ++++++- Node.php | 53 +++++++++++++++++- README.md | 7 ++- Test/GraphTest.php | 130 ++++++++++++++++++++++++++++++++++++++++++++- Value.php | 14 +---- 6 files changed, 232 insertions(+), 19 deletions(-) diff --git a/Document.php b/Document.php index 20d26e7..339b07a 100644 --- a/Document.php +++ b/Document.php @@ -9,6 +9,7 @@ namespace ML\JsonLD; +use stdClass as Object; use ML\IRI\IRI; /** @@ -18,7 +19,7 @@ * * @author Markus Lanthaler */ -class Document implements DocumentInterface +class Document implements DocumentInterface, JsonLdSerializable { /** * @var IRI The document's IRI @@ -171,4 +172,29 @@ public function removeGraph($graph = null) unset($this->namedGraphs[$name]); } } + + /** + * {@inheritdoc} + */ + public function toJsonLd($useNativeTypes = true) + { + $defGraph = $this->defaultGraph->toJsonLd($useNativeTypes); + + if (0 === count($this->namedGraphs)) { + return $defGraph; + } + + foreach ($this->namedGraphs as $graphName => $graph) { + $namedGraph = new Object(); + $namedGraph->{'@id'} = $graphName; + $namedGraph->{'@graph'} = $graph->toJsonLd($useNativeTypes); + + $defGraph[] = $namedGraph; + } + + $document = new Object(); + $document->{'@graph'} = $defGraph; + + return array($document); + } } diff --git a/Graph.php b/Graph.php index 4593781..23362c5 100644 --- a/Graph.php +++ b/Graph.php @@ -16,7 +16,7 @@ * * @author Markus Lanthaler */ -class Graph implements GraphInterface +class Graph implements GraphInterface, JsonLdSerializable { /** * @var DocumentInterface The document this graph belongs to. @@ -219,6 +219,23 @@ public function merge(GraphInterface $graph) return $this; } + /** + * {@inheritdoc} + */ + public function toJsonLd($useNativeTypes = true) + { + // Bring nodes into a deterministic order + $nodes = $this->nodes; + ksort($nodes); + $nodes = array_values($nodes); + + $serializeNode = function ($node) use ($useNativeTypes) { + return $node->toJsonLd($useNativeTypes); + }; + + return array_map($serializeNode, $nodes); + } + /** * Create a new blank node identifier unique to the document. * diff --git a/Node.php b/Node.php index 546eef9..343c780 100644 --- a/Node.php +++ b/Node.php @@ -9,12 +9,14 @@ namespace ML\JsonLD; +use stdClass as Object; + /** * A Node represents a node in a JSON-LD graph. * * @author Markus Lanthaler */ -class Node implements NodeInterface +class Node implements NodeInterface, JsonLdSerializable { /** The @type constant. */ const TYPE = '@type'; @@ -356,6 +358,55 @@ public function equals(NodeInterface $other) return $this === $other; } + /** + * {@inheritdoc} + */ + public function toJsonLd($useNativeTypes = true) + { + $node = new \stdClass(); + + // Only label blank nodes if other nodes point to it + if ((false === $this->isBlankNode()) || (count($this->getReverseProperties()) > 0)) { + $node->{'@id'} = $this->getId(); + } + + $properties = $this->getProperties(); + + foreach ($properties as $prop => $values) { + if (false === is_array($values)) { + $values = array($values); + } + + if (self::TYPE === $prop) { + $node->{'@type'} = array(); + foreach ($values as $val) { + $node->{'@type'}[] = $val->getId(); + } + + continue; + } + + $node->{$prop} = array(); + + foreach ($values as $value) { + if ($value instanceof NodeInterface) { + $ref = new \stdClass(); + $ref->{'@id'} = $value->getId(); + $node->{$prop}[] = $ref; + } elseif (is_object($value)) { // language-tagged string or typed value + $node->{$prop}[] = $value->toJsonLd($useNativeTypes); + } else { + $val = new Object(); + $val->{'@value'} = $value; + $node->{$prop}[] = $val; + } + } + + } + + return $node; + } + /** * Add a reverse property. * diff --git a/README.md b/README.md index 7d4dd97..19fcfa3 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ $doc = JsonLD::getDocument('document.jsonld'); // get the default graph $graph = $doc->getGraph(); -// get all nodes in the document +// get all nodes in the graph $nodes = $graph->getNodes(); // retrieve a node by ID @@ -99,7 +99,7 @@ $node = $graph->getNode('http://example.com/node1'); // get a property $node->getProperty('http://example.com/vocab/name'); -// add a new blank node to the document +// add a new blank node to the graph $newNode = $graph->createNode(); // link the new blank node to the existing node @@ -107,6 +107,9 @@ $node->addPropertyValue('http://example.com/vocab/link', $newNode); // even reverse properties are supported; this returns $newNode $node->getReverseProperty('http://example.com/vocab/link'); + +// serialize the graph and convert it to a string +$serialized = JsonLD::toString($graph->toJsonLd()); ``` diff --git a/Test/GraphTest.php b/Test/GraphTest.php index 78a141b..b0ea2c3 100644 --- a/Test/GraphTest.php +++ b/Test/GraphTest.php @@ -718,7 +718,8 @@ public function testMerge() $this->assertEquals( array('2', 'and a different name in graph 2'), - $node2->getProperty('http://vocab.com/name'), 'n2->name' + $node2->getProperty('http://vocab.com/name'), + 'n2->name' ); $this->assertSame(array($node3, $node4), $node2->getProperty('http://vocab.com/link'), 'n2 -link-> n3 & n4'); @@ -756,4 +757,131 @@ public function testMerge() $this->assertSame($graph2, $n->getGraph(), 'linked to graph (graph 2)'); } } + + /** + * Tests the serialization of nodes + */ + public function testSerializeNode() + { + $expected = JsonLD::parse( + '{ + "@id": "http://example.com/node/1", + "@type": [ "http://vocab.com/type/node" ], + "http://vocab.com/name": [ { "@value": "1" } ], + "http://vocab.com/link": [ { "@id": "http://example.com/node/2" } ], + "http://vocab.com/contains": [ { "@id": "_:b0" } ] + }' + ); + + $node1 = $this->graph->getNode('http://example.com/node/1'); + $this->assertEquals($expected, $node1->toJsonLd(), 'Serialize node 1'); + } + + /** + * Tests the serialization of graphs + */ + public function testSerializeGraph() + { + // This is the expanded and flattened version of the test document + // (the blank node labels have been renamed from _:t... to _:b...) + $expected = JsonLD::parse( + '[{ + "@id": "_:b0", + "http://vocab.com/nested": [{ + "@value": "1.1" + }] + }, { + "@id": "_:b1", + "http://vocab.com/nested": [{ + "@value": "2.1" + }] + }, { + "@id": "_:b2", + "http://vocab.com/nested": [{ + "@value": "2.2" + }] + }, { + "@id": "_:b3", + "http://vocab.com/nested": [{ + "@value": "3.1" + }] + }, { + "@id": "http://example.com/node/1", + "@type": ["http://vocab.com/type/node"], + "http://vocab.com/contains": [{ + "@id": "_:b0" + }], + "http://vocab.com/link": [{ + "@id": "http://example.com/node/2" + }], + "http://vocab.com/name": [{ + "@value": "1" + }] + }, { + "@id": "http://example.com/node/2", + "@type": ["http://vocab.com/type/nodeWithAliases"], + "http://vocab.com/aliases": [{ + "@value": "node2" + }, { + "@value": 2 + }], + "http://vocab.com/contains": [{ + "@id": "_:b1" + }, { + "@id": "_:b2" + }], + "http://vocab.com/lang": [{ + "@language": "en", + "@value": "language-tagged string" + }], + "http://vocab.com/link": [{ + "@id": "http://example.com/node/3" + }], + "http://vocab.com/name": [{ + "@value": "2" + }], + "http://vocab.com/typed": [{ + "@type": "http://vocab.com/type/datatype", + "@value": "typed value" + }] + }, { + "@id": "http://example.com/node/3", + "@type": ["http://vocab.com/type/node"], + "http://vocab.com/contains": [{ + "@id": "_:b3" + }], + "http://vocab.com/lang": [{ + "@language": "en", + "@value": "language-tagged string: en" + }, { + "@language": "de", + "@value": "language-tagged string: de" + }], + "http://vocab.com/link": [{ + "@id": "http://example.com/node/1" + }], + "http://vocab.com/name": [{ + "@value": "3" + }], + "http://vocab.com/typed": [{ + "@type": "http://vocab.com/type/datatype", + "@value": "typed value" + }, { + "@language": "ex:/type/otherDataType", + "@value": "typed value" + }, { + "@language": "ex:/type/datatype", + "@value": "typed value" + }] + }, { + "@id": "http://vocab.com/type/datatype" + }, { + "@id": "http://vocab.com/type/node" + }, { + "@id": "http://vocab.com/type/nodeWithAliases" + }]' + ); + + $this->assertEquals($expected, $this->graph->toJsonLd(false), 'Serialize graph'); + } } diff --git a/Value.php b/Value.php index 06b3ddd..1370c15 100644 --- a/Value.php +++ b/Value.php @@ -17,7 +17,7 @@ * * @author Markus Lanthaler */ -abstract class Value +abstract class Value implements JsonLdSerializable { /** * The value in the form of a string @@ -99,18 +99,6 @@ public static function fromJsonLd(Object $element) return new TypedValue($value, (null === $type) ? RdfConstants::XSD_STRING : $type); } - /** - * Convert this instance to a JSON-LD element in expanded form - * - * @param boolean $useNativeTypes If set to true, native types are used - * for xsd:integer, xsd:double, and - * xsd:boolean, otherwise typed strings - * will be used instead. - * - * @return string|integer|float|boolean|Object The JSON-LD element. - */ - abstract public function toJsonLd($useNativeTypes = true); - /** * Compares this instance to the specified value. *