Skip to content

Commit 71a7c63

Browse files
committed
round-trip RDF namespaces
1 parent 8a63664 commit 71a7c63

File tree

5 files changed

+45
-23
lines changed

5 files changed

+45
-23
lines changed

reader-cj/src/main/java/com/graphinout/reader/cj/CjValidator.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,7 @@ public static void validateCjReferences(String cjJson, ContentErrorList errors)
9999
}
100100
}
101101
});
102-
// Expanded URIs:
103-
// - context + edge.type,
104-
// - TODO context + node.types
102+
// Expanded URIs: context + edge.type,
105103
cjDoc.edgesAll().forEach(edge -> {
106104
ifPresentAccept(edge.edgeType(), edgeType -> {
107105
// expand edge type ID via @context and validate resulting URI
@@ -112,6 +110,17 @@ public static void validateCjReferences(String cjJson, ContentErrorList errors)
112110
});
113111
});
114112

113+
// Expanded URIs: context + node.types
114+
cjDoc.nodesAll().forEach(node -> {
115+
node.types().forEach(nodeType -> {
116+
// expand node type ID via @context and validate resulting URI
117+
String uri = cjDoc.uri(nodeType.type());
118+
if (!isValidUri(uri)) {
119+
errors.add(ContentError.error("URI Error: Node type URI from String '" + uri + "' is not a valid URI"));
120+
}
121+
});
122+
});
123+
115124
// check for empty graphs or edges
116125
cjDoc.graphs().forEach(graph -> {
117126
if (graph.nodes().findAny().isEmpty() && graph.edges().findAny().isEmpty()) {

reader-rdf/src/main/java/com/graphinout/reader/rdf/CjDoc2RdfModel.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ private static void cjEdge2rdfModel(@Nullable Map<String, String> context, ICjEd
4747
List<ICjEndpoint> targets = cjEdge.targets();
4848
List<ICjEndpoint> undirectedEndpoints = cjEdge.undirectedEndpoints();
4949

50-
String edgeType = cjEdge.type();
50+
String edgeType = cjEdge.type() != null ? CjUris.expandId(context, cjEdge.type()) : null;
5151

5252
if (endpoints.size() == 2 && sources.size() < 2 && targets.size() < 2) {
5353
// Order can be derived from endpoints
@@ -87,12 +87,12 @@ private static void cjEdge2rdfModel(@Nullable Map<String, String> context, ICjEd
8787
}
8888
ifPresentAccept(sourceEndpoint.type(), sssType -> {
8989
// Triple: (SSS, SSS_TYPE, TTT)
90-
Property property = rdfModel.getProperty(sssType);
90+
Property property = rdfModel.getProperty(CjUris.expandId(context, sssType));
9191
rdfModel.add(subject, property, object);
9292
});
9393
ifPresentAccept(targetEndpoint.type(), tttType -> {
9494
// Triple: (SSS, TTT_TYPE, TTT)
95-
Property property = rdfModel.getProperty(tttType);
95+
Property property = rdfModel.getProperty(CjUris.expandId(context, tttType));
9696
rdfModel.add(subject, property, object);
9797
});
9898

@@ -118,11 +118,11 @@ private static void cjEdge2rdfModel(@Nullable Map<String, String> context, ICjEd
118118
String predicateUri;
119119
// Priority: endpoint type > edge type
120120
if (ep2.type() != null) {
121-
predicateUri = ep2.type();
121+
predicateUri = CjUris.expandId(context, ep2.type());
122122
} else if (ep1.type() != null) {
123-
predicateUri = ep1.type();
123+
predicateUri = CjUris.expandId(context, ep1.type());
124124
} else if (cjEdge.edgeType() != null) {
125-
predicateUri = cjEdge.type();
125+
predicateUri = CjUris.expandId(context, cjEdge.type());
126126
} else {
127127
predicateUri = RdfCj.CjInRdf.IS_RELATED;
128128
}
@@ -157,7 +157,7 @@ private static void cjNode2rdfModel(@Nullable Map<String, String> context, ICjNo
157157

158158
// Add node types
159159
cjNode.types().forEach(cjType -> {
160-
Resource typeResource = rdfModel.createResource(cjType.type());
160+
Resource typeResource = rdfModel.createResource(CjUris.expandId(context, cjType.type()));
161161
rdfModel.add(rdfSubject, RDF.type, typeResource);
162162
});
163163

@@ -182,7 +182,7 @@ private static void cjNode2rdfModel(@Nullable Map<String, String> context, ICjNo
182182
if (value != null) {
183183
if (value.isObject()) {
184184
IJsonObject dataObject = value.asObject();
185-
jsonObject2rdfModel(rdfSubject, dataObject, rdfModel);
185+
jsonObject2rdfModel(context, rdfSubject, dataObject, rdfModel);
186186
}
187187
}
188188
});
@@ -207,7 +207,7 @@ private static ICjNode findNode(ICjDocument cjDoc, String nodeId) {
207207
return cjDoc.nodesAll().filter(n -> nodeId.equals(n.id())).findFirst().orElse(null);
208208
}
209209

210-
private static void jsonObject2rdfModel(Resource rdfSubject, IJsonObject dataObject, Model rdfModel) {
210+
private static void jsonObject2rdfModel(@Nullable Map<String, String> context, Resource rdfSubject, IJsonObject dataObject, Model rdfModel) {
211211
boolean hasRdfData = false;
212212

213213
// Check if there's an "rdf:data" object containing RDF property-value pairs
@@ -217,9 +217,9 @@ private static void jsonObject2rdfModel(Resource rdfSubject, IJsonObject dataObj
217217
hasRdfData = true;
218218
IJsonObject rdfDataObject = rdfDataValue.asObject();
219219
// Extract and emit RDF triples from the "rdf:data" object
220-
for (String predicateUri : rdfDataObject.keys()) {
221-
Property predicate = rdfModel.createProperty(predicateUri);
222-
IJsonValue value = rdfDataObject.get(predicateUri);
220+
for (String predicateId : rdfDataObject.keys()) {
221+
Property predicate = rdfModel.createProperty(CjUris.expandId(context, predicateId));
222+
IJsonValue value = rdfDataObject.get(predicateId);
223223

224224
toRdfLiterals(rdfModel, value, literal -> {
225225
rdfModel.add(rdfSubject, predicate, literal);

reader-rdf/src/main/java/com/graphinout/reader/rdf/RdfModel2CjDoc.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import com.graphinout.base.cj.CjConstants;
2222

23+
import java.util.HashMap;
2324
import java.util.HashSet;
2425
import java.util.Map;
2526
import java.util.Set;
@@ -58,9 +59,13 @@ public static void rdfModel2cjDoc(Model rdfModel, ICjDocumentMutable cjDoc, Stri
5859
c.versionDate(ConnectedJson.CJ_LATEST_VERSION_DATE);
5960
c.versionNumber(ConnectedJson.CJ_LATEST_VERSION_NUMBER);
6061
});
61-
// Set @context with @vocab if a baseUri was provided
62+
// Collect RDF namespace prefixes into @context
63+
Map<String, String> contextMap = new HashMap<>(rdfModel.getNsPrefixMap());
6264
if (baseUri != null && !baseUri.isEmpty()) {
63-
cjDoc.context(Map.of(CjConstants.VOCAB, baseUri));
65+
contextMap.put(CjConstants.VOCAB, baseUri);
66+
}
67+
if (!contextMap.isEmpty()) {
68+
cjDoc.context(contextMap);
6469
}
6570

6671
cjDoc.addGraph(cjGraph -> rdfModel2CjGraph(rdfModel, cjDoc, cjGraph));

reader-rdf/src/main/java/com/graphinout/reader/rdf/RdfReader.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ public void write(ICjDocument cjDoc, OutputSink outputSink) throws IOException {
111111
model.setNsPrefix("cj", RdfCj.CjInRdf.VOC);
112112
model.setNsPrefix("rdf", RDF.uri);
113113
model.setNsPrefix("rdfs", RDFS.uri);
114+
// Map @context prefix entries back to RDF namespace prefixes
115+
if (context != null) {
116+
for (Map.Entry<String, String> entry : context.entrySet()) {
117+
if (!CjConstants.VOCAB.equals(entry.getKey())) {
118+
model.setNsPrefix(entry.getKey(), entry.getValue());
119+
}
120+
}
121+
}
114122

115123
// Write RDF as Turtle (more readable than RDF/XML)
116124
StringWriter stringWriter = new StringWriter();

reader-rdf/src/test/resources/text/rdf/test--EXPECTED-rdf2cj.cj.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
2-
"baseUri": "https://example.com/",
2+
"@context": {"ex": "https://example.com/"},
33
"connectedJson": { "canonical": false, "versionDate": "2026-01-15", "versionNumber": "7.0.0" }, "graphs": [
44
{
55
"nodes": [
6-
{ "id": "Alice" },
7-
{ "id": "Bob" },
8-
{ "id": "Charlie" }
6+
{ "id": "ex:Alice" },
7+
{ "id": "ex:Bob" },
8+
{ "id": "ex:Charlie" }
99
],
1010
"edges": [
11-
{ "type": "knows", "endpoints": [ { "node": "Alice", "direction": "in" }, { "node": "Bob", "direction": "out" } ] },
12-
{ "type": "knows", "endpoints": [ { "node": "Charlie", "direction": "out" }, { "node": "Bob", "direction": "in" } ] }
11+
{ "type": "ex:knows", "endpoints": [ { "node": "ex:Alice", "direction": "in" }, { "node": "ex:Bob", "direction": "out" } ] },
12+
{ "type": "ex:knows", "endpoints": [ { "node": "ex:Charlie", "direction": "out" }, { "node": "ex:Bob", "direction": "in" } ] }
1313
]
1414
}
1515
]

0 commit comments

Comments
 (0)