Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix nested list expansion in JSON-LD #2517

Merged
merged 14 commits into from
Aug 15, 2023
39 changes: 27 additions & 12 deletions rdflib/plugins/parsers/jsonld.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ def _key_to_graph(
if term.type == JSON:
obj_nodes = [self._to_typed_json_value(obj)]
elif LIST in term.container:
obj_nodes = [{LIST: obj_nodes}]
obj_nodes = [self._expand_nested_list(obj_nodes)]
elif isinstance(obj, dict):
obj_nodes = self._parse_container(context, term, obj)
else:
Expand Down Expand Up @@ -333,17 +333,21 @@ def _key_to_graph(

context = context.get_context_for_term(term)

flattened = []
for obj in obj_nodes:
if isinstance(obj, dict):
objs = context.get_set(obj)
if objs is not None:
obj = objs
if isinstance(obj, list):
flattened += obj
continue
flattened.append(obj)
obj_nodes = flattened
# Flatten deep nested lists
def flatten(n):
aucampia marked this conversation as resolved.
Show resolved Hide resolved
flattened = []
for obj in n:
if isinstance(obj, dict):
objs = context.get_set(obj)
if objs is not None:
obj = objs
if isinstance(obj, list):
flattened += flatten(obj)
continue
flattened.append(obj)
return flattened

obj_nodes = flatten(obj_nodes)

if not pred_uri:
return
Expand Down Expand Up @@ -593,3 +597,14 @@ def _to_typed_json_value(value: Any) -> Dict[str, str]:
value, separators=(",", ":"), sort_keys=True, ensure_ascii=False
),
}

@staticmethod
def _expand_nested_list(obj_nodes):
aucampia marked this conversation as resolved.
Show resolved Hide resolved
if not isinstance(obj_nodes, list):
result = obj_nodes
aucampia marked this conversation as resolved.
Show resolved Hide resolved
else:
result = [
Parser._expand_nested_list(o) if isinstance(o, list) else o
aucampia marked this conversation as resolved.
Show resolved Hide resolved
for o in obj_nodes
]
return {LIST: result}
76 changes: 76 additions & 0 deletions test/jsonld/test_nested_arrays.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# -*- coding: UTF-8 -*-
aucampia marked this conversation as resolved.
Show resolved Hide resolved

from rdflib import Graph, Literal, URIRef
from rdflib.collection import Collection

prop = URIRef("http://example.com/props/a")
res = URIRef("http://example.com/res")

data_no_container = """
{
"@context": {
"a": {
"@id": "_PROP_ID_"
}
},
"a": [
[[1, 2, 3], ["4", 5]],
6,
[7, { "@id": "_RES_ID_" }]
]
}
""".replace(
"_PROP_ID_", str(prop)
).replace(
"_RES_ID_", str(res)
)

data_list = """
{
"@context": {
"a": {
"@id": "_PROP_ID_",
"@container": "@list"
}
},
"a": [
[[1, 2, 3], ["4", 5]],
6,
[7, { "@id": "_RES_ID_" }]
]
}
""".replace(
"_PROP_ID_", str(prop)
).replace(
"_RES_ID_", str(res)
)
aucampia marked this conversation as resolved.
Show resolved Hide resolved


def test_container_list():
g = Graph()
g.parse(data=data_list, format="application/ld+json")

outer = Collection(g, next(g.objects(predicate=prop)))
assert len(outer) == 3
inner1, inner2, inner3 = outer

inner1 = Collection(g, inner1)
inner1_1, inner1_2 = map(lambda l: Collection(g, l), inner1)
assert list(inner1_1) == [Literal(x) for x in (1, 2, 3)]
assert list(inner1_2) == [Literal(x) for x in ("4", 5)]

assert inner2 == Literal(6)

inner3 = Collection(g, inner3)
assert list(inner3) == [Literal(7), res]


def test_no_container():
g = Graph()
g.parse(data=data_no_container, format="application/ld+json")

assert len(g) == 8

objects = set(g.objects(predicate=prop))
assert len(objects) == 8
assert objects == set([Literal(x) for x in (1, 2, 3, "4", 5, 6, 7)] + [res])
Loading