Skip to content

Commit

Permalink
fix: Add to_dict method to the JSON-LD Context class.
Browse files Browse the repository at this point in the history
`Context.to_dict` is used in JSON-LD serialization, but it was not implemented.
This change adds the method.
  • Loading branch information
aucampia committed Mar 24, 2023
1 parent f417eb7 commit 73a731e
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 10 deletions.
24 changes: 14 additions & 10 deletions rdflib/plugins/shared/jsonld/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def __init__(
self.terms: Dict[str, Any] = {}
# _alias maps NODE_KEY to list of aliases
self._alias: Dict[str, List[str]] = {}
self._lookup: Dict[Tuple[str, Any, Union[Defined, str], bool], Any] = {}
self._lookup: Dict[Tuple[str, Any, Union[Defined, str], bool], Term] = {}
self._prefixes: Dict[str, Any] = {}
self.active = False
self.parent: Optional[Context] = None
Expand Down Expand Up @@ -243,8 +243,10 @@ def add_term(

if isinstance(container, (list, set, tuple)):
container = set(container)
else:
elif container is not UNDEF:
container = set([container])
else:
container = set()

term = Term(
idref,
Expand Down Expand Up @@ -617,23 +619,25 @@ def _get_source_id(self, source: Dict[str, Any], key: str) -> Optional[str]:
term = term.get(ID)
return term

def _term_dict(self, term):
tdict = {ID: self.shrink_iri(term.id)}
def _term_dict(self, term: Term) -> Union[Dict[str, Any], str]:
tdict: Dict[str, Any] = {}
if term.type != UNDEF:
tdict[TYPE] = self.shrink_iri(term.type)
if term.container != UNDEF:
tdict[CONTAINER] = term.container
if term.container:
tdict[CONTAINER] = list(term.container)
if term.language != UNDEF:
tdict[LANG] = term.language
if term.reverse:
tdict[REV] = term.reverse
if len(tdict) == 1:
tdict[REV] = term.id
else:
tdict[ID] = term.id
if tdict.keys() == {ID}:
return tdict[ID]
return tdict

def to_dict(self):
def to_dict(self) -> Dict[str, Any]:
r = {v: k for (k, v) in self._prefixes.items()}
r.update({t.name: self._term_dict(t) for t in self._lookup.values()})
r.update({term.name: self._term_dict(term) for term in self._lookup.values()})
if self.base:
r[BASE] = self.base
if self.language:
Expand Down
51 changes: 51 additions & 0 deletions test/jsonld/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
JSON-LD Context Spec
"""

import json
from functools import wraps
from pathlib import Path
from typing import Any, Dict

from rdflib.namespace import PROV, XSD, Namespace
from rdflib.plugins.shared.jsonld import context, errors
from rdflib.plugins.shared.jsonld.context import Context

Expand Down Expand Up @@ -234,3 +236,52 @@ def test_dict_source(tmp_path: Path) -> None:
file.write_text(r"""{ "@context": { "ex": "http://example.com/" } }""")
ctx = Context(source=[{"@context": file.as_uri()}])
assert "http://example.com/" == ctx.terms["ex"].id


EG = Namespace("https://example.com/")

DIVERSE_CONTEXT = json.loads(
"""
{
"@context": {
"ex": "https://example.com/",
"generatedAt": { "@id": "http://www.w3.org/ns/prov#generatedAtTime", "@type": "http://www.w3.org/2001/XMLSchema#dateTime" },
"graphMap": { "@id": "https://example.com/graphMap", "@container": ["@graph", "@id"] },
"occupation_en": { "@id": "https://example.com/occupation", "@language": "en" },
"children": { "@reverse": "https://example.com/parent" }
}
}
"""
)


def test_parsing() -> None:
"""
A `Context` can be parsed from a dict.
"""
ctx = Context(DIVERSE_CONTEXT)
assert f"{EG}" == ctx.terms["ex"].id
assert f"{PROV.generatedAtTime}" == ctx.terms["generatedAt"].id
assert f"{XSD.dateTime}" == ctx.terms["generatedAt"].type
assert f"{EG.graphMap}" == ctx.terms["graphMap"].id
assert {"@graph", "@id"} == ctx.terms["graphMap"].container
assert f"{EG.occupation}" == ctx.terms["occupation_en"].id
assert "en" == ctx.terms["occupation_en"].language
assert False is ctx.terms["occupation_en"].reverse
assert True is ctx.terms["children"].reverse
assert f"{EG.parent}" == ctx.terms["children"].id


def test_to_dict() -> None:
"""
A `Context` can be converted to a dictionary.
"""
ctx = Context()
ctx.add_term("ex", f"{EG}")
ctx.add_term("generatedAt", f"{PROV.generatedAtTime}", coercion=f"{XSD.dateTime}")
ctx.add_term("graphMap", f"{EG.graphMap}", container=["@graph", "@id"])
ctx.add_term("occupation_en", f"{EG.occupation}", language="en")
ctx.add_term("children", f"{EG.parent}", reverse=True)
result = ctx.to_dict()
result["graphMap"]["@container"] = sorted(result["graphMap"]["@container"])
assert DIVERSE_CONTEXT["@context"] == result
44 changes: 44 additions & 0 deletions test/test_serializers/test_serializer_jsonld.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import json
import logging
import pprint
from typing import Any, Dict, Union

import pytest

from rdflib import Graph
from rdflib.namespace import Namespace
from rdflib.plugins.shared.jsonld.context import Context

EG = Namespace("http://example.org/")


@pytest.mark.parametrize(
["input"],
[
(
Context(
{
"eg": f"{EG}",
}
),
),
({"eg": f"{EG}"},),
],
)
def test_serialize_context(input: Union[Dict[str, Any], Context]) -> None:
"""
The JSON-LD serializer accepts and correctly serializes the context argument to the output.
"""
graph = Graph()
graph.add((EG.subject, EG.predicate, EG.object0))
graph.add((EG.subject, EG.predicate, EG.object1))
context = Context(
{
"eg": f"{EG}",
}
)
logging.debug("context = %s", pprint.pformat(vars(context)))
data = graph.serialize(format="json-ld", context=context)
logging.debug("data = %s", data)
obj = json.loads(data)
assert obj["@context"] == {"eg": f"{EG}"}

0 comments on commit 73a731e

Please sign in to comment.