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

Feat/rdf #389

Merged
merged 7 commits into from
Feb 23, 2025
Merged
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
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,18 @@ JsonLd.flatten("https://example/document.jsonld").get();
// JSON-LD to RDF
JsonLd.toRdf("https://example/document.jsonld").get();
// or, since 1.6.0
JsonLd.toRdf("https://example/document.jsonld").process(RdfConsumer);
JsonLd.toRdf("https://example/document.jsonld").provide(RdfConsumer);

// Standard RDF Dataset Canonicalization with Titanium RDFC, since 1.6.0
var canonicalizer = new RdfCanonicalizer();
JsonLd.toRdf("https://example/document.jsonld").provide(canonicalizer);
canonicalizer.canonize(RdfConsumer);
// or, since 1.7.0
var rdfWriter = Rdf.createWriter(MediaType.N_QUADS, writer);
canonicalizer.canonize(rdfWriter);

// RDF to JSON-LD
JsonLd.fromRdf("https://example/document.nq").options(options).get();
JsonLd.fromRdf("https://example/document.nq").get();

// Framing
JsonLd.frame("https://example/document.jsonld", "https://example/frame.jsonld").get();
Expand Down
26 changes: 19 additions & 7 deletions src/main/java/com/apicatalog/jsonld/api/ToRdfApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
import com.apicatalog.jsonld.loader.DocumentLoader;
import com.apicatalog.jsonld.processor.ToRdfProcessor;
import com.apicatalog.jsonld.uri.UriUtils;
import com.apicatalog.rdf.RdfConsumer;
import com.apicatalog.rdf.RdfTripleConsumer;
import com.apicatalog.rdf.RdfDataset;
import com.apicatalog.rdf.RdfDatasetProducer;
import com.apicatalog.rdf.RdfDatasetSupplier;
import com.apicatalog.rdf.RdfQuadConsumer;
import com.apicatalog.rdf.RdfQuadEmitter;

import jakarta.json.JsonStructure;

Expand Down Expand Up @@ -164,18 +166,18 @@ public ToRdfApi ordered(boolean enable) {
* @throws JsonLdError
*/
public RdfDataset get() throws JsonLdError {
final RdfDatasetProducer consumer = new RdfDatasetProducer();
process(consumer);
return consumer.dataset();
final RdfDatasetSupplier consumer = new RdfDatasetSupplier();
provide(consumer);
return consumer.get();
}

/**
* Emit transformed <code>JSON-LD</code> as RDF statements.
* Emit transformed <code>JSON-LD</code> as RDF graph scoped triples.
*
* @param consumer that accepts emitted RDF statements
* @throws JsonLdError
*/
public void process(RdfConsumer consumer) throws JsonLdError {
public void provide(RdfTripleConsumer consumer) throws JsonLdError {
if (documentUri != null) {
ToRdfProcessor.toRdf(consumer, documentUri, options);

Expand All @@ -187,6 +189,16 @@ public void process(RdfConsumer consumer) throws JsonLdError {
}
}

/**
* Emit transformed <code>JSON-LD</code> as RDF quads.
*
* @param consumer that accepts emitted RDF statements
* @throws JsonLdError
*/
public void provide(RdfQuadConsumer consumer) throws JsonLdError {
provide(new RdfQuadEmitter(consumer));
}

/**
* Experimental: Accept numeric <code>@id</code>. Disabled by default.
*
Expand Down
141 changes: 49 additions & 92 deletions src/main/java/com/apicatalog/jsonld/deseralization/JsonLdToRdf.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
import com.apicatalog.jsonld.uri.UriUtils;
import com.apicatalog.jsonld.uri.UriValidationPolicy;
import com.apicatalog.rdf.Rdf;
import com.apicatalog.rdf.RdfConsumer;
import com.apicatalog.rdf.RdfDataset;
import com.apicatalog.rdf.RdfDatasetProducer;
import com.apicatalog.rdf.RdfDatasetSupplier;
import com.apicatalog.rdf.RdfTripleConsumer;
import com.apicatalog.rdf.lang.RdfConstants;
import com.apicatalog.rdf.lang.XsdConstants;

Expand Down Expand Up @@ -83,7 +83,7 @@ private JsonLdToRdf(NodeMap nodeMap, RdfDataset dataset) {

/**
* @deprecated since 1.6.0, use {@link #with(NodeMap)} and
* {@link #process(RdfConsumer)}.
* {@link #provide(RdfTripleConsumer)}.
* @param nodeMap
* @param dataset
* @return
Expand All @@ -107,31 +107,26 @@ public JsonLdToRdf rdfDirection(RdfDirection rdfDirection) {
return this;
}

public void process(RdfConsumer consumer) throws JsonLdError {
public void provide(RdfTripleConsumer consumer) throws JsonLdError {

for (final String graphName : Utils.index(nodeMap.graphs(), true)) {

if (Keywords.DEFAULT.equals(graphName)) {
consumer.defaultGraph();

} else if (BlankNode.isWellFormed(graphName)) {
consumer.namedGraph(graphName, true);
consumer.namedGraph(graphName);

} else if (UriUtils.isAbsoluteUri(graphName, uriValidation)) {
consumer.namedGraph(graphName, false);
consumer.namedGraph(graphName);

} else {
continue;
}

for (final String subject : Utils.index(nodeMap.subjects(graphName), true)) {

boolean blankSubject = false;

if (BlankNode.isWellFormed(subject)) {
blankSubject = true;

} else if (UriUtils.isNotAbsoluteUri(subject, uriValidation)) {
if (!BlankNode.isWellFormed(subject) && UriUtils.isNotAbsoluteUri(subject, uriValidation)) {
LOGGER.log(Level.WARNING, "Non well-formed subject [{0}] has been skipped.", subject);
continue;
}
Expand All @@ -148,43 +143,28 @@ public void process(RdfConsumer consumer) throws JsonLdError {

final String typeString = ((JsonString) type).getString();

boolean blankType = false;

if (BlankNode.isWellFormed(typeString)) {
blankType = true;

} else if (UriUtils.isNotAbsoluteUri(typeString, uriValidation)) {
if (!BlankNode.isWellFormed(typeString) && UriUtils.isNotAbsoluteUri(typeString, uriValidation)) {
continue;
}

consumer.accept(
consumer.triple(
subject,
blankSubject,
RdfConstants.TYPE,
false,
typeString,
blankType);
typeString);
}

} else if (!Keywords.contains(property)) {

boolean blankProperty = false;

if (BlankNode.isWellFormed(property) && !produceGeneralizedRdf) {
blankProperty = true;

} else if (UriUtils.isNotAbsoluteUri(property, uriValidation)) {
if ((!BlankNode.isWellFormed(property) || produceGeneralizedRdf) && UriUtils.isNotAbsoluteUri(property, uriValidation)) {
continue;
}

for (final JsonValue item : nodeMap.get(graphName, subject, property).asJsonArray()) {
processObject(
fromObject(
consumer,
item.asJsonObject(),
subject,
blankSubject,
property,
blankProperty);
property);
}
}
}
Expand All @@ -193,7 +173,7 @@ public void process(RdfConsumer consumer) throws JsonLdError {
}

/**
* @deprecated since 1.6.0, use {@link #process(RdfConsumer)}.
* @deprecated since 1.6.0, use {@link #provide(RdfTripleConsumer)}.
* @return
* @throws JsonLdError
*/
Expand All @@ -204,7 +184,7 @@ public RdfDataset build() throws JsonLdError {
dataset = Rdf.createDataset();
}

process(new RdfDatasetProducer(dataset));
provide(new RdfDatasetSupplier(dataset));

return dataset;
}
Expand All @@ -227,13 +207,11 @@ public JsonLdToRdf uriValidation(UriValidationPolicy uriValidation) {
* "https://w3c.github.io/json-ld-api/#deserialize-json-ld-to-rdf-algorithm">
* Object to RDF Conversion</a>
*/
private void processObject(
final RdfConsumer consumer,
private void fromObject(
final RdfTripleConsumer consumer,
final JsonObject item,
final String subject,
final boolean blankSubject,
final String predicate,
final boolean blankPredicate) throws JsonLdError {
final String predicate) throws JsonLdError {

// 1. - 2.
if (NodeObject.isNodeObject(item)) {
Expand All @@ -246,19 +224,16 @@ private void processObject(

String idString = ((JsonString) id).getString();

if (BlankNode.isWellFormed(idString)) {
consumer.accept(subject, blankSubject, predicate, blankPredicate, idString, true);

} else if (UriUtils.isAbsoluteUri(idString, uriValidation)) {
consumer.accept(subject, blankSubject, predicate, blankPredicate, idString, false);
if (BlankNode.isWellFormed(idString) || UriUtils.isAbsoluteUri(idString, uriValidation)) {
consumer.triple(subject, predicate, idString);
}

return;
}

// 3.
if (ListObject.isListObject(item)) {
processList(consumer, item.get(Keywords.LIST).asJsonArray(), subject, blankSubject, predicate, blankPredicate);
fromList(consumer, item.get(Keywords.LIST).asJsonArray(), subject, predicate);
}

// 4.
Expand Down Expand Up @@ -365,70 +340,56 @@ private void processObject(
: "";
// 13.2.
if (RdfDirection.I18N_DATATYPE == rdfDirection) {
datatype = "https://www.w3.org/ns/i18n#"
.concat(language)
.concat("_")
.concat(item.getString(Keywords.DIRECTION));

consumer.accept(
subject, blankSubject,
predicate, blankPredicate,
valueString, datatype, null);
consumer.triple(
subject,
predicate,
valueString, language, item.getString(Keywords.DIRECTION));

// 13.3.
} else if (RdfDirection.COMPOUND_LITERAL == rdfDirection) {

final String blankNodeId = nodeMap.createIdentifier();

// 13.3.2.
consumer.accept(
consumer.triple(
blankNodeId,
true,
RdfConstants.VALUE,
false,
valueString,
XsdConstants.STRING,
null);
XsdConstants.STRING);

// 13.3.3.
if (item.containsKey(Keywords.LANGUAGE) && JsonUtils.isString(item.get(Keywords.LANGUAGE))) {
consumer.accept(
consumer.triple(
blankNodeId,
true,
RdfConstants.LANGUAGE,
false,
item.getString(Keywords.LANGUAGE).toLowerCase(),
XsdConstants.STRING,
null);
XsdConstants.STRING);
}

// 13.3.4.
consumer.accept(
consumer.triple(
blankNodeId,
true,
RdfConstants.DIRECTION,
false,
item.getString(Keywords.DIRECTION),
XsdConstants.STRING,
null);
XsdConstants.STRING);

consumer.accept(subject, blankSubject, predicate, blankPredicate, blankNodeId, true);
consumer.triple(subject, predicate, blankNodeId);
return;
}

// 14.
} else {
if (item.containsKey(Keywords.LANGUAGE) && JsonUtils.isString(item.get(Keywords.LANGUAGE))) {
consumer.accept(
subject, blankSubject,
predicate, blankPredicate,
valueString, RdfConstants.LANG_STRING, item.getString(Keywords.LANGUAGE));
consumer.triple(
subject,
predicate,
valueString, item.getString(Keywords.LANGUAGE), null);

} else {
consumer.accept(
subject, blankSubject,
predicate, blankPredicate,
valueString, datatype, null);
consumer.triple(
subject,
predicate,
valueString, datatype);
}
}
}
Expand All @@ -437,17 +398,15 @@ private void processObject(
* @see <a href="https://w3c.github.io/json-ld-api/#list-to-rdf-conversion">List
* to RDF Conversion</a>
*/
private void processList(
final RdfConsumer consumer,
private void fromList(
final RdfTripleConsumer consumer,
final JsonArray list,
final String subject,
final boolean blankSubject,
final String predicate,
final boolean blankPredicate) throws JsonLdError {
final String predicate) throws JsonLdError {

// 1.
if (JsonUtils.isEmptyArray(list)) {
consumer.accept(subject, blankSubject, predicate, blankPredicate, RdfConstants.NIL, false);
consumer.triple(subject, predicate, RdfConstants.NIL);
return;
}

Expand All @@ -456,7 +415,7 @@ private void processList(

IntStream.range(0, bnodes.length).forEach(i -> bnodes[i] = nodeMap.createIdentifier());

consumer.accept(subject, blankSubject, predicate, blankPredicate, bnodes[0], true);
consumer.triple(subject, predicate, bnodes[0]);

// 3.
int index = 0;
Expand All @@ -465,20 +424,18 @@ private void processList(
final String blankNodeSubject = bnodes[index];
index++;

processObject(
fromObject(
consumer,
item.asJsonObject(),
blankNodeSubject,
true,
RdfConstants.FIRST,
false);
RdfConstants.FIRST);

// 3.4.
if (index < bnodes.length) {
consumer.accept(blankNodeSubject, true, RdfConstants.REST, false, bnodes[index], true);
consumer.triple(blankNodeSubject, RdfConstants.REST, bnodes[index]);

} else {
consumer.accept(blankNodeSubject, true, RdfConstants.REST, false, RdfConstants.NIL, false);
consumer.triple(blankNodeSubject, RdfConstants.REST, RdfConstants.NIL);
}
}
}
Expand Down
Loading
Loading