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

jsonld.expand incorrect when there are identical scoped contexts #180

Open
rossbowen opened this issue Sep 11, 2023 · 1 comment
Open

jsonld.expand incorrect when there are identical scoped contexts #180

rossbowen opened this issue Sep 11, 2023 · 1 comment

Comments

@rossbowen
Copy link

I have an example of a context where two of my terms have an identical scoped context - here creator and publisher have a scoped context which happens to be identical.

import json
from rdflib import Graph
from pyld import jsonld

CONTEXT = {
    "dcat": "http://www.w3.org/ns/dcat#",
    "dcterms": "http://purl.org/dc/terms/",
    "creator": {
        "@id": "dcterms:creator",
        "@type": "@id",
        "@context": {
            "@base": "http://example.org/organisations/",
        },
    },
    "publisher": {
        "@id": "dcterms:publisher",
        "@type": "@id",
        "@context": {
            "@base": "http://example.org/organisations/",
        },
    },
}

FRAME = {
    "@context": CONTEXT,
    "@type": "dcat:DatasetSeries",
}

db = Graph()
db.parse(
    data="""
        @prefix dcat: <http://www.w3.org/ns/dcat#> .
        @prefix dcterms: <http://purl.org/dc/terms/> .

        <http://example.org/datasets/cpih> a dcat:DatasetSeries ;
            dcterms:publisher <http://example.org/organisations/acme-inc> ;
            dcterms:creator <http://example.org/organisations/acme-inc> ;
            .
         """
)

result = db.query(
    """
        PREFIX dcat: <http://www.w3.org/ns/dcat#>
        PREFIX dcterms: <http://purl.org/dc/terms/>
        CONSTRUCT {
            ?ds a dcat:DatasetSeries ;
                dcterms:publisher ?publisher ;
                dcterms:creator ?creator ;
                .
        }
        WHERE {
            ?ds a dcat:DatasetSeries ;
                dcterms:publisher ?publisher ;
                dcterms:creator ?creator ;
                .
        }
    """
)

ld = json.loads(result.serialize(format="json-ld", context=CONTEXT))
framed = jsonld.frame(ld, FRAME)

My expected output is:

{
  "@context": {
    "dcat": "http://www.w3.org/ns/dcat#",
    "dcterms": "http://purl.org/dc/terms/",
    "creator": {
      "@id": "dcterms:creator",
      "@type": "@id",
      "@context": { "@base": "http://example.org/organisations/" }
    },
    "publisher": {
      "@id": "dcterms:publisher",
      "@type": "@id",
      "@context": { "@base": "http://example.org/organisations/" }
    }
  },
  "@id": "http://example.org/datasets/cpih",
  "@type": "dcat:DatasetSeries",
  "creator": "acme-inc",
  "publisher": "acme-inc"
}

But instead I get (notice the dcterms:publisher and the full URI):

{
  "@context": {
    "dcat": "http://www.w3.org/ns/dcat#",
    "dcterms": "http://purl.org/dc/terms/",
    "creator": {
      "@id": "dcterms:creator",
      "@type": "@id",
      "@context": { "@base": "http://example.org/organisations/" }
    },
    "publisher": {
      "@id": "dcterms:publisher",
      "@type": "@id",
      "@context": { "@base": "http://example.org/organisations/" }
    }
  },
  "@id": "http://example.org/datasets/cpih",
  "@type": "dcat:DatasetSeries",
  "creator": "acme-inc",
  "dcterms:publisher": "http://example.org/organisations/acme-inc"
}

I've stepped through the code a bit and noticed the issue isn't with the framing, but with the expansion.

Running JsonLdProcessor().expand(input_=ld, options=None) provides the following - notice how publisher gets an @value instead of a @id.

[
  {
    "@id": "http://example.org/datasets/cpih",
    "@type": ["http://www.w3.org/ns/dcat#DatasetSeries"],
    "http://purl.org/dc/terms/creator": [
      { "@id": "http://example.org/organisations/acme-inc" }
    ],
    "http://purl.org/dc/terms/publisher": [
      { "@value": "http://example.org/organisations/acme-inc" }
    ]
  }
]

Stepping through it further, the issue seems to be with the _process_context() not differentiating in the case of having two identical scoped contexts, which results in published being dropped from the term_ctx mapping.

pyld/lib/pyld/jsonld.py

Lines 2679 to 2684 in 316fbc2

# use potential scoped context for key
term_ctx = active_ctx
ctx = JsonLdProcessor.get_context_value(active_ctx, key, '@context')
if ctx is not None:
term_ctx = self._process_context(active_ctx, ctx, options,
propagate=True, override_protected=True)

A workaround for now is to trivially make the contexts different from one another.

@rossbowen
Copy link
Author

rossbowen commented Sep 20, 2023

An issue looks to be here:

pyld/lib/pyld/jsonld.py

Lines 5510 to 5515 in 316fbc2

if base and '@base' in active_ctx:
# The None case preserves rval as potentially relative
if active_ctx['@base'] is not None:
rval = prepend_base(prepend_base(base, active_ctx['@base']), rval)
elif base:
rval = prepend_base(base, rval)

base is usually passed though as an empty string "" so these conditions do not trigger.

Fixing returns this:

input = {
    "@context": {
        "a": {
            "@id": "http://example.org/a",
            "@type": "@id",
            "@context": {
                "@base": "http://example.org/",
            },
        },
    },
    "@id": "http://example.org/ex",
    "a": "a1",
}

expected = [
    {
        "@id": "http://example.org/ex",
        "http://example.org/a": [{"@id": "http://example.org/a1"}],
    }
]

jsonld.expand(input) == expected

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant