Replies: 9 comments 22 replies
-
Well there's Commons RDF but that's essentially a dead project because none of the contributing projects could agree on anything 🤣 Turning this around could we think about inverting the control flow here? Part of the overhead right now AFAIK is that we have to hand full control over to Titanium and then convert its final output data model back into Jena's data model. If instead Titanium had an output interface in the vein of Jena's StreamRDF then we could hand a Jena specific implementation of that interface to Titanium. Then Titanium call the relevant methods on its interface as it produces triples which Jena can then immediately convert to its own types in a streaming fashion. I don't know if this is feasible within the JSON-LD processing model but perhaps worth considering. WDYT? |
Beta Was this translation helpful? Give feedback.
-
Current Titanium API works as a supplier (PULL), and accepts only complete data (PULL). requirements
PUSH: Titanium -> Jena// Jena calls toRDF
void JsonLd.toRDF(jsonld).options(...).produce(consumer);
//Titanium writes RDF to the provided consumer inside produce method
loop [
// Jena provided code called inside produce method
consumer.dataset(...);
consumer.accept(...)
]
// Jena gets back full control and might close the consumer
consumer.end(); // can be dropped, as toRDF's done writing PUSH: Jena -> Titanium// Jena calls fromRDF
<RDFConsumer, Supplier<JsonArray>> consumer = JsonLd.fromRDF().options(...).consumeAndSupply();
// Jena writes RDF to the consumer
loop [
consumer.dataset(...).accept(...).accept(...).accept(...) // Flow?
.dataset(...).accept(...);
]
// Jena closes the consumer
consumer.end();
// Jena gets JSON-LD
JsonArray jsonld = consumer.get(); // could act as end() APIinterface RDFConsumer {
dataset(name);
accept(resource, predicate, value, datatype);
accept(resource, predicate, value);
// end can be dropped to keep it minimal
end();
} Just brainstorming, thank you for the comments. |
Beta Was this translation helpful? Give feedback.
-
Hi, @afs I’d love to hear your feedback before it goes out. Thanks! package com.apicatalog.rdf;
/**
* RDF dataset consumer interface designed for
* speed, streamlined processing, and efficiency.
*
* This interface minimizes unnecessary object instantiation
* and facilitates seamless integration with third-party libraries,
* enabling efficient RDF data processing.
*/
public interface RdfConsumer {
/**
* Sets the default graph as the active scope.
* Invoked when triples belong to the unnamed, default graph.
*/
void defaultGraph();
/**
* Sets a named graph as the active scope.
* Ensures that subsequent triples are associated with the specified graph.
*
* @param graph The name of the graph (IRI or blank node identifier).
* @param blankGraph {@code true} if the graph name is a blank node identifier.
*/
void namedGraph(String graph, boolean blankGraph);
/**
* Accepts a new RDF triple where the object is an IRI or a blank node.
* The triple is processed within the currently active graph scope.
*
* @param subject The subject of the triple (IRI or blank node identifier).
* @param blankSubject {@code true} if the subject is a blank node identifier.
* @param predicate The predicate of the triple (IRI or blank node identifier).
* @param blankPredicate {@code true} if the predicate is a blank node identifier.
* @param object The object of the triple (IRI or blank node identifier).
* @param blankObject {@code true} if the object is a blank node identifier.
*/
void accept(
String subject,
boolean blankSubject,
String predicate,
boolean blankPredicate,
String object,
boolean blankObject
);
/**
* Accepts a new RDF triple where the object is a literal value.
* The triple is processed within the currently active graph scope.
* Optimized for efficient handling of typed and language-tagged literals.
*
* @param subject The subject of the triple (IRI or blank node identifier).
* @param blankSubject {@code true} if the subject is a blank node identifier.
* @param predicate The predicate of the triple (IRI or blank node identifier).
* @param blankPredicate {@code true} if the predicate is a blank node identifier.
* @param literal The literal value of the object.
* @param datatype The datatype IRI of the literal, never null.
* @param language The language tag of the literal (optional, may be {@code null}).
*/
void accept(
String subject,
boolean blankSubject,
String predicate,
boolean blankPredicate,
String literal,
String datatype,
String language
);
} |
Beta Was this translation helpful? Give feedback.
-
For RDF 1.2 proofing, add a base direction argument to the accept-litteral form. JSON-LD already has base direction. void accept(
String subject,
boolean blankSubject,
String predicate,
boolean blankPredicate,
String literal,
String datatype,
String language,
String baseDirection
); There is also "triple term" coming but it's not JSON-LD yet. |
Beta Was this translation helpful? Give feedback.
-
I agree about making it quads (only) but if, for Titanium, the triples style fits better then fine. It's not a huge difference. re: name "accept" or "triple" or "quad" - no preference, just not both "quad" and "triple". Is the blank node identifier a system generated unique id (UUID-like) or a local label encountered by the JSON-LD parser? If a blank node name starts with Will Titanium be able to stream-write via the supplier interface? |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
@afs @hmottestad Thank you very much for the feedback! This was very productive, and I hope we've found a good compromise while keeping the objective. If it's okay with you, then it's close to being shipped, perhaps after some quick polishing and refactoring, within a week. package com.apicatalog.rdf;
/**
* An optimized RDF quad consumer interface designed for high-speed processing
* and seamless integration with third-party libraries.
*
* This interface minimizes unnecessary object instantiation, enhancing
* efficiency in RDF data processing.
*/
public interface RdfQuadConsumer {
/**
* Accepts an RDF quad where the object is an IRI or a blank node.
*
* @param subject The subject of the quad (IRI or blank node identifier
* prefixed with "_:"); never {@code null}.
* @param predicate The predicate of the quad (IRI); never {@code null}.
* @param object The object of the quad (IRI or blank node identifier
* prefixed with "_:"); never {@code null}.
* @param graph The graph (IRI or blank node identifier prefixed with "_:"),
* or {@code null} for the default graph.
*
* @return An instance enabling fluent programming; never {@code null}.
*/
RdfQuadConsumer quad(
String subject,
String predicate,
String object,
String graph);
/**
* Accepts an RDF quad where the object is a literal with a datatype. Optimized
* for efficient handling of typed literals.
*
* @param subject The subject of the quad (IRI or blank node identifier
* prefixed with "_:"); never {@code null}.
* @param predicate The predicate of the quad (IRI); never {@code null}.
* @param literal The literal value of the object; never {@code null}.
* @param datatype The datatype IRI of the literal; never {@code null}.
* @param graph The graph (IRI or blank node identifier prefixed with "_:"),
* or {@code null} for the default graph.
*
* @return An instance enabling fluent programming; never {@code null}.
*/
RdfQuadConsumer quad(
String subject,
String predicate,
String literal,
String datatype,
String graph);
/**
* Accepts an RDF quad where the object is a localized string value. Optimized
* for efficient handling of language-tagged literals.
*
* @param subject The subject of the quad (IRI or blank node identifier
* prefixed with "_:"); never {@code null}.
* @param predicate The predicate of the quad (IRI); never {@code null}.
* @param literal The literal value of the object; never {@code null}.
* @param language The language code of the literal; never {@code null}.
* @param direction The text direction of the literal (optional, may be
* {@code null}).
* @param graph The graph (IRI or blank node identifier prefixed with "_:"),
* or {@code null} for the default graph.
*
* @return An instance enabling fluent programming; never {@code null}.
*/
RdfQuadConsumer quad(
String subject,
String predicate,
String literal,
String language,
String direction,
String graph);
} |
Beta Was this translation helpful? Give feedback.
-
Hi, package com.apicatalog.rdf.api;
/**
* RDF quad consumer interface designed for high-performance processing and
* seamless integration with third-party libraries.
* <p>
* This interface minimizes unnecessary object instantiation, improving
* efficiency when handling RDF data at scale.
* <p>
* Use the provided static helper methods to analyze and validate consumer
* parameters.
*/
@FunctionalInterface
public interface RdfQuadConsumer {
/**
* Consumes an RDF quad where the {@code object} may be an IRI, blank node,
* typed literal, or language-tagged literal.
* <p>
* This method provides fine-grained control over RDF quad data, allowing
* precise handling of datatypes, language tags, and text direction.
*
* @param subject the subject of the quad; must be an IRI or blank node
* identifier prefixed with "<code>_:</code>". Must not be
* {@code null}.
* @param predicate the predicate of the quad; must be an IRI. Must not be
* {@code null}.
* @param object the object of the quad; must be either:
* <ul>
* <li>an IRI</li>
* <li>a blank node identifier prefixed with
* "<code>_:</code>"</li>
* <li>a literal value, when {@code datatype} is not
* {@code null}</li>
* </ul>
* Must not be {@code null}.
* <p>
* Use {@link #isValidObject(String, String, String)},
* {@link #isLiteral(String, String, String)},
* {@link #isLangString(String, String, String)}, and
* {@link #isDirLangString(String, String, String)} to validate
* and classify the input.
* @param datatype the datatype IRI of the literal. Must be {@code null} if
* {@code object} is not a literal. Must not be {@code null}
* when {@code language} or {@code direction} is provided.
* @param language the language tag of the literal. May be {@code null}, but
* must not be {@code null} if {@code direction} is provided.
* @param direction the text direction of the literal. Optional; may be
* {@code null}.
* @param graph the graph name of the quad; must be an IRI or blank node
* identifier prefixed with "<code>_:</code>". May be
* {@code null} to indicate the default graph.
*
* @return a reference to this consumer, enabling fluent chaining; never
* {@code null}.
*
* @throws RdfConsumerException if an error occurs while processing the quad
* statement.
*/
RdfQuadConsumer quad(
String subject,
String predicate,
String object,
String datatype,
String language,
String direction,
String graph) throws RdfConsumerException;
/**
* Determines if the provided combination of {@code datatype}, {@code language},
* and {@code direction} qualifies the object as RDF literal.
*
* @param datatype the datatype IRI
* @param language the language tag
* @param direction the text direction
* @return {@code true} indicating a literal, otherwise {@code false}.
*/
static boolean isLiteral(String datatype, String language, String direction) {
return datatype != null;
}
/**
* Determines if the provided combination of {@code datatype}, {@code language},
* and {@code direction} qualifies the object as an RDF language-tagged string
* literal with no specified direction.
*
* @param datatype the datatype IRI
* @param language the language tag
* @param direction the text direction
* @return {@code true} if the provided object is RDF language-tagged literal,
* otherwise {@code false}.
*/
static boolean isLangString(String datatype, String language, String direction) {
return datatype != null && language != null && direction == null;
}
/**
* Determines if the provided combination of {@code datatype}, {@code language},
* and {@code direction} qualifies the object as an RDF directional
* language-tagged string literal with a specified direction.
*
* @param datatype the datatype IRI
* @param language the language tag
* @param direction the text direction
* @return {@code true} if the provided object is RDF directional
* language-tagged literal, otherwise {@code false}.
*/
static boolean isDirLangString(String datatype, String language, String direction) {
return datatype != null && language != null && direction != null;
}
/**
* Validates whether the object is a valid RDF object based on the presence of
* {@code datatype}, {@code language}, and {@code direction}.
*
* @param datatype the datatype IRI
* @param language the language tag
* @param direction the text direction
* @return {@code true} if the object is valid according to RDF term rules.
*/
static boolean isValidObject(String datatype, String language, String direction) {
return datatype != null
? (language != null || direction == null)
: (language == null && direction == null);
}
/**
* Checks whether the provided resource identifier represents a blank node. A
* blank node identifier must start with "<code>_:</code>".
*
* @param resource the resource identifier to check; may be {@code null}.
* @return {@code true} if the resource is a non-null blank node identifier;
* otherwise {@code false}.
*/
static boolean isBlank(String resource) {
return resource != null && resource.startsWith("_:");
}
} Supported by |
Beta Was this translation helpful? Give feedback.
-
I'm not seeing a change to Writing is harder to abstract away completely because it is where application choices (context, compaction. framing, ...) come in. Jena provides some options but, when the app wants detailed control, it is better for the application to convert a Jena RDF Dataset to a Titanium RdfDataset and use the Titanium APIs. Jena code would be no more than translating from some Jena JSON-LD specific settings to Titanium calls in a 1-1 fashion. |
Beta Was this translation helpful? Give feedback.
-
Let's collaborate on a new repository with RDF dataset interfaces that would allow seamless integration between Jena & Titanium and maybe other libs, like URDNA2015, N-QUADS Reader/Writer (bundled with Titanium now) etc.
The interface(s) should be minimal and single purpose, to exchange RDF dataset, i.e. reading. It is hard, and there are existing attempts, but something like micro-jars, containing only minimum necessary, single purpose, interfaces could be a way to deal with limitations/features coming from strongly typed language and dependencies linking enforcing strong consistency. In comparison to the JavaScript world where compatibility is declared.
Beta Was this translation helpful? Give feedback.
All reactions