diff --git a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDSettings.java b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDSettings.java index d7774158959..01dc34b3da0 100644 --- a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDSettings.java +++ b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDSettings.java @@ -63,6 +63,11 @@ public class JSONLDSettings { public static final RioSetting FRAME = new ClassRioSetting<>( "org.eclipse.rdf4j.rio.jsonld.frame_document", "A no.hasmac.jsonld.document.Document that contains the frame used for framing as specified in https://www.w3.org/TR/json-ld11-framing/", + null); + + public static final RioSetting CONTEXT = new ClassRioSetting<>( + "org.eclipse.rdf4j.rio.jsonld.context_document", + "A no.hasmac.jsonld.document.Document that contains the JSON-LD context as specified in https://www.w3.org/TR/json-ld11/#the-context. If not defined, the context is created using the namespaces defined in the model", null);; /** diff --git a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriter.java b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriter.java index 5633a9c6fc6..372acbab805 100644 --- a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriter.java +++ b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriter.java @@ -267,15 +267,24 @@ public int size() { jsonld = JsonLd.flatten(JsonDocument.of(jsonld)).options(opts).get(); break; case COMPACT: - JsonObjectBuilder context = Json.createObjectBuilder(); - for (Namespace namespace : model.getNamespaces()) { - if (namespace.getPrefix().isEmpty()) { - context.add("@vocab", namespace.getName()); - } else { - context.add(namespace.getPrefix(), namespace.getName()); + Document context = writerConfig.get(JSONLDSettings.CONTEXT); + if (context == null) { + + // define a default context using namespaces provided in the + // model if none is provided to the writer settings + JsonObjectBuilder contextBuilder = Json.createObjectBuilder(); + for (Namespace namespace : model.getNamespaces()) { + if (namespace.getPrefix().isEmpty()) { + contextBuilder.add("@vocab", namespace.getName()); + } else { + contextBuilder.add(namespace.getPrefix(), namespace.getName()); + } } + + context = JsonDocument.of(contextBuilder.build()); } - jsonld = JsonLd.compact(JsonDocument.of(jsonld), JsonDocument.of(context.build())).options(opts).get(); + + jsonld = JsonLd.compact(JsonDocument.of(jsonld), context).options(opts).get(); break; } diff --git a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterTest.java b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterTest.java index 0a904b5ab8e..97a49acabe2 100644 --- a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterTest.java +++ b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDWriterTest.java @@ -22,9 +22,15 @@ import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.Model; +import org.eclipse.rdf4j.model.Namespace; import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.model.impl.LinkedHashModel; +import org.eclipse.rdf4j.model.util.ModelBuilder; +import org.eclipse.rdf4j.model.util.Values; import org.eclipse.rdf4j.model.vocabulary.DCTERMS; +import org.eclipse.rdf4j.model.vocabulary.FOAF; +import org.eclipse.rdf4j.model.vocabulary.RDF; +import org.eclipse.rdf4j.model.vocabulary.RDFS; import org.eclipse.rdf4j.model.vocabulary.XSD; import org.eclipse.rdf4j.rio.ParserConfig; import org.eclipse.rdf4j.rio.RDFFormat; @@ -39,9 +45,12 @@ import org.eclipse.rdf4j.rio.helpers.BasicParserSettings; import org.eclipse.rdf4j.rio.helpers.BasicWriterSettings; import org.eclipse.rdf4j.rio.helpers.StatementCollector; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import jakarta.json.Json; +import jakarta.json.JsonObjectBuilder; import no.hasmac.jsonld.JsonLdError; import no.hasmac.jsonld.document.Document; import no.hasmac.jsonld.document.JsonDocument; @@ -238,6 +247,69 @@ public void testFraming() throws IOException, JsonLdError { } + @Test + public void testContextRefine() throws Exception { + + Model model = new ModelBuilder() + .subject(Values.iri("https://example.com/bob")) + .add(RDF.TYPE, FOAF.PERSON) + .add(RDFS.LABEL, Values.literal("Bob", "en")) + .add(FOAF.NAME, Values.literal("Bob")) + .build(); + + model.setNamespace("rdfs", RDFS.NAMESPACE); + model.setNamespace("foaf", FOAF.NAMESPACE); + model.setNamespace("", "https://example.com/"); + + JsonDocument context = JsonDocument.of(new StringReader("" + + "{\n" + + " \"rdfs\": \"http://www.w3.org/2000/01/rdf-schema#\",\n" + + " \"foaf\": \"http://xmlns.com/foaf/0.1/\",\n" + + " \"@vocab\": \"https://example.com/\",\n" + + " \"label\": {\n" + + " \"@id\": \"rdfs:label\",\n" + + " \"@container\": \"@language\"\n" + + " },\n" + + " \"name\": \"foaf:name\",\n" + + " \"Person\": \"foaf:Person\"\n" + + "}")); + + StringWriter sw = new StringWriter(); + + JSONLDWriter mpJsonLd = new JSONLDWriter(sw); + mpJsonLd.set(JSONLDSettings.JSONLD_MODE, JSONLDMode.COMPACT); + mpJsonLd.set(BasicWriterSettings.PRETTY_PRINT, true); + mpJsonLd.set(JSONLDSettings.CONTEXT, context); + + Rio.write(model, mpJsonLd); + + Assertions.assertEquals( + "{\n" + + " \"@id\": \"https://example.com/bob\",\n" + + " \"@type\": \"Person\",\n" + + " \"label\": {\n" + + " \"en\": \"Bob\"\n" + + " },\n" + + " \"name\": \"Bob\",\n" + + " \"@context\": {\n" + + " \"rdfs\": \"http://www.w3.org/2000/01/rdf-schema#\",\n" + + " \"foaf\": \"http://xmlns.com/foaf/0.1/\",\n" + + " \"@vocab\": \"https://example.com/\",\n" + + " \"label\": {\n" + + " \"@id\": \"rdfs:label\",\n" + + " \"@container\": \"@language\"\n" + + " },\n" + + " \"name\": \"foaf:name\",\n" + + " \"Person\": \"foaf:Person\"\n" + + " }\n" + + "}", + sw.toString()); + + // test round-trip + Model parsed = Rio.parse(new StringReader(sw.toString()), RDFFormat.JSONLD); + Assertions.assertEquals(model, parsed); + } + @Override protected RioSetting[] getExpectedSupportedSettings() { return new RioSetting[] {