Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions lib/pyld/jsonld.py
Original file line number Diff line number Diff line change
Expand Up @@ -2615,11 +2615,6 @@ def _expand_object(

continue

# nested keys
if expanded_property == '@nest':
nests.append(key)
continue

# use potential scoped context for key
term_ctx = active_ctx
ctx = JsonLdProcessor.get_context_value(active_ctx, key, '@context')
Expand All @@ -2628,6 +2623,11 @@ def _expand_object(
active_ctx, ctx, options, propagate=True, override_protected=True
)

# for nested keys, add scoped context with key
if expanded_property == '@nest':
nests.append((key, term_ctx))
continue

container = JsonLdProcessor.arrayify(
JsonLdProcessor.get_context_value(active_ctx, key, '@container')
)
Expand Down Expand Up @@ -2770,13 +2770,13 @@ def _expand_object(
code='invalid value object value',
)

# expand each nested key
for key in nests:
# expand each nested key and scoped context
for key, term_ctx in nests:
for nv in JsonLdProcessor.arrayify(element[key]):
if not _is_object(nv) or [
k
for k, v in nv.items()
if self._expand_iri(active_ctx, k, vocab=True) == '@value'
if self._expand_iri(term_ctx, k, vocab=True) == '@value'
]:
raise JsonLdError(
'Invalid JSON-LD syntax; nested value must be a node object.',
Expand All @@ -2785,7 +2785,7 @@ def _expand_object(
code='invalid @nest value',
)
self._expand_object(
active_ctx,
term_ctx,
active_property,
expanded_active_property,
nv,
Expand Down
2 changes: 0 additions & 2 deletions tests/runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1019,12 +1019,10 @@ def write(self, filename):
# well formed
'.*toRdf-manifest#twf05$',
# uncategorized
'.*toRdf-manifest#tc038$',
'.*toRdf-manifest#ter54$',
'.*toRdf-manifest#ter56$',
'.*toRdf-manifest#tli12$',
'.*toRdf-manifest#tli14$',
'.*toRdf-manifest#tc037$',
]
},
'skip': {
Expand Down
141 changes: 141 additions & 0 deletions tests/test_jsonld.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,94 @@ def test_structured_value_still_works_with_scoped_context(self):
# meaning -> @id
assert "@id" in prop_val

# Issue 204
def test_scoped_context_on_nest_term_expands_nested_properties(self):
"""A scoped context on a @nest term should apply to nested properties."""
input = {
"@context": {
"@vocab": "http://example.org/vocab#",
"p1": {
"@id": "@nest",
"@context": {"p2": "http://example.org/ns#P2"},
},
},
"p1": {"p2": "foo"},
}

expected = [
{
"http://example.org/ns#P2": [
{
"@value": "foo",
}
],
}
]

result = jsonld.expand(input)

assert result == expected

# Issue 204
def test_scoped_context_on_nest_term_expands_nested_type_scoped_context(self):
"""
A scoped context on a @nest term should be in effect when expanding the
nested node, including when processing any type-scoped contexts found on
that node.

JSON-LD 1.1 defines property nesting as semantically transparent: nested
properties are removed during expansion and treated as if their contents
were declared directly on the containing node object. It also defines
scoped contexts as property-scoped when the term is used as a property
and type-scoped when the term is used as a type. This test combines both
rules deliberately:

* p1 is an @nest term with a property-scoped context.
* That context defines Type and gives Type its own type-scoped context.
* The nested node uses Type and then uses p2 from Type's scoped context.

If nested values are expanded by directly walking their keys instead of
running the normal expansion setup for the nested node, Type and p2 fall
back to the outer @vocab. The expected result proves that the @nest term
context is active before @type is expanded and before Type's scoped
context is applied.
"""
input = {
"@context": {
"@vocab": "http://example.org/outer#",
"p1": {
"@id": "@nest",
"@context": {
"Type": {
"@id": "http://example.org/ns#Type",
"@context": {
"p2": "http://example.org/ns#P2",
},
},
},
},
},
"p1": {
"@type": "Type",
"p2": "foo",
},
}

expected = [
{
"@type": ["http://example.org/ns#Type"],
"http://example.org/ns#P2": [
{
"@value": "foo",
}
],
}
]

result = jsonld.expand(input)

assert result == expected


def test_mixed_plain_and_vocab_terms(self):
"""Contexts with both plain and @type:@vocab terms should work correctly."""
Expand Down Expand Up @@ -692,6 +780,59 @@ def test_double_and_float_values(self):
result = jsonld.to_rdf(input)
assert result == expected

# Issue 204
def test_conflicting_property_names(self):
"""
Conversion to RDF should allow a node in the root @context with
a conflicting property name in its own @context
"""
input = {
"@context": {
"dublinCore": {
"@id": "http://foo.bar/dc",
"@context": {"title": "http://purl.org/dc/terms/title"},
},
"title": "http://foo.bar/title",
},
"@id": "http://foo.bar/obj/test",
"title": "test",
"dublinCore": {"title": "Chapter 1: Jonathan Harker's Journal"},
}

expected = """<http://foo.bar/obj/test> <http://foo.bar/dc> _:b0 .
<http://foo.bar/obj/test> <http://foo.bar/title> "test" .
_:b0 <http://purl.org/dc/terms/title> "Chapter 1: Jonathan Harker's Journal" .
"""

nquads = jsonld.to_rdf(input, options={'format': 'application/n-quads'})
assert nquads == expected


def test_conflicting_property_names_in_nested_node(self):
"""
Conversion to RDF should not ignore a @nest'ed node in the root @context
a conflicting property name in its own @context
"""
input = {
"@context": {
"dublinCore": {
"@id": "@nest",
"@context": {"title": "http://purl.org/dc/terms/title"},
},
"title": "http://foo.bar/title",
},
"@id": "http://foo.bar/obj/test",
"title": "test",
"dublinCore": {"title": "Chapter 1: Jonathan Harker's Journal"},
}

expected = """<http://foo.bar/obj/test> <http://foo.bar/title> "test" .
<http://foo.bar/obj/test> <http://purl.org/dc/terms/title> "Chapter 1: Jonathan Harker's Journal" .
"""

nquads = jsonld.to_rdf(input, options={'format': 'application/n-quads'})
assert nquads == expected


class TestCompact:
# Issue 59 - PR: https://github.com/digitalbazaar/pyld/pull/60
Expand Down
Loading