Hi,
quite some time I have not been using json-ld + jena (I wasn’t even aware of
the “@embed”). Anyway, here are some observations. Hope they can help. (I may
be wrong, but it seems that “@embed” is related to framings, so I only tried to
use frames.)
Using an example model:
private Model exampleModel() {
Model m = ModelFactory.createDefaultModel();
String ns = "http://xmlns.com/foaf/0.1/";
Resource person = m.createResource(ns + "Person");
Resource s = m.createResource("http://a.com/person1");
m.add(s, m.createProperty(ns + "name"), "Jane Doe");
m.add(s, m.createProperty(ns + "title"), "Professor");
m.add(s, RDF.type, person);
Resource s2 = m.createResource("http://a.com/person2");
m.add(s2, m.createProperty(ns + "name"), "Gado Salamatou");
m.add(s2, m.createProperty(ns + "homePage"), "http://www.salamatou.com");
m.add(s2, m.createProperty(ns + "title"), "Dr");
m.add(s2, RDF.type, person);
Property knows = m.createProperty(ns + "knows");
m.add(s,knows,s2);
s = m.createResource();
m.add(s, RDF.type, m.createResource(ns + "Organization"));
return m;
}
following code:
@Test public final void ftest1() {
Model m = exampleModel();
DatasetGraph g = DatasetFactory.wrap(m).asDatasetGraph();
JsonLDWriteContext ctx = new JsonLDWriteContext();
// no effect, it seems
// JsonLdOptions opts = new JsonLdOptions ();
//
// opts.setEmbed ( "@always" );
// ctx.setOptions ( opts );
Map<String, Object> frame = new HashMap<>();
ctx.setFrame(frame);
// only output Persons
frame.put("@type", "http://xmlns.com/foaf/0.1/Person");
write(g, RDFFormat.JSONLD_FRAME_PRETTY, ctx);
}
outputs the following:
{
"@graph" : [ {
"@id" : "http://a.com/person1",
"@type" : "http://xmlns.com/foaf/0.1/Person",
"http://xmlns.com/foaf/0.1/knows" : {
"@id" : "http://a.com/person2",
"@type" : "http://xmlns.com/foaf/0.1/Person",
"http://xmlns.com/foaf/0.1/homePage" : "http://www.salamatou.com",
"http://xmlns.com/foaf/0.1/name" : "Gado Salamatou",
"http://xmlns.com/foaf/0.1/title" : "Dr"
},
"http://xmlns.com/foaf/0.1/name" : "Jane Doe",
"http://xmlns.com/foaf/0.1/title" : "Professor"
}, {
"@id" : "http://a.com/person2",
"@type" : "http://xmlns.com/foaf/0.1/Person",
"http://xmlns.com/foaf/0.1/homePage" : "http://www.salamatou.com",
"http://xmlns.com/foaf/0.1/name" : "Gado Salamatou",
"http://xmlns.com/foaf/0.1/title" : "Dr"
} ]
}
So,
- we have “person2” embedded in “person1”, but it is repeated at the end
anyway. I don’t know why, but I would say that this is related to JsonLD-Java,
not jena.
- there is no “@context” (which is automatically computed by jena when using a
COMPACT or a FLATTEN output)
Regarding the “@context”, trying (for instance)
String atContextAsJson = "{\"name\":{\"@id\":\"http://xmlns.com/foaf/0.1/name\"},\"Person\":
{\"@id\": \"http://xmlns.com/foaf/0.1/Person\"}}";
ctx.setJsonLDContext(atContextAsJson);
(the way to set the @context for compact of flatten) doesn’t work.
The code of org.apache.jena.riot.writer.JsonLDWriter, doesn’t take the context
into account when it’s about frames. It could be changed (around line 210) to
} else if (variant.isFrame()) {
Object frame = null;
if (jenaContext != null) {
frame = jenaContext.get(JSONLD_FRAME);
}
if (frame == null) {
throw new IllegalArgumentException("No frame object found in jena
Context");
}
if (frame instanceof String) {
frame = JsonUtils.fromString((String) frame);
}
// THIS IS THE SUGGESTED CHANGE:
if (frame instanceof Map) {
Map map = (Map) frame;
if (map.get("@context") == null) {
// no @context in frame: add one (from jena context, or
create one -- should we create one ?)
Object ctx = getJsonldContext(dataset, prefixMap, jenaContext);
map.put("@context", ctx);
}
}
obj = JsonLdProcessor.frame(obj, frame, opts);
and the previous would then work. But I don’t know whether it is a good idea.
Another option is to pass the @context directly inside the frame, like this :
@Test public final void ftest2() {
Model m = exampleModel();
DatasetGraph g = DatasetFactory.wrap(m).asDatasetGraph();
JsonLDWriteContext ctx = new JsonLDWriteContext();
// no effect, it seems
// JsonLdOptions opts = new JsonLdOptions ();
//
// opts.setEmbed ( "@always" );
// ctx.setOptions ( opts );
Map<String, Object> frame = new HashMap<>();
ctx.setFrame(frame);
// only output Persons
frame.put("@type", "http://xmlns.com/foaf/0.1/Person");
String atContextAsJson = "{\"name\":{\"@id\":\"http://xmlns.com/foaf/0.1/name\"},\"Person\":
{\"@id\": \"http://xmlns.com/foaf/0.1/Person\"}}";
try {
frame.put("@context", JsonUtils.fromString(atContextAsJson));
} catch (Exception e) { throw new RuntimeException(e); }
write(g, RDFFormat.JSONLD_FRAME_PRETTY, ctx);
}
But I agree with you that it is annoying to pass the context, when we know that
jena is able to compute it. But the method that does it in
org.apache.jena.riot.writer.JsonLDWriter is not public. I would suggest to make
it public:
public static Map<String, Object> createJsonldContext(Graph g, PrefixMap
prefixMap, boolean addAllPrefixesToContext) {
you then could write:
@Test public final void ftest3() {
Model m = exampleModel();
DatasetGraph g = DatasetFactory.wrap(m).asDatasetGraph();
JsonLDWriteContext ctx = new JsonLDWriteContext();
// no effect, it seems
// JsonLdOptions opts = new JsonLdOptions ();
//
// opts.setEmbed ( "@always" );
// ctx.setOptions ( opts );
Map<String, Object> frame = new HashMap<>();
ctx.setFrame(frame);
// only output Persons
frame.put("@type", "http://xmlns.com/foaf/0.1/Person");
PrefixMap pm = RiotLib.prefixMap(g);
boolean addAllPrefixesToContext = true;
Map<String, Object> map =
JsonLDWriter.createJsonldContext(g.getDefaultGraph(), pm, addAllPrefixesToContext);
frame.put("@context", map);
write(g, RDFFormat.JSONLD_FRAME_PRETTY, ctx);
}
to get following output:
{
"@context" : {
"title" : "http://xmlns.com/foaf/0.1/title",
"homePage" : "http://xmlns.com/foaf/0.1/homePage",
"name" : "http://xmlns.com/foaf/0.1/name",
"knows" : {
"@id" : "http://xmlns.com/foaf/0.1/knows",
"@type" : "@id"
}
},
"@graph" : [ {
"@id" : "http://a.com/person1",
"@type" : "http://xmlns.com/foaf/0.1/Person",
"knows" : {
"@id" : "http://a.com/person2",
"@type" : "http://xmlns.com/foaf/0.1/Person",
"homePage" : "http://www.salamatou.com",
"name" : "Gado Salamatou",
"title" : "Dr"
},
"name" : "Jane Doe",
"title" : "Professor"
}, {
"@id" : "http://a.com/person2",
"@type" : "http://xmlns.com/foaf/0.1/Person",
"homePage" : "http://www.salamatou.com",
"name" : "Gado Salamatou",
"title" : "Dr"
} ]
}
fps
Le 24 août 2018 à 14:08, Zak Mc Kracken <[email protected]> a écrit :
Hi all,
Trying here in the mailing list too:
https://stackoverflow.com/questions/51966888/writing-embedding-json-ld-from-jena
In short, I want to get a JSON result from CONSTRUCT, which, in: person1
foaf:knows person2, puts the person2 object inside person1, as value of
'knows'. The current defaults set the person2's URI only and report the person2
object separately. JSON-LD and jsonld-java accept the @embed = @always option,
but only the framing processing seem to recognise it, which makes things more
complicated than I wish, since I don't need any framing, just always-embeded
output.
Thanks in advance for any help,
Marco.