diff --git a/src/JsonLdRdfWriter.php b/src/JsonLdRdfWriter.php index e94a68d..21df20b 100644 --- a/src/JsonLdRdfWriter.php +++ b/src/JsonLdRdfWriter.php @@ -1,381 +1,482 @@ graph to null in * #finishJson() to ensure that the deferred callback in #finishDocument() * doesn't later emit "@graph". * * @see https://www.w3.org/TR/json-ld/#named-graphs * * @var array[]|null */ private $graph = []; /** * A collection of predicates about a specific subject. The * subject is identified by the "@id" key in this array; the other * keys identify JSON-LD properties. * * @see https://www.w3.org/TR/json-ld/#dfn-edge * * @var array */ private $predicates = []; /** * A sequence of zero or more IRIs, nodes, or values, which are the * destination targets of the current predicates. * * @see https://www.w3.org/TR/json-ld/#dfn-list * * @var array */ private $values = []; /** * True iff we have written the opening of the "@graph" field. * * @var bool */ private $wroteGraph = false; /** * JSON-LD objects describing a single node can omit the "@graph" field; * this variable remains false only so long as we can guarantee that * only a single node has been described. * * @var bool */ private $disableGraphOpt = false; /** * The IRI for the RDF `type` property. */ const RDF_TYPE_IRI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; + /** + * The type internally used for "default type", which is a string or + * otherwise default-coerced type. + */ + const DEFAULT_TYPE = "@purtle@default@"; + /** * @param string $role * @param BNodeLabeler|null $labeler */ public function __construct( $role = parent::DOCUMENT_ROLE, BNodeLabeler $labeler = null ) { parent::__construct( $role, $labeler ); // The following named methods are protected, not private, so we // can invoke them directly w/o function wrappers. $this->transitionTable[self::STATE_START][self::STATE_DOCUMENT] = [ $this, 'beginJson' ]; $this->transitionTable[self::STATE_DOCUMENT][self::STATE_FINISH] = [ $this, 'finishJson' ]; $this->transitionTable[self::STATE_OBJECT][self::STATE_PREDICATE] = [ $this, 'finishPredicate' ]; $this->transitionTable[self::STATE_OBJECT][self::STATE_SUBJECT] = [ $this, 'finishSubject' ]; $this->transitionTable[self::STATE_OBJECT][self::STATE_DOCUMENT] = [ $this, 'finishDocument' ]; } /** * Emit $val as JSON, with $indent extra indentations on each line. * @param array $val * @param int $indent * @return string the JSON string for $val */ public function encode( $val, $indent ) { $str = json_encode( $val, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ); // Strip outermost open/close braces/brackets $str = preg_replace( '/^[[{]\n?|\n?[}\]]$/', '', $str ); if ( $indent > 0 ) { // add extra indentation $str = preg_replace( '/^/m', str_repeat( ' ', $indent ), $str ); } return $str; } /** * Return a "compact IRI" corresponding to the given base/local pair. * This adds entries to the "@context" key when needed to allow use * of a given prefix. * @see https://www.w3.org/TR/json-ld/#dfn-compact-iri * * @param string $base A QName prefix if $local is given, or an IRI if $local is null. * @param string|null $local A QName suffix, or null if $base is an IRI. * * @return string A compact IRI. */ private function compactify( $base, $local = null ) { $this->expandShorthand( $base, $local ); if ( $local === null ) { return $base; } else { if ( $base !== '_' && isset( $this->prefixes[ $base ] ) ) { if ( $base === '' ) { // Empty prefix not supported; use full IRI return $this->prefixes[ $base ] . $local; } - $this->context[ $base ] = $this->prefixes[ $base ]; + if ( !isset( $this->context[ $base ] ) ) { + $this->context[ $base ] = $this->prefixes[ $base ]; + } + if ( $this->context[ $base ] !== $this->prefixes[ $base ] ) { + // Context name conflict; use full IRI + return $this->prefixes[ $base ] . $local; + } } return $base . ':' . $local; } } /** * Return an absolute IRI from the given base/local pair. * @see https://www.w3.org/TR/json-ld/#dfn-absolute-iri * * @param string $base A QName prefix if $local is given, or an IRI if $local is null. * @param string|null $local A QName suffix, or null if $base is an IRI. * * @return string|null An absolute IRI, or null if it cannot be constructed. */ private function toIRI( $base, $local ) { $this->expandShorthand( $base, $local ); $this->expandQName( $base, $local ); if ( $local !== null ) { throw new LogicException( 'Unknown prefix: ' . $base ); } return $base; } + /** + * Return a appropriate term for the current predicate value. + */ + private function getCurrentTerm() { + list( $base, $local ) = $this->currentPredicate; + $predIRI = $this->toIRI( $base, $local ); + if ( $predIRI === self::RDF_TYPE_IRI ) { + return $predIRI; + } + $this->expandShorthand( $base, $local ); + if ( $local === null ) { + return $base; + } elseif ( $base !== '_' && !isset( $this->prefixes[ $local ] ) ) { + // Prefixes get priority over field names in @context + $pred = $this->compactify( $base, $local ); + if ( !isset( $this->context[ $local ] ) ) { + $this->context[ $local ] = [ "@id" => $pred ]; + } + if ( $this->context[ $local ][ "@id" ] === $pred ) { + return $local; + } + return $pred; + } + return $this->compactify( $base, $local ); + } + /** * Write document header. */ protected function beginJson() { if ( $this->role === self::DOCUMENT_ROLE ) { $this->write( "{\n" ); $this->write( function () { // If this buffer is drained early, disable @graph optimization $this->disableGraphOpt = true; return ''; } ); } } /** * Write document footer. */ protected function finishJson() { // If we haven't drained yet, and @graph has only 1 element, then we // can optimize our output and hoist the single node to top level. if ( $this->role === self::DOCUMENT_ROLE ) { if ( ( !$this->disableGraphOpt ) && count( $this->graph ) === 1 ) { $this->write( $this->encode( $this->graph[0], 0 ) ); $this->graph = null; // We're done with @graph. } else { $this->disableGraphOpt = true; $this->write( "\n ]" ); } } if ( count( $this->context ) ) { // Write @context field. $this->write( ",\n" ); $this->write( $this->encode( [ "@context" => $this->context ], 0 ) ); } $this->write( "\n}" ); } protected function finishDocument() { $this->finishSubject(); $this->write( function () { // if this is drained before finishJson(), then disable // the graph optimization and dump what we've got so far. $str = ''; if ( $this->graph !== null && count( $this->graph ) > 0 ) { $this->disableGraphOpt = true; if ( $this->role === self::DOCUMENT_ROLE && !$this->wroteGraph ) { $str .= " \"@graph\": [\n"; $this->wroteGraph = true; } else { $str .= ",\n"; } $str .= $this->encode( $this->graph, 1 ); $this->graph = []; return $str; } // Delay; maybe we'll be able to optimize this later. return $str; } ); } /** * @param string $base * @param string|null $local */ protected function writeSubject( $base, $local = null ) { $this->predicates = [ "@id" => $this->compactify( $base, $local ) ]; } protected function finishSubject() { $this->finishPredicate(); $this->graph[] = $this->predicates; } /** * @param string $base * @param string|null $local */ protected function writePredicate( $base, $local = null ) { // no op } /** * @param string $base * @param string|null $local */ protected function writeResource( $base, $local = null ) { - $this->values[] = [ - "@id" => $this->compactify( $base, $local ) - ]; + $pred = $this->getCurrentTerm(); + $value = $this->compactify( $base, $local ); + $this->addTypedValue( "@id", $value, [ + "@id" => $value + ], ( $pred === self::RDF_TYPE_IRI ) ); } /** * @param string $text * @param string|null $language */ protected function writeText( $text, $language = null ) { if ( !$this->isValidLanguageCode( $language ) ) { - $this->values[] = $text; + $this->addTypedValue( self::DEFAULT_TYPE, $text ); } else { - $this->values[] = [ + $expanded = [ "@language" => $language, "@value" => $text ]; + $this->addTypedValue( self::DEFAULT_TYPE, $expanded, $expanded ); } } /** * @param string $literal * @param string|null $typeBase * @param string|null $typeLocal */ public function writeValue( $literal, $typeBase, $typeLocal = null ) { if ( $typeBase === null && $typeLocal === null ) { - $this->values[] = $literal; + $this->addTypedValue( self::DEFAULT_TYPE, $literal ); return; } switch ( $this->toIRI( $typeBase, $typeLocal ) ) { case 'http://www.w3.org/2001/XMLSchema#string': - $this->values[] = strval( $literal ); + $this->addTypedValue( self::DEFAULT_TYPE, strval( $literal ) ); return; case 'http://www.w3.org/2001/XMLSchema#integer': - $this->values[] = intval( $literal ); + $this->addTypedValue( self::DEFAULT_TYPE, intval( $literal ) ); return; case 'http://www.w3.org/2001/XMLSchema#boolean': - $this->values[] = $literal === 'true'; + $this->addTypedValue( self::DEFAULT_TYPE, ( $literal === 'true' ) ); return; case 'http://www.w3.org/2001/XMLSchema#double': $v = doubleval( $literal ); // Only "numbers with fractions" are xsd:double. We need // to verify that the JSON string will contain a decimal // point, otherwise the value would be interpreted as an // xsd:integer. // TODO: consider instead using JSON_PRESERVE_ZERO_FRACTION // in $this->encode() once our required PHP >= 5.6.6. + // OTOH, the spec language is ambiguous about whether "5." + // would be considered an integer or a double. if ( strpos( json_encode( $v ), '.' ) !== false ) { - $this->values[] = $v; + $this->addTypedValue( self::DEFAULT_TYPE, $v ); return; } } - $this->values[] = [ - "@type" => $this->compactify( $typeBase, $typeLocal ), - "@value" => strval( $literal ) - ]; + $type = $this->compactify( $typeBase, $typeLocal ); + $literal = strval( $literal ); + $this->addTypedValue( $type, $literal, [ + "@type" => $type, + "@value" => $literal + ] ); + } + + /** + * Add a typed value for the given predicate. If possible, adds a + * default type to the context to avoid having to repeat type information + * in each value for this predicate. If there is already a default + * type which conflicts with this one, or if $forceExpand is true, + * then use the "expanded" value which will explicitly override any + * default type. + * + * @param string $type The compactified JSON-LD @type for this value, or + * self::DEFAULT_TYPE to indicate the default JSON-LD type coercion rules + * should be used. + * @param string|int|float|bool $simpleVal The "simple" representation + * for this value, used if the type can be hoisted into the context. + * @param array|null $expandedVal The "expanded" representation for this + * value, used if the context @type conflicts with this value; or null + * to use "@value" for the expanded representation. + * @param bool $forceExpand If true, don't try to add this type to the + * context. Defaults to false. + */ + protected function addTypedValue( $type, $simpleVal, $expandedVal=null, $forceExpand=false ) { + if ( !$forceExpand ) { + $pred = $this->getCurrentTerm(); + if ( $type === self::DEFAULT_TYPE ) { + if ( !isset( $this->context[ $pred ][ "@type" ] ) ) { + $this->defaulted[ $pred ] = true; + } + if ( isset( $this->defaulted[ $pred ] ) ) { + $this->values[] = $simpleVal; + return; + } + } elseif ( !isset( $this->defaulted[ $pred ] ) ) { + if ( !isset( $this->context[ $pred ] ) ) { + $this->context[ $pred ] = []; + } + if ( !isset( $this->context[ $pred ][ "@type" ] ) ) { + $this->context[ $pred ][ "@type" ] = $type; + } + if ( $this->context[ $pred ][ "@type" ] === $type ) { + $this->values[] = $simpleVal; + return; + } + } + } + if ( $expandedVal === null ) { + $this->values[] = [ "@value" => $simpleVal ]; + } else { + $this->values[] = $expandedVal; + } } protected function finishPredicate() { - list( $base, $local ) = $this->currentPredicate; - $predIRI = $this->toIRI( $base, $local ); + $name = $this->getCurrentTerm(); - if ( $predIRI === self::RDF_TYPE_IRI ) { - // TODO: the context can optionally specify other predicates - // have type "@id" or "@vocab", which would trigger this - // same coercion. See https://www.w3.org/TR/json-ld/#iris + if ( $name === self::RDF_TYPE_IRI ) { $name = "@type"; $this->values = array_map( function ( array $val ) { return $val[ "@id" ]; }, $this->values ); - } else { - $name = $this->compactify( $base, $local ); } if ( isset( $this->predicates[$name] ) ) { $was = $this->predicates[$name]; // Wrap $was into a numeric indexed array if it isn't already. // Note that $was could have non-numeric indices, eg // [ "@id" => "foo" ], in which was it still needs to be wrapped. if ( !( is_array( $was ) && isset( $was[0] ) ) ) { $was = [ $was ]; } $this->values = array_merge( $was, $this->values ); } $cnt = count( $this->values ); if ( $cnt === 0 ) { throw new LogicException( "finishPredicate can't be called without at least one value" ); } elseif ( $cnt === 1 ) { $this->predicates[$name] = $this->values[0]; } else { $this->predicates[$name] = $this->values; } $this->values = []; } /** * @param string $role * @param BNodeLabeler $labeler * * @return RdfWriterBase */ protected function newSubWriter( $role, BNodeLabeler $labeler ) { $writer = new self( $role, $labeler ); // Have subwriter share context with this parent. $writer->context = &$this->context; + $writer->defaulted = &$this->defaulted; + // We can't use the @graph optimization. $this->disableGraphOpt = true; return $writer; } /** * @return string a MIME type */ public function getMimeType() { return 'application/ld+json; charset=UTF-8'; } } diff --git a/tests/data/AlternatingValues.jsonld b/tests/data/AlternatingValues.jsonld index ce7ed09..387dc5b 100644 --- a/tests/data/AlternatingValues.jsonld +++ b/tests/data/AlternatingValues.jsonld @@ -1,33 +1,26 @@ { "@id": "wikibase:Dump", - "owl:foo": [ - { - "@id": "owl:A" - }, - { - "@id": "owl:B" - }, - { - "@id": "owl:C" - } + "foo": [ + "owl:A", + "owl:B", + "owl:C" ], - "owl:bar": [ - { - "@type": "xsd:decimal", - "@value": "5" - }, - { - "@type": "xsd:decimal", - "@value": "6" - }, - { - "@type": "xsd:decimal", - "@value": "7" - } + "bar": [ + "5", + "6", + "7" ], "@context": { "wikibase": "http://wikiba.se/ontology-beta#", "owl": "http://www.w3.org/2002/07/owl#", - "xsd": "http://www.w3.org/2001/XMLSchema#" + "foo": { + "@id": "owl:foo", + "@type": "@id" + }, + "xsd": "http://www.w3.org/2001/XMLSchema#", + "bar": { + "@id": "owl:bar", + "@type": "xsd:decimal" + } } } \ No newline at end of file diff --git a/tests/data/DumpHeader.jsonld b/tests/data/DumpHeader.jsonld index 0002dfe..ae4f30b 100644 --- a/tests/data/DumpHeader.jsonld +++ b/tests/data/DumpHeader.jsonld @@ -1,25 +1,33 @@ { "@id": "wikibase:Dump", "@type": [ "schema:Dataset", "owl:Ontology" ], - "cc:license": { - "@id": "http://creativecommons.org/publicdomain/zero/1.0/" - }, - "schema:softwareVersion": "0.1.0", - "schema:dateModified": { - "@type": "xsd:dateTime", - "@value": "2017-09-19T22:53:13-04:00" - }, - "owl:imports": { - "@id": "http://wikiba.se/ontology-1.0.owl" - }, + "license": "http://creativecommons.org/publicdomain/zero/1.0/", + "softwareVersion": "0.1.0", + "dateModified": "2017-09-19T22:53:13-04:00", + "imports": "http://wikiba.se/ontology-1.0.owl", "@context": { "wikibase": "http://wikiba.se/ontology-beta#", "schema": "http://schema.org/", "owl": "http://www.w3.org/2002/07/owl#", "cc": "http://creativecommons.org/ns#", - "xsd": "http://www.w3.org/2001/XMLSchema#" + "license": { + "@id": "cc:license", + "@type": "@id" + }, + "softwareVersion": { + "@id": "schema:softwareVersion" + }, + "xsd": "http://www.w3.org/2001/XMLSchema#", + "dateModified": { + "@id": "schema:dateModified", + "@type": "xsd:dateTime" + }, + "imports": { + "@id": "owl:imports", + "@type": "@id" + } } } \ No newline at end of file diff --git a/tests/data/EricMiller.jsonld b/tests/data/EricMiller.jsonld index aa97d02..782a579 100644 --- a/tests/data/EricMiller.jsonld +++ b/tests/data/EricMiller.jsonld @@ -1,12 +1,20 @@ { "@id": "http://www.w3.org/People/EM/contact#me", "@type": "contact:Person", - "contact:fullName": "Eric Miller", - "contact:mailbox": { - "@id": "mailto:em@w3.org" - }, - "contact:personalTitle": "Dr.", + "fullName": "Eric Miller", + "mailbox": "mailto:em@w3.org", + "personalTitle": "Dr.", "@context": { - "contact": "http://www.w3.org/2000/10/swap/pim/contact#" + "contact": "http://www.w3.org/2000/10/swap/pim/contact#", + "fullName": { + "@id": "contact:fullName" + }, + "mailbox": { + "@id": "contact:mailbox", + "@type": "@id" + }, + "personalTitle": { + "@id": "contact:personalTitle" + } } } \ No newline at end of file diff --git a/tests/data/LabeledBlankNode.jsonld b/tests/data/LabeledBlankNode.jsonld index ff88487..fa682e9 100644 --- a/tests/data/LabeledBlankNode.jsonld +++ b/tests/data/LabeledBlankNode.jsonld @@ -1,21 +1,35 @@ { "@graph": [ { "@id": "exstaff:85740", - "exterms:address": { - "@id": "_:johnaddress" - } + "address": "_:johnaddress" }, { "@id": "_:johnaddress", - "exterms:street": "1501 Grant Avenue", - "exterms:city": "Bedfort", - "exterms:state": "Massachusetts", - "exterms:postalCode": "01730" + "street": "1501 Grant Avenue", + "city": "Bedfort", + "state": "Massachusetts", + "postalCode": "01730" } ], "@context": { "exstaff": "http://www.example.org/staffid/", - "exterms": "http://www.example.org/terms/" + "exterms": "http://www.example.org/terms/", + "address": { + "@id": "exterms:address", + "@type": "@id" + }, + "street": { + "@id": "exterms:street" + }, + "city": { + "@id": "exterms:city" + }, + "state": { + "@id": "exterms:state" + }, + "postalCode": { + "@id": "exterms:postalCode" + } } } \ No newline at end of file diff --git a/tests/data/NumberedBlankNode.jsonld b/tests/data/NumberedBlankNode.jsonld index e8e930e..1693223 100644 --- a/tests/data/NumberedBlankNode.jsonld +++ b/tests/data/NumberedBlankNode.jsonld @@ -1,28 +1,31 @@ { "@graph": [ { "@id": "exstaff:Sue", - "exterms:publication": { - "@id": "_:genid1" - } + "publication": "_:genid1" }, { "@id": "_:genid1", - "exterms:title": "Antology of Time" + "title": "Antology of Time" }, { "@id": "exstaff:Jack", - "exterms:publication": { - "@id": "_:genid2" - } + "publication": "_:genid2" }, { "@id": "_:genid2", - "exterms:title": "Anthony of Time" + "title": "Anthony of Time" } ], "@context": { "exstaff": "http://www.example.org/staffid/", - "exterms": "http://www.example.org/terms/" + "exterms": "http://www.example.org/terms/", + "publication": { + "@id": "exterms:publication", + "@type": "@id" + }, + "title": { + "@id": "exterms:title" + } } } \ No newline at end of file diff --git a/tests/data/Numbers.jsonld b/tests/data/Numbers.jsonld index a6c7b69..af36e29 100644 --- a/tests/data/Numbers.jsonld +++ b/tests/data/Numbers.jsonld @@ -1,18 +1,21 @@ { "@graph": [ { "@id": "acme:Bongos", - "acme:stock": [ + "stock": [ 5, 7 ] }, { "@id": "acme:Tablas", - "acme:stock": 6 + "stock": 6 } ], "@context": { - "acme": "http://acme.test/" + "acme": "http://acme.test/", + "stock": { + "@id": "acme:stock" + } } } \ No newline at end of file diff --git a/tests/data/Predicates.jsonld b/tests/data/Predicates.jsonld index bec4bb5..cb1f4d6 100644 --- a/tests/data/Predicates.jsonld +++ b/tests/data/Predicates.jsonld @@ -1,19 +1,24 @@ { "@graph": [ { "@id": "http://foobar.test/Bananas", "@type": "http://foobar.test/Fruit", - "http://acme.test/name": [ + "name": [ "Banana", { "@language": "de", "@value": "Banane" } ] }, { "@id": "http://foobar.test/Apples", - "http://acme.test/name": "Apple" + "name": "Apple" } - ] + ], + "@context": { + "name": { + "@id": "http://acme.test/name" + } + } } \ No newline at end of file diff --git a/tests/data/Resources.jsonld b/tests/data/Resources.jsonld index 0b3d606..86859fa 100644 --- a/tests/data/Resources.jsonld +++ b/tests/data/Resources.jsonld @@ -1,14 +1,14 @@ { "@id": "acme:Bongos", - "acme:sounds": [ - { - "@id": "acme:Bing" - }, - { - "@id": "http://foobar.test/sound/Bang" - } + "sounds": [ + "acme:Bing", + "http://foobar.test/sound/Bang" ], "@context": { - "acme": "http://acme.test/" + "acme": "http://acme.test/", + "sounds": { + "@id": "acme:sounds", + "@type": "@id" + } } } \ No newline at end of file diff --git a/tests/data/TextWithSpecialChars.jsonld b/tests/data/TextWithSpecialChars.jsonld index f71e6cd..59e6eee 100644 --- a/tests/data/TextWithSpecialChars.jsonld +++ b/tests/data/TextWithSpecialChars.jsonld @@ -1,19 +1,22 @@ { "@graph": [ { "@id": "exterms:Duck", - "exterms:says": "Duck says: \"Quack!\"" + "says": "Duck says: \"Quack!\"" }, { "@id": "exterms:Cow", - "exterms:says": "Cow says:\n\r 'Moo! \\Moo!'" + "says": "Cow says:\n\r 'Moo! \\Moo!'" }, { "@id": "exterms:Bear", - "exterms:says": "Bear says: \u041f\u0440\u0435\u0432\u0435\u0434!" + "says": "Bear says: \u041f\u0440\u0435\u0432\u0435\u0434!" } ], "@context": { - "exterms": "http://www.example.org/terms/" + "exterms": "http://www.example.org/terms/", + "says": { + "@id": "exterms:says" + } } } \ No newline at end of file diff --git a/tests/data/Texts.jsonld b/tests/data/Texts.jsonld index c8bafd6..d52406b 100644 --- a/tests/data/Texts.jsonld +++ b/tests/data/Texts.jsonld @@ -1,21 +1,24 @@ { "@id": "acme:Bongos", - "acme:sounds": [ + "sounds": [ { "@language": "de", "@value": "Bom" }, { "@language": "en", "@value": "Bam" }, { "@language": "es-419", "@value": "Como estas" }, "What?" ], "@context": { - "acme": "http://acme.test/" + "acme": "http://acme.test/", + "sounds": { + "@id": "acme:sounds" + } } } \ No newline at end of file diff --git a/tests/data/Triples.jsonld b/tests/data/Triples.jsonld index 767bb85..ca64266 100644 --- a/tests/data/Triples.jsonld +++ b/tests/data/Triples.jsonld @@ -1,20 +1,24 @@ { "@graph": [ { "@id": "http://foobar.test/Bananas", "@type": "http://foobar.test/Fruit" }, { "@id": "acme:Nuts", - "acme:weight": { - "@type": "xsd:decimal", - "@value": "5.5" - }, - "acme:color": "brown" + "weight": "5.5", + "color": "brown" } ], "@context": { "acme": "http://acme.test/", - "xsd": "http://www.w3.org/2001/XMLSchema#" + "xsd": "http://www.w3.org/2001/XMLSchema#", + "weight": { + "@id": "acme:weight", + "@type": "xsd:decimal" + }, + "color": { + "@id": "acme:color" + } } } \ No newline at end of file diff --git a/tests/data/TypeConflict.jsonld b/tests/data/TypeConflict.jsonld index bb0cc7d..40ae142 100644 --- a/tests/data/TypeConflict.jsonld +++ b/tests/data/TypeConflict.jsonld @@ -1,35 +1,45 @@ { "@graph": [ { "@id": "ex:A", - "ex:foo": [ - { - "@id": "ex:Node" - }, + "foo": [ + "ex:Node", { "@type": "xsd:decimal", "@value": "5" }, - "string" + { + "@value": "string" + } ], - "ex:bar": [ + "bar": [ "string", { "@type": "xsd:decimal", "@value": "5" } ], - "ex:bat": "string" + "bat": "string" }, { "@id": "ex:B", - "ex:bat": { + "bat": { "@id": "_:genid1" } } ], "@context": { "ex": "http://example.com/", - "xsd": "http://www.w3.org/2001/XMLSchema#" + "foo": { + "@id": "ex:foo", + "@type": "@id" + }, + "xsd": "http://www.w3.org/2001/XMLSchema#", + "bar": { + "@id": "ex:bar" + }, + "bat": { + "@id": "ex:bat" + } } } \ No newline at end of file diff --git a/tests/data/Values.jsonld b/tests/data/Values.jsonld index a523ebb..c481ae5 100644 --- a/tests/data/Values.jsonld +++ b/tests/data/Values.jsonld @@ -1,49 +1,73 @@ { "@id": "http://foobar.test/Bananas", - "acme:multi": [ + "multi": [ "A", "B", "C" ], - "acme:type": [ + "type": [ + "foo", { - "@type": "acme:thing", - "@value": "foo" + "@value": -5 }, - -5, { "@type": "xsd:decimal", "@value": "-5" }, { "@type": "xsd:double", "@value": "-5" }, - true, - false + { + "@value": true + }, + { + "@value": false + } ], - "acme:autotype": [ + "autotype": [ -5, 3.14, true, false ], - "acme:no-autotype": [ + "no-autotype": [ + "-5", { - "@type": "xsd:decimal", - "@value": "-5" + "@value": "3.14" }, - "3.14", - "1", - "" + { + "@value": "1" + }, + { + "@value": "" + } ], - "acme:shorthand": "foo", - "acme:typed-shorthand": { - "@type": "acme:thing", - "@value": "foo" - }, + "shorthand": "foo", + "typed-shorthand": "foo", "@context": { "acme": "http://acme.test/", - "xsd": "http://www.w3.org/2001/XMLSchema#" + "multi": { + "@id": "acme:multi" + }, + "type": { + "@id": "acme:type", + "@type": "acme:thing" + }, + "xsd": "http://www.w3.org/2001/XMLSchema#", + "autotype": { + "@id": "acme:autotype" + }, + "no-autotype": { + "@id": "acme:no-autotype", + "@type": "xsd:decimal" + }, + "shorthand": { + "@id": "acme:shorthand" + }, + "typed-shorthand": { + "@id": "acme:typed-shorthand", + "@type": "acme:thing" + } } } \ No newline at end of file