Skip to content

Commit 172e0c9

Browse files
committed
Updates for @included as an array of node objects.
1 parent 18d8a91 commit 172e0c9

File tree

8 files changed

+916
-49
lines changed

8 files changed

+916
-49
lines changed

lib/json/ld.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ module LD
5353
@explicit
5454
@json
5555
@id
56+
@included
5657
@index
5758
@first
5859
@graph
@@ -120,6 +121,7 @@ class InvalidIdValue < JsonLdError; @code = "invalid @id value"; end
120121
class InvalidIndexValue < JsonLdError; @code = "invalid @index value"; end
121122
class InvalidVersionValue < JsonLdError; @code = "invalid @version value"; end
122123
class InvalidImportValue < JsonLdError; @code = "invalid @import value"; end
124+
class InvalidIncludedValue < JsonLdError; @code = "invalid @included value"; end
123125
class InvalidIRIMapping < JsonLdError; @code = "invalid IRI mapping"; end
124126
class InvalidKeywordAlias < JsonLdError; @code = "invalid keyword alias"; end
125127
class InvalidLanguageMapping < JsonLdError; @code = "invalid language mapping"; end
@@ -150,6 +152,7 @@ class KeywordRedefinition < JsonLdError; @code = "keyword redefinition"; end
150152
class LoadingDocumentFailed < JsonLdError; @code = "loading document failed"; end
151153
class LoadingRemoteContextFailed < JsonLdError; @code = "loading remote context failed"; end
152154
class ContextOverflow < JsonLdError; @code = "maximum number of @context URLs exceeded"; end
155+
class MissingIncludedReferent < JsonLdError; @code = "missing @included referent"; end
153156
class MultipleContextLinkHeaders < JsonLdError; @code = "multiple context link headers"; end
154157
class ProtectedTermRedefinition < JsonLdError; @code = "protected term redefinition"; end
155158
class ProcessingModeConflict < JsonLdError; @code = "processing mode conflict"; end

lib/json/ld/expand.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Expand
1818
CONTAINER_GRAPH_ID = %w(@graph @id).freeze
1919
KEYS_VALUE_LANGUAGE_TYPE_INDEX = %w(@value @language @type @index).freeze
2020
KEYS_SET_LIST_INDEX = %w(@set @list @index).freeze
21+
KEYS_INCLUDED_TYPE = %w(@included @type).freeze
2122

2223
##
2324
# Expand an Array or Object given an active context and performing local context expansion.
@@ -124,7 +125,7 @@ def expand(input, active_property, context, ordered: false, framing: false, from
124125
ary = Array(output_object['@value'])
125126
return nil if ary.empty?
126127

127-
if context.processingMode == 'json-ld-1.1' && output_object['@type'] == '@json'
128+
if (context.processingMode || 'json-ld-1.0') > 'json-ld-1.0' && output_object['@type'] == '@json'
128129
# Any value of @value is okay if @type: @json
129130
elsif !ary.all? {|v| v.is_a?(String) || v.is_a?(Hash) && v.empty?} && output_object.has_key?('@language')
130131
# Otherwise, if the value of result's @value member is not a string and result contains the key @language, an invalid language-tagged value error has been detected (only strings can be language-tagged) and processing is aborted.
@@ -221,7 +222,7 @@ def expand_object(input, active_property, context, output_object,
221222

222223
# If result has already an expanded property member (other than @type), an colliding keywords error has been detected and processing is aborted.
223224
raise JsonLdError::CollidingKeywords,
224-
"#{expanded_property} already exists in result" if output_object.has_key?(expanded_property) && expanded_property != '@type'
225+
"#{expanded_property} already exists in result" if output_object.has_key?(expanded_property) && !KEYS_INCLUDED_TYPE.include?(expanded_property)
225226

226227
expanded_value = case expanded_property
227228
when '@id'
@@ -257,6 +258,15 @@ def expand_object(input, active_property, context, output_object,
257258
else
258259
e_id
259260
end
261+
when '@included'
262+
# Included blocks are treated as an array of separate object nodes sharing the same referencing active_property. For 1.0, it is skipped as are other unknown keywords
263+
next if context.processingMode == 'json-ld-1.0'
264+
included_result = as_array(expand(value, active_property, context, ordered: ordered, framing: framing))
265+
266+
# Expanded values must be node objects
267+
raise JsonLdError::InvalidIncludedValue, "values of @included must expand to node objects" unless included_result.all? {|e| node?(e)}
268+
# As other properties may alias to @included, add this to any other previously expanded values
269+
Array(output_object['@included']) + included_result
260270
when '@type'
261271
# If expanded property is @type and value is neither a string nor an array of strings, an invalid type value error has been detected and processing is aborted. Otherwise, set expanded value to the result of using the IRI Expansion algorithm, passing active context, true for vocab, and true for document relative to expand the value or each of its items.
262272
#log_debug("@type") {"value: #{value.inspect}"}
@@ -292,7 +302,7 @@ def expand_object(input, active_property, context, output_object,
292302
# If expanded property is @value and value is not a scalar or null, an invalid value object value error has been detected and processing is aborted. (In 1.1, @value can have any JSON value of @type is @json or the property coerces to @json).
293303
# Otherwise, set expanded value to value. If expanded value is null, set the @value member of result to null and continue with the next key from element. Null values need to be preserved in this case as the meaning of an @type member depends on the existence of an @value member.
294304
# If framing, always use array form, unless null
295-
if context.processingMode == 'json-ld-1.1' && input_type == '@json'
305+
if (context.processingMode || 'json-ld-1.0') > 'json-ld-1.0' && input_type == '@json'
296306
value
297307
else
298308
case value
@@ -338,7 +348,7 @@ def expand_object(input, active_property, context, output_object,
338348
"Value of @index is not a string: #{value.inspect}" unless value.is_a?(String)
339349
value
340350
when '@list'
341-
# If expanded property is @list:
351+
# If expanded property is @graph:
342352

343353
# If active property is null or @graph, continue with the next key from element to remove the free-floating list.
344354
next if (expanded_active_property || '@graph') == '@graph'

lib/json/ld/flatten.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ def create_node_map(element, graph_map,
112112
active_graph: id)
113113
end
114114

115+
if element['@included']
116+
create_node_map(element.delete('@included'), graph_map,
117+
active_graph: active_graph)
118+
end
119+
115120
element.keys.each do |property|
116121
value = element[property]
117122

lib/json/ld/to_rdf.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ def item_to_rdf(item, graph_name: nil, &block)
8484
yield RDF::Statement(object, predicate, subject, graph_name: graph_name)
8585
end
8686
end
87+
when '@included'
88+
values.each do |v|
89+
item_to_rdf(v, graph_name: graph_name, &block)
90+
end
8791
when /^@/
8892
# Otherwise, if @type is any other keyword, skip to the next property-values pair
8993
else

spec/compact_spec.rb

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1832,6 +1832,116 @@
18321832
end
18331833
end
18341834

1835+
context "@included" do
1836+
{
1837+
"Basic Included array": {
1838+
output: %({
1839+
"@context": {
1840+
"@version": 1.1,
1841+
"@vocab": "http://example.org/",
1842+
"included": {"@id": "@included", "@container": "@set"}
1843+
},
1844+
"prop": "value",
1845+
"included": [{
1846+
"prop": "value2"
1847+
}]
1848+
}),
1849+
input: %([{
1850+
"http://example.org/prop": [{"@value": "value"}],
1851+
"@included": [{
1852+
"http://example.org/prop": [{"@value": "value2"}]
1853+
}]
1854+
}])
1855+
},
1856+
"Basic Included object": {
1857+
output: %({
1858+
"@context": {
1859+
"@version": 1.1,
1860+
"@vocab": "http://example.org/"
1861+
},
1862+
"prop": "value",
1863+
"@included": {
1864+
"prop": "value2"
1865+
}
1866+
}),
1867+
input: %([{
1868+
"http://example.org/prop": [{"@value": "value"}],
1869+
"@included": [{
1870+
"http://example.org/prop": [{"@value": "value2"}]
1871+
}]
1872+
}])
1873+
},
1874+
"Multiple properties mapping to @included are folded together": {
1875+
output: %({
1876+
"@context": {
1877+
"@version": 1.1,
1878+
"@vocab": "http://example.org/",
1879+
"included1": "@included",
1880+
"included2": "@included"
1881+
},
1882+
"included1": [
1883+
{"prop": "value1"},
1884+
{"prop": "value2"}
1885+
]
1886+
}),
1887+
input: %([{
1888+
"@included": [
1889+
{"http://example.org/prop": [{"@value": "value1"}]},
1890+
{"http://example.org/prop": [{"@value": "value2"}]}
1891+
]
1892+
}])
1893+
},
1894+
"Included containing @included": {
1895+
output: %({
1896+
"@context": {
1897+
"@version": 1.1,
1898+
"@vocab": "http://example.org/"
1899+
},
1900+
"prop": "value",
1901+
"@included": {
1902+
"prop": "value2",
1903+
"@included": {
1904+
"prop": "value3"
1905+
}
1906+
}
1907+
}),
1908+
input: %([{
1909+
"http://example.org/prop": [{"@value": "value"}],
1910+
"@included": [{
1911+
"http://example.org/prop": [{"@value": "value2"}],
1912+
"@included": [{
1913+
"http://example.org/prop": [{"@value": "value3"}]
1914+
}]
1915+
}]
1916+
}])
1917+
},
1918+
"Property value with @included": {
1919+
output: %({
1920+
"@context": {
1921+
"@version": 1.1,
1922+
"@vocab": "http://example.org/"
1923+
},
1924+
"prop": {
1925+
"@type": "Foo",
1926+
"@included": {
1927+
"@type": "Bar"
1928+
}
1929+
}
1930+
}),
1931+
input: %([{
1932+
"http://example.org/prop": [{
1933+
"@type": ["http://example.org/Foo"],
1934+
"@included": [{
1935+
"@type": ["http://example.org/Bar"]
1936+
}]
1937+
}]
1938+
}])
1939+
},
1940+
}.each do |title, params|
1941+
it(title) {run_compact(params)}
1942+
end
1943+
end
1944+
18351945
context "@nest" do
18361946
{
18371947
"Indexes to @nest for property with @nest" => {

0 commit comments

Comments
 (0)