Implement toRDF.
This commit is contained in:
parent
273c9c88de
commit
ad531248c6
2 changed files with 221 additions and 21 deletions
|
@ -282,6 +282,13 @@ class TestRunner {
|
|||
$test->expect = read_test_json($test->expect, $filepath);
|
||||
$result = jsonld_from_rdf($input, $options);
|
||||
}
|
||||
else if(in_array('jld:ToRDFTest', $type)) {
|
||||
$this->test($test->name);
|
||||
$input = read_test_json($test->input, $filepath);
|
||||
$test->expect = read_test_nquads($test->expect, $filepath);
|
||||
$options['format'] = 'application/nquads';
|
||||
$result = jsonld_to_rdf($input, $options);
|
||||
}
|
||||
else {
|
||||
echo "Skipping test \"{$test->name}\" of type: " .
|
||||
json_encode($type) . $eol;
|
||||
|
|
235
jsonld.php
235
jsonld.php
|
@ -110,7 +110,7 @@ function jsonld_normalize($input, $options=array()) {
|
|||
* @param mixed $statements a serialized string of RDF statements in a format
|
||||
* specified by the format option or an array of the RDF statements
|
||||
* to convert.
|
||||
* @param assoc [options] the options to use:
|
||||
* @param assoc [$options] the options to use:
|
||||
* [format] the format if input is a string:
|
||||
* 'application/nquads' for N-Quads (default).
|
||||
* [notType] true to use rdf:type, false to use @type (default).
|
||||
|
@ -127,6 +127,9 @@ function jsonld_from_rdf($input, $options=array()) {
|
|||
*
|
||||
* @param mixed $input the JSON-LD object.
|
||||
* @param assoc [$options] the options to use:
|
||||
* [base] the base IRI to use.
|
||||
* [format] the format to use to output a string:
|
||||
* 'application/nquads' for N-Quads (default).
|
||||
* [resolver(url, callback(err, jsonCtx))] the URL resolver to use.
|
||||
*
|
||||
* @return array all RDF statements in the JSON-LD object.
|
||||
|
@ -415,7 +418,7 @@ class JsonLdProcessor {
|
|||
* @param mixed $statements a serialized string of RDF statements in a format
|
||||
* specified by the format option or an array of the RDF statements
|
||||
* to convert.
|
||||
* @param assoc options the options to use:
|
||||
* @param assoc $options the options to use:
|
||||
* [format] the format if input is a string:
|
||||
* 'application/nquads' for N-Quads (default).
|
||||
* [notType] true to use rdf:type, false to use @type (default).
|
||||
|
@ -448,9 +451,12 @@ class JsonLdProcessor {
|
|||
*
|
||||
* @param mixed $input the JSON-LD object.
|
||||
* @param assoc $options the options to use:
|
||||
* [base] the base IRI to use.
|
||||
* [format] the format to use to output a string:
|
||||
* 'application/nquads' for N-Quads (default).
|
||||
* [resolver(url, callback(err, jsonCtx))] the URL resolver to use.
|
||||
*
|
||||
* @return array the RDF statements.
|
||||
* @return array all RDF statements in the JSON-LD object.
|
||||
*/
|
||||
public function toRDF($input, $options) {
|
||||
// set default options
|
||||
|
@ -458,12 +464,40 @@ class JsonLdProcessor {
|
|||
// FIXME: implement jsonld_resolve_url
|
||||
isset($options['resolver']) or $options['resolver'] = 'jsonld_resolve_url';
|
||||
|
||||
// resolve all @context URLs in the input
|
||||
$input = self::copy($input);
|
||||
$this->_resolveUrls($input, $options['resolver']);
|
||||
try {
|
||||
// expand input
|
||||
$expanded = $this->expand($input, $options);
|
||||
}
|
||||
catch(JsonLdException $e) {
|
||||
throw new JsonLdException(
|
||||
'Could not expand input before conversion to RDF.',
|
||||
'jsonld.RdfError', $e);
|
||||
}
|
||||
|
||||
// get RDF statements
|
||||
$namer = new UniqueNamer('_:t');
|
||||
$statements = array();
|
||||
$this->_toRDF($expanded, $namer, null, null, null, $statements);
|
||||
|
||||
// convert to output format
|
||||
if(isset($options['format'])) {
|
||||
// supported formats
|
||||
if($options['format'] === 'application/nquads') {
|
||||
$nquads = '';
|
||||
foreach($statements as $statement) {
|
||||
$nquads .= $this->_toNQuad($statement);
|
||||
}
|
||||
$statements = $nquads;
|
||||
}
|
||||
else {
|
||||
throw new JsonLdException(
|
||||
'Unknown output format.',
|
||||
'jsonld.UnknownFormat', array('format' => $options['format']));
|
||||
}
|
||||
}
|
||||
|
||||
// output RDF statements
|
||||
return $this->_toRDF($input);
|
||||
return $statements;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1516,15 +1550,174 @@ class JsonLdProcessor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Outputs the RDF statements found in the given JSON-LD object.
|
||||
* Outputs the RDF statements found in the given JSON-LD element.
|
||||
*
|
||||
* @param mixed $input the JSON-LD object.
|
||||
*
|
||||
* @return array the RDF statements.
|
||||
* @param mixed element the JSON-LD element.
|
||||
* @param UniqueNamer namer the UniqueNamer for assigning bnode names.
|
||||
* @param mixed subject the active subject.
|
||||
* @param mixed property the active property.
|
||||
* @param mixed graph the graph name.
|
||||
* @param &array statements the array to add statements to.
|
||||
*/
|
||||
protected function _toRDF($input) {
|
||||
// FIXME: implement
|
||||
throw new JsonLdException('Not implemented', 'jsonld.NotImplemented');
|
||||
protected function _toRDF(
|
||||
$element, $namer, $subject, $property, $graph, &$statements) {
|
||||
// recurse into arrays
|
||||
if(is_array($element)) {
|
||||
foreach($element as $e) {
|
||||
$this->_toRDF($e, $namer, $subject, $property, $graph, $statements);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(is_object($element)) {
|
||||
// convert @value to object
|
||||
if(self::_isValue($element)) {
|
||||
$object = (object)array(
|
||||
'nominalValue' => $element->{'@value'},
|
||||
'interfaceName' => 'LiteralNode');
|
||||
|
||||
if(property_exists($element, '@type')) {
|
||||
$object->datatype = (object)array(
|
||||
'nominalValue' => $element->{'@type'},
|
||||
'interfaceName' => 'IRI');
|
||||
}
|
||||
else if(property_exists($element, '@language')) {
|
||||
$object->language = $element->{'@language'};
|
||||
}
|
||||
|
||||
// emit literal
|
||||
$statement = (object)array(
|
||||
'subject' => self::copy($subject),
|
||||
'property' => self::copy($property),
|
||||
'object' => $object);
|
||||
if($graph !== null) {
|
||||
$statement->name = $graph;
|
||||
}
|
||||
$statements[] = $statement;
|
||||
return;
|
||||
}
|
||||
|
||||
// convert @list
|
||||
if(self::_isList($element)) {
|
||||
$list = $this->_makeLinkedList($element);
|
||||
$this->_toRDF($list, $namer, $subject, $property, $graph, $statements);
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: element must be a subject
|
||||
|
||||
// get subject @id (generate one if it is a bnode)
|
||||
$id = property_exists($element, '@id') ? $element->{'@id'} : null;
|
||||
$is_bnode = self::_isBlankNode($element);
|
||||
if($is_bnode) {
|
||||
$id = $namer->getName($id);
|
||||
}
|
||||
|
||||
// create object
|
||||
$object = (object)array(
|
||||
'nominalValue' => $id,
|
||||
'interfaceName' => $is_bnode ? 'BlankNode' : 'IRI');
|
||||
|
||||
// emit statement if subject isn't null
|
||||
if($subject !== null) {
|
||||
$statement = (object)array(
|
||||
'subject' => self::copy($subject),
|
||||
'property' => self::copy($property),
|
||||
'object' => self::copy($object));
|
||||
if($graph !== null) {
|
||||
$statement->name = $graph;
|
||||
}
|
||||
$statements[] = $statement;
|
||||
}
|
||||
|
||||
// set new active subject to object
|
||||
$subject = $object;
|
||||
|
||||
// recurse over subject properties in order
|
||||
$props = array_keys((array)$element);
|
||||
sort($props);
|
||||
foreach($props as $prop) {
|
||||
$p = $prop;
|
||||
|
||||
// convert @type to rdf:type
|
||||
if($prop === '@type') {
|
||||
$p = self::RDF_TYPE;
|
||||
}
|
||||
|
||||
// recurse into @graph
|
||||
if($prop === '@graph') {
|
||||
$this->_toRDF(
|
||||
$element->{$prop}, $namer, null, null, $subject, $statements);
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip keywords
|
||||
if(self::_isKeyword($p)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// create new active property
|
||||
$property = (object)array(
|
||||
'nominalValue' => $p,
|
||||
'interfaceName' => 'IRI');
|
||||
|
||||
// recurse into value
|
||||
$this->_toRDF(
|
||||
$element->{$prop}, $namer, $subject, $property, $graph, $statements);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(is_string($element)) {
|
||||
// emit IRI for rdf:type, else plain literal
|
||||
$statement = (object)array(
|
||||
'subject' => self::copy($subject),
|
||||
'property' => self::copy($property),
|
||||
'object' => (object)array(
|
||||
'nominalValue' => $element,
|
||||
'interfaceName' => (($property->nominalValue === self::RDF_TYPE) ?
|
||||
'IRI' : 'LiteralNode')));
|
||||
if($graph !== null) {
|
||||
$statement->name = $graph;
|
||||
}
|
||||
$statements[] = $statement;
|
||||
return;
|
||||
}
|
||||
|
||||
if(is_bool($element) || is_double($element) || is_integer($element)) {
|
||||
// convert to XSD datatype
|
||||
if(is_bool($element)) {
|
||||
$datatype = self::XSD_BOOLEAN;
|
||||
$value = ($element ? 'true' : 'false');
|
||||
}
|
||||
else if(is_double($element)) {
|
||||
$datatype = self::XSD_DOUBLE;
|
||||
// do special JSON-LD double format, printf('%1.15e') equivalent
|
||||
$value = preg_replace('/(e(?:\+|-))([0-9])$/', '${1}0${2}',
|
||||
sprintf('%1.15e', $element));
|
||||
}
|
||||
else {
|
||||
$datatype = self::XSD_INTEGER;
|
||||
$value = strval($element);
|
||||
}
|
||||
|
||||
// emit typed literal
|
||||
$statement = (object)array(
|
||||
'subject' => self::copy($subject),
|
||||
'property' => self::copy($property),
|
||||
'object' => (object)array(
|
||||
'nominalValue' => $value,
|
||||
'interfaceName' => 'LiteralNode',
|
||||
'datatype' => (object)array(
|
||||
'nominalValue' => $datatype,
|
||||
'interfaceName' => 'IRI')));
|
||||
if($graph !== null) {
|
||||
$statement->name = $graph;
|
||||
}
|
||||
$statements[] = $statement;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1675,23 +1868,23 @@ class JsonLdProcessor {
|
|||
}
|
||||
|
||||
// Note: safe to assume input is a subject/blank node
|
||||
$isBnode = self::_isBlankNode($input);
|
||||
$is_bnode = self::_isBlankNode($input);
|
||||
|
||||
// name blank node if appropriate, use passed name if given
|
||||
if($name === null) {
|
||||
if(property_exists($input, '@id')) {
|
||||
$name = $input->{'@id'};
|
||||
}
|
||||
if($isBnode) {
|
||||
if($is_bnode) {
|
||||
$name = $namer->getName($name);
|
||||
}
|
||||
}
|
||||
|
||||
// use a subject of '_:a' for blank node statements
|
||||
$s = $isBnode ? '_:a' : $name;
|
||||
$s = $is_bnode ? '_:a' : $name;
|
||||
|
||||
// get statements for the blank node
|
||||
if($isBnode) {
|
||||
if($is_bnode) {
|
||||
if(!property_exists($bnodes, $name)) {
|
||||
$entries = $bnodes->{$name} = new ArrayObject();
|
||||
}
|
||||
|
@ -3645,15 +3838,15 @@ class JsonLdProcessor {
|
|||
$quad .= " <{$p->nominalValue}> ";
|
||||
|
||||
// object is IRI, bnode, or literal
|
||||
if(o.interfaceName === 'IRI') {
|
||||
if($o->interfaceName === 'IRI') {
|
||||
$quad .= "<{$o->nominalValue}>";
|
||||
}
|
||||
else if(o.interfaceName === 'BlankNode') {
|
||||
else if($o->interfaceName === 'BlankNode') {
|
||||
$quad .= $o->nominalValue;
|
||||
}
|
||||
else {
|
||||
$quad .= '"' . $o->nominalValue . '"';
|
||||
if(propert_exists($o, 'datatype')) {
|
||||
if(property_exists($o, 'datatype')) {
|
||||
$quad .= "^^<{$o->datatype->nominalValue}>";
|
||||
}
|
||||
else if(property_exists($o, 'language')) {
|
||||
|
|
Loading…
Reference in a new issue