You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2016/10/03 10:35:53 UTC

[21/41] jena git commit: JsonLDWriteContext

JsonLDWriteContext

Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/429d0b14
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/429d0b14
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/429d0b14

Branch: refs/heads/master
Commit: 429d0b14136df06b3d716b200cf8ec41ac0a1c61
Parents: 43be1cd
Author: Franc\u0327ois-Paul Servant <fp...@semanlink.net>
Authored: Tue Sep 20 16:04:37 2016 +0200
Committer: Franc\u0327ois-Paul Servant <fp...@semanlink.net>
Committed: Tue Sep 20 16:04:37 2016 +0200

----------------------------------------------------------------------
 .../apache/jena/riot/JsonLDWriteContext.java    |  79 +++++++
 .../org/apache/jena/riot/out/JsonLDWriter.java  | 143 ++++++++----
 .../jena/riot/writer/TestJsonLDWriter.java      | 220 +++++++++++++++----
 3 files changed, 356 insertions(+), 86 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/429d0b14/jena-arq/src/main/java/org/apache/jena/riot/JsonLDWriteContext.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/JsonLDWriteContext.java b/jena-arq/src/main/java/org/apache/jena/riot/JsonLDWriteContext.java
new file mode 100644
index 0000000..c5d447f
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/riot/JsonLDWriteContext.java
@@ -0,0 +1,79 @@
+/* Created on 31 ao�t 2016 */
+package org.apache.jena.riot;
+
+import org.apache.jena.riot.out.JsonLDWriter;
+import org.apache.jena.sparql.util.Context;
+
+import com.github.jsonldjava.core.JsonLdOptions;
+
+/**
+ * The set of parameters that can be used to control the writing of JSON-LD.
+ */
+public class JsonLDWriteContext extends Context {
+    /**
+     * Set the JSON-LD java API's options
+     * 
+     * If not set, a default value is used.
+     * 
+     * @param opts the options as defined by the JSON-LD java API
+     */
+    public void setOptions(JsonLdOptions opts) {     
+        set(JsonLDWriter.JSONLD_OPTIONS, opts);
+    }
+
+    /**
+     * Set the value of the JSON-LD "@context" node, used in "Compact" and "Flattened" JSON-LD outputs.
+     * 
+     * It is not required to set this parameter: if it is not set,
+     * a value for the "@Context" node is computed, based on the content of the dataset and its prefix mappings.
+     * 
+     * If an URI is passed, the JSONLD-code java will attempt to dereference it.
+     * 
+     * @param jsonLdContext the value of the "@context" node. Note that it is a Json Value: if passing an URI, the String must be quoted.
+     */
+    public void setJsonLdContext(String jsonLdContext) {
+        set(JsonLDWriter.JSONLD_CONTEXT, jsonLdContext);
+    }
+
+    //        /**
+    //         * Allow to replace the content of the "@context" node with a given value.
+    //         * 
+    //         * This is useful, for instance, to set the "@context" to an URI, without requiring to dereference the URI in question:
+    //         * the context used to compute the JSONLD output is the one normally used (as defined by a call to -
+    //         * or the lack of call to - setJsonLdContext) 
+    //         * 
+    //         * (only useful for "Compact" and "Flattened" JSON-LD outputs).
+    //         * 
+    //         * @param jsonLdContext the value of the "@context" node.
+    //         */
+    //        public void setJsonLdContextSubstitution(JsonString jsonLdContext) {
+    //            setJsonLdContextSubstitution(jsonLdContext.toString());       
+    //        }
+
+    /**
+     * Allow to replace the content of the "@context" node with a given value.
+     * 
+     * This is useful, for instance, to set the "@context" to an URI, without requiring to dereference the URI in question:
+     * the context used to compute the JSONLD output is the one normally used (as defined by a call to -
+     * or the lack of call to - setJsonLdContext) 
+     * 
+     * (only useful for "Compact" and "Flattened" JSON-LD outputs).
+     * 
+     * @param jsonLdContext the value of the "@context" node. Note that it is a Json Value: if passing an URI, the String must be quoted.
+     */
+    public void setJsonLdContextSubstitution(String jsonLdContext) {
+        set(JsonLDWriter.JSONLD_CONTEXT_SUBSTITUTION, jsonLdContext);       
+    }
+
+    //        public void setFrame(JsonObject frame) {
+    //            setFrame(frame.toString());
+    //        }
+
+    /**
+     * Set the frame used in a "Frame" output
+     * @param frame the Json Object used as frame for the "frame" output
+     */
+    public void setFrame(String frame) {
+        set(JsonLDWriter.JSONLD_FRAME, frame);
+    }           
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/429d0b14/jena-arq/src/main/java/org/apache/jena/riot/out/JsonLDWriter.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/out/JsonLDWriter.java b/jena-arq/src/main/java/org/apache/jena/riot/out/JsonLDWriter.java
index 919e308..ae9ac0f 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/out/JsonLDWriter.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/out/JsonLDWriter.java
@@ -33,11 +33,11 @@ import java.util.function.Consumer;
 import org.apache.jena.atlas.io.IO ;
 import org.apache.jena.atlas.iterator.Iter ;
 import org.apache.jena.atlas.lib.Chars ;
+import org.apache.jena.atlas.lib.Pair;
 import org.apache.jena.graph.Graph ;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.Triple ;
 import org.apache.jena.iri.IRI ;
-import org.apache.jena.rdf.model.Model;
 import org.apache.jena.riot.Lang ;
 import org.apache.jena.riot.RDFFormat ;
 import org.apache.jena.riot.RiotException ;
@@ -69,7 +69,6 @@ import com.github.jsonldjava.utils.JsonUtils ;
  * For formats using a context ("@context" node), (compact and expand), this automatically generates a default one.
  * One can pass a jsonld context using the (jena) Context mechanism, defining a (jena) Context
  * (sorry for this clash of contexts), (cf. last argument in
- * {@link org.apache.jena.riot.RDFDataMgr#write(OutputStream out, Model model, RDFFormat serialization, Context ctx)})
  * {@link java.io.OutputStream.WriterDatasetRIOT.write(OutputStream out, DatasetGraph datasetGraph, PrefixMap prefixMap, String baseURI, Context context)})
  * with:
  * <pre>
@@ -113,6 +112,10 @@ public class JsonLDWriter extends WriterDatasetRIOTBase
     public static final Symbol JSONLD_FRAME = createSymbol("JSONLD_FRAME");
     /** value: the option object expected by JsonLdProcessor (instance of JsonLdOptions) */
     public static final Symbol JSONLD_OPTIONS = createSymbol("JSONLD_OPTIONS");
+    /** 
+     * if creating a (jsonld) context from dataset, should we include all the prefixes defined in graph's prefix mappings
+     * value: a Boolean (default : true) */
+    public static final Symbol JSONLD_ADD_ALL_PREFIXES_TO_CONTEXT = createSymbol("JSONLD_ADD_ALL_PREFIXES_TO_CONTEXT");
 
     private final RDFFormat format ;
 
@@ -140,34 +143,43 @@ public class JsonLDWriter extends WriterDatasetRIOTBase
     private RDFFormat.JSONLDVariant getVariant() {
         return (RDFFormat.JSONLDVariant) format.getVariant();
     }
-
+    
     private JsonLdOptions getJsonLdOptions(String baseURI, Context jenaContext) {
         JsonLdOptions opts = null;
         if (jenaContext != null) {
             opts = (JsonLdOptions) jenaContext.get(JSONLD_OPTIONS);
         }
         if (opts == null) {
-            opts = new JsonLdOptions(baseURI);
-            // maybe we should have used the same defaults as jsonld-java. Too late now
-            opts.useNamespaces = true ;
-            //opts.setUseRdfType(true); // false -> use "@type"
-            opts.setUseNativeTypes(true);
-            opts.setCompactArrays(true);	  			
+            opts = defaultJsonLdOptions(baseURI);
         } 
         return opts;
     }
+    
+    // jena is not using same default as JSONLD-java
+    // maybe we should have, but it's too late now:
+    // changing now would imply some unexpected changes in current users' outputs
+    static private JsonLdOptions defaultJsonLdOptions(String baseURI) {
+        JsonLdOptions opts = new JsonLdOptions(baseURI);
+        // maybe we should have used the same defaults as jsonld-java. Too late now
+        opts.useNamespaces = true ; // this is NOT jsonld-java's default
+        // opts.setUseRdfType(true); // false -> use "@type"
+        opts.setUseNativeTypes(true); // this is NOT jsonld-java's default
+        opts.setCompactArrays(true); // this is jsonld-java's default           
+        return opts;
+    }
 
-    @SuppressWarnings("deprecation") // JsonLdApi.fromRDF(RDFDataset, boolean) is "experimental" rather than "deprecated", cf. https://github.com/jsonld-java/jsonld-java/pull/173
     private void serialize(Writer writer, DatasetGraph dataset, PrefixMap prefixMap, String baseURI, Context jenaContext) {
         try {
             JsonLdOptions opts = getJsonLdOptions(baseURI, jenaContext) ;
 
             // we can benefit from the fact we know that there are no duplicates in the jsonld RDFDataset that we create
-            // cf. cf. https://github.com/jsonld-java/jsonld-java/pull/173
+            // (optimization in jsonld-java 0.8.3)
+            // see https://github.com/jsonld-java/jsonld-java/pull/173
             
             // with this, we cannot call the json-ld fromRDF method that assumes no duplicates in RDFDataset
             // Object obj = JsonLdProcessor.fromRDF(dataset, opts, new JenaRDF2JSONLD()) ;
             final RDFDataset jsonldDataset = (new JenaRDF2JSONLD()).parse(dataset);
+            @SuppressWarnings("deprecation") // JsonLdApi.fromRDF(RDFDataset, boolean) is "experimental" rather than "deprecated"
             Object obj = (new JsonLdApi(opts)).fromRDF(jsonldDataset, true); // true because we know that we don't have any duplicate in jsonldDataset
 
             RDFFormat.JSONLDVariant variant = getVariant();
@@ -184,7 +196,7 @@ public class JsonLDWriter extends WriterDatasetRIOTBase
                 obj = JsonLdProcessor.frame(obj, frame, opts);
 
             } else { // compact or flatten
-                // we need a (jsonld) context. Get it from jenaContext, or make one:
+                // we need a (jsonld) context. Get it from jenaContext, or create one:
                 Object ctx = getJsonldContext(dataset, prefixMap, jenaContext);
 
                 if (variant.isCompact()) {
@@ -204,7 +216,7 @@ public class JsonLDWriter extends WriterDatasetRIOTBase
                         if (obj instanceof Map) {
                             Map map = (Map) obj;
                             if (map.containsKey("@context")) {
-                                map.put("@context", JsonUtils.fromString((String) ctxReplacement));
+                                map.put("@context", JsonUtils.fromString(ctxReplacement.toString()));
                             }
                         }
                     }
@@ -244,9 +256,9 @@ public class JsonLDWriter extends WriterDatasetRIOTBase
                     // (should not be useful)
                     if (o instanceof String) {
                         String jsonString = (String) o;
-                        if (jsonString != null) ctx = JsonUtils.fromString(jsonString);     	  						
+                        if (jsonString != null) ctx = JsonUtils.fromString(jsonString);
                     } else {
-                        Logger.getLogger(JsonLDWriter.class).warn("JSONLD_CONTEXT value is not a String. Assuming a context object expected by JSON-LD JsonLdProcessor.compact or flatten");
+                        Logger.getLogger(JsonLDWriter.class).warn("JSONLD_CONTEXT value is not a String. Assuming the context object expected by JSON-LD JsonLdProcessor.compact or flatten");
                         ctx = o;
                     }
                 }
@@ -255,56 +267,46 @@ public class JsonLDWriter extends WriterDatasetRIOTBase
 
         if (!isCtxDefined) {
             // if no ctx passed via jenaContext, create one in order to have localnames as keys for properties
-            ctx = createJsonldContext(dataset.getDefaultGraph(), prefixMap) ;
+            ctx = createJsonldContext(dataset.getDefaultGraph(), prefixMap, addAllPrefixesToContextFlag(jenaContext)) ;
 
             // I don't think this should be done: the JsonLdProcessor begins
             // by looking whether the argument passed is a map with key "@context" and if so, takes corresponding value
             // Then, better not to do this: we create a map for nothing, and worse,
             // if the context object has been created by a user and passed through the (jena) context
-            // in case he got the same idea, we would end up with 2 levels of maps an it would not work
+            // in case he got the same idea, we would end up with 2 levels of maps and it would not work
             //        Map<String, Object> localCtx = new HashMap<>() ;
             //        localCtx.put("@context", ctx) ;
         }
         return ctx;
     }
-
+    
     // useful to help people who want to create their own context?
     // It is used in TestJsonLDWriter (marginally) (TestJsonLDWriter which happens to be in another package,
     // so either I remove the test in question, or this has to be public)
     public static Object createJsonldContext(Graph g) {
-        return createJsonldContext(g, PrefixMapFactory.create(g.getPrefixMapping()));
+        return createJsonldContext(g, PrefixMapFactory.create(g.getPrefixMapping()), true);
     }
 
-    private static Object createJsonldContext(Graph g, PrefixMap prefixMap) {
+    private static Object createJsonldContext(Graph g, PrefixMap prefixMap, boolean addAllPrefixesToContext) {
         final Map<String, Object> ctx = new LinkedHashMap<>() ;
-        addProperties(ctx, g) ;
-        addPrefixes(ctx, prefixMap) ;	
-        return ctx ;
-    }
+        
+        // Add properties (in order to get: "localname": ....)
+        addProperties(ctx, g);
 
-    private static void addPrefixes(Map<String, Object> ctx, PrefixMap prefixMap) {
-        if (prefixMap != null) {
-            Map<String, IRI> pmap = prefixMap.getMapping() ;
-            for ( Entry<String, IRI> e : pmap.entrySet() ) {
-                String key = e.getKey() ;
-                if ( key.isEmpty() ) {
-                    // Prefix "" is not allowed in JSON-LD
-                    // we could replace "" with "@vocab"
-                    // key = "@vocab" ;
-                    continue;
-                }
-                ctx.put(key, e.getValue().toString()) ;
-            }
-        }
+        // Add prefixes
+        addPrefixes(ctx, g, prefixMap, addAllPrefixesToContext);
+
+        return ctx ;
     }
 
-    private static void addProperties(final Map<String, Object> ctx, Graph graph) {
-        // Add some properties directly so it becomes "localname": ....
+    /** Add properties to jsonld context. */
+    static void addProperties(Map<String, Object> ctx, Graph g) {
         Consumer<Triple> x = new Consumer<Triple>() {
             @Override
             public void accept(Triple item) {
                 Node p = item.getPredicate() ;
                 Node o = item.getObject() ;
+                
                 if ( p.equals(RDF.type.asNode()) )
                     return ;
                 String x = p.getLocalName() ;
@@ -335,7 +337,66 @@ public class JsonLDWriter extends WriterDatasetRIOTBase
                 }
             }
         } ;
+        Iter.iter(g.find(null, null, null)).apply(x) ;
+    }
+    /** Add prefixes to jsonld context */
+    // if adding all the prefixes in PrefixMap to ctx
+    // one pb is, many of the prefixes may be actually unused in the graph.
+    // This happens for instance when downloading schema.org: a very long list of prefixes
+    // hence the addAllPrefixesToContext param
+    private static void addPrefixes(Map<String, Object> ctx, Graph g, PrefixMap prefixMap, boolean addAllPrefixesToContext) {
+        if (prefixMap != null) {
+            Map<String, IRI> mapping = prefixMap.getMapping();
+            if (addAllPrefixesToContext) {
+                for ( Entry<String, IRI> e : mapping.entrySet() ) {
+                    addOnePrefix(ctx, e.getKey(), e.getValue().toString());
+                 }
+            } else {
+                // only add those that are actually used
+                Consumer<Triple> x = new Consumer<Triple>() {
+                    @Override
+                    public void accept(Triple item) {
+                        Node node = item.getSubject();
+                        if (node.isURI()) addPrefix2Ctx(node.getURI());
+                        node = item.getPredicate() ;
+                        addPrefix2Ctx(node.getURI());
+                        node = item.getObject() ;
+                        if (node.isURI()) addPrefix2Ctx(node.getURI());
+                    }
 
-        Iter.iter(graph.find(null, null, null)).apply(x) ;
+                    private void addPrefix2Ctx(String resUri) {
+                        Pair<String, String> pair = prefixMap.abbrev(resUri);
+                        if (pair != null) {
+                            String prefix = pair.getLeft();
+                            addOnePrefix(ctx, prefix, mapping.get(prefix).toString());
+                        }        
+                    }
+                } ;
+                Iter.iter(g.find(null, null, null)).apply(x) ;
+            }
+        }
+    }
+    
+    /** Add one prefix to jsonld context */
+    static void addOnePrefix(Map<String, Object> ctx, String prefix, String value) {
+        if (!prefix.isEmpty()) { // Prefix "" is not allowed in JSON-LD -- could probably be replaced by "@vocab"
+            ctx.put(prefix, value);
+        }        
     }
+    
+    private static boolean addAllPrefixesToContextFlag(Context jenaContext) {
+        if (jenaContext != null) {
+            Object o = jenaContext.get(JSONLD_ADD_ALL_PREFIXES_TO_CONTEXT);
+            if (o != null) {
+                if (o instanceof Boolean) {
+                    return ((Boolean) o).booleanValue();
+                } else {
+                    throw new IllegalArgumentException("Value attached to JSONLD_ADD_ALL_PREFIXES_TO_CONTEXT shoud be a Boolean");
+                }
+            }
+        }
+        // default
+        return true;
+    }
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/429d0b14/jena-arq/src/test/java/org/apache/jena/riot/writer/TestJsonLDWriter.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/riot/writer/TestJsonLDWriter.java b/jena-arq/src/test/java/org/apache/jena/riot/writer/TestJsonLDWriter.java
index 6241248..10f69af 100644
--- a/jena-arq/src/test/java/org/apache/jena/riot/writer/TestJsonLDWriter.java
+++ b/jena-arq/src/test/java/org/apache/jena/riot/writer/TestJsonLDWriter.java
@@ -20,8 +20,13 @@ package org.apache.jena.riot.writer;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
 
+import org.apache.jena.atlas.json.JSON;
 import org.apache.jena.atlas.json.JsonObject;
+import org.apache.jena.atlas.json.JsonString;
+import org.apache.jena.atlas.json.JsonValue;
 import org.apache.jena.atlas.junit.BaseTest;
 import org.apache.jena.query.DatasetFactory;
 import org.apache.jena.rdf.model.Model;
@@ -29,6 +34,7 @@ import org.apache.jena.rdf.model.ModelFactory;
 import org.apache.jena.rdf.model.Property;
 import org.apache.jena.rdf.model.RDFNode;
 import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.riot.JsonLDWriteContext;
 import org.apache.jena.riot.RDFDataMgr;
 import org.apache.jena.riot.RDFFormat;
 import org.apache.jena.riot.WriterDatasetRIOT;
@@ -42,7 +48,9 @@ import org.apache.log4j.Logger;
 import org.junit.Test;
 
 import com.fasterxml.jackson.core.JsonParseException;
+import com.github.jsonldjava.core.JsonLdError;
 import com.github.jsonldjava.core.JsonLdOptions;
+import com.github.jsonldjava.core.JsonLdProcessor;
 import com.github.jsonldjava.utils.JsonUtils;
 
 public class TestJsonLDWriter extends BaseTest {
@@ -129,22 +137,21 @@ public class TestJsonLDWriter extends BaseTest {
         }
     }
 
-    //@Test public final void supportEmptyNs() {
-    //    Model m = ModelFactory.createDefaultModel();
-    //    String ns = "http://www.a.com/foo/";
-    //    Resource s = m.createResource(ns + "s");
-    //    Property p = m.createProperty(ns + "p");
-    //    Resource o = m.createResource(ns + "o");
-    //    m.add(s,p,o);
-    //    m.add(m.createResource(ns + "s2"),p,m.createResource(ns + "o2"));
-    //    m.setNsPrefix("ns", ns);
-    //    Model m2 = parse(toJsonLDString(m));
-    //    assertTrue(m2.isIsomorphicWith(m));
-    //    
-    //    // RDFDataMgr.write(DevNull.out, m, RDFFormat.JSONLD) ;
-    //    RDFDataMgr.write(System.out, m2, RDFFormat.TURTLE);
-    //    RDFDataMgr.write(System.out, m, RDFFormat.JSONLD);
-    //}
+    /**
+     * an output with something like:<pre>
+     *  "@context" : {
+     *      "" : "http://www.a.com/foo/",
+     * <pre> is incorrect
+     */
+    @Test public final void noEmptyPrefixInContext() {
+        Model m = simpleModel();
+        m.setNsPrefix("", "http://www.a.com/foo/");
+        String jsonld = toString(m, RDFFormat.JSONLD_COMPACT_PRETTY, null);
+        assertTrue(jsonld.indexOf("\"\"") < 0);
+        Model m2 = parse(jsonld);
+        assertTrue(m2.isIsomorphicWith(m));
+        System.out.println(jsonld);
+    }
 
     /** verify that one may pass a context as a JSON string, and that it is actually used in the output */
     @Test public void testSettingContextAsJsonString() {
@@ -165,28 +172,25 @@ public class TestJsonLDWriter extends BaseTest {
         String s1 = toString(m, RDFFormat.JSONLD_COMPACT_FLAT, null);
         // there's a prefix in m, and we find it in the output
         String prefixStringInResult = "\"ex\":\"" + url + "\"";
-        assertTrue(s1.indexOf(prefixStringInResult) > 0);
+        assertTrue(s1.indexOf(prefixStringInResult) > -1);
         Model m1 = parse(s1);
 
-        // must we pass the json object associated to "@context",
-        // or its parent node (that is, with the "@context") ?
-        // Actually, we can do both (JSONLD-java's code ensure it)
-        // We check it for here
-
         // this is json object associated to "@context" in s1
         // it includes the "ex" prefix
 
         String js = "{\"p\":{\"@id\":\"http://www.semanlink.net/test/p\",\"@type\":\"@id\"},\"ex\":\"http://www.semanlink.net/test/\"}";
 
         // remove the prefix from m
-        m.removeNsPrefix("ex");    // RDFDataMgr.write(System.out, m, RDFFormat.JSONLD) ;
+        m.removeNsPrefix("ex");
         String s2 = toString(m, RDFFormat.JSONLD_COMPACT_PRETTY, null);
         // model wo prefix -> no more prefix string in result:
         assertTrue(s2.indexOf(prefixStringInResult) < 0);
 
-        // the model wo prefix, outputed as jsonld using a context that defines the prefix    
-        Context jenaCtx = new Context();
-        jenaCtx.set(JsonLDWriter.JSONLD_CONTEXT, js);
+        // the model wo prefix, output as jsonld using a context that defines the prefix    
+        JsonLDWriteContext jenaCtx = new JsonLDWriteContext();
+        jenaCtx.setJsonLdContext(js);
+        
+        
         String s3 = toString(m, RDFFormat.JSONLD_COMPACT_FLAT, jenaCtx);
 
         assertTrue(s3.length() == s1.length());
@@ -197,7 +201,8 @@ public class TestJsonLDWriter extends BaseTest {
 
         // same thing, but passing also the "@context"
         js = "{\"@context\":" + js + "}";
-        jenaCtx.set(JsonLDWriter.JSONLD_CONTEXT, js);
+        jenaCtx.setJsonLdContext(js);
+        
         String s4 = toString(m, RDFFormat.JSONLD_COMPACT_FLAT, jenaCtx);
 
         assertTrue(s4.length() == s1.length());
@@ -205,8 +210,30 @@ public class TestJsonLDWriter extends BaseTest {
         Model m4 = parse(s4);
         assertTrue(m4.isIsomorphicWith(m));
         assertTrue(m4.isIsomorphicWith(m1));
+        
+        // same thing, but passing a JsonObject //////////////////////////////////////////////////////
+        JsonObject obj = new JsonObject();  
+        obj.put("@id", "http://www.semanlink.net/test/p");
+        obj.put("@type", "@id");
+        JsonObject json = new JsonObject();
+        json.put("p", obj);
+        json.put("ex", "http://www.semanlink.net/test/");
+        
+        jenaCtx.setJsonLdContext(json.toString());
+        String s5 = toString(m, RDFFormat.JSONLD_COMPACT_FLAT, jenaCtx);
+
+        assertTrue(s5.length() == s1.length());
+        assertTrue(s5.indexOf(prefixStringInResult) > 0);
+        Model m5 = parse(s5);
+        assertTrue(m5.isIsomorphicWith(m));
+        assertTrue(m5.isIsomorphicWith(m1));
+      
     }
 
+    
+// KO
+    
+    
     /**
      * Checks that one can pass a context defined by its URI
      */
@@ -217,19 +244,27 @@ public class TestJsonLDWriter extends BaseTest {
         m.add(s, m.createProperty(ns + "name"), "Jane Doe");
         m.add(s, m.createProperty(ns + "url"), "http://www.janedoe.com");
         m.add(s, RDF.type, "Person");
-
-        // we can pass a uri in the context, as a quoted string (it is a JSON string)
+        
+        // we can pass an uri in the context, as a quoted string (it is a JSON string)
         Context jenaContext = new Context();
         try {
-            // jenaContext.set(JsonLDWriter.JSONLD_CONTEXT, "http://schema.org/");
-            // beware, it must be quoted, as it is supposed to be a json string
-            jenaContext.set(JsonLDWriter.JSONLD_CONTEXT, "\"http://schema.org/\"");
+            jenaContext.set(JsonLDWriter.JSONLD_CONTEXT, "{\"@context\" : \"http://schema.org/\"}");
             String jsonld = toString(m, RDFFormat.JSONLD, jenaContext);
+            System.out.println("----------");
+            System.out.println(jsonld);
+            // check it parses ok
+            Model m2 = parse(jsonld);
+            
+            System.out.println("----------");
+            m2.write(System.out, "TURTLE");
+            assertTrue(m2.isIsomorphicWith(m));
+            
         } catch (Throwable e) {
             // maybe test run in a setting wo external connectivity - not a real problem
             String mess = e.getMessage();
             if ((mess != null) && (mess.indexOf("loading remote context failed") > -1)) {
-                Logger.getLogger(getClass()).info("Attempt to load remote context failed - no drama");
+                Logger.getLogger(getClass()).info(mess);
+                e.printStackTrace();
             } else {
                 throw e;
             }
@@ -305,15 +340,110 @@ public class TestJsonLDWriter extends BaseTest {
         m.add(s, m.createProperty(ns + "jobTitle"), "Professor");
         m.add(s, RDF.type, person);
 
-        Context jenaCtx = new Context();
-        jenaCtx.set(JsonLDWriter.JSONLD_CONTEXT_SUBSTITUTION, "\"" + ns + "\"");
-
-        String jsonld = toString(m, RDFFormat.JSONLD_COMPACT_FLAT, jenaCtx);
+        // change @context to a URI
+        
+        JsonLDWriteContext jenaCtx = new JsonLDWriteContext();
+        jenaCtx.setJsonLdContextSubstitution((new JsonString(ns)).toString());
+        String jsonld;
+        jsonld = toString(m, RDFFormat.JSONLD_COMPACT_FLAT, jenaCtx);
         String c = "\"@context\":\"http://schema.org/\"";
         assertTrue(jsonld.indexOf(c) > -1);
+
+         // change @context to a given ctx
+
+        String ctx = "{\"jobTitle\":{\"@id\":\"http://ex.com/jobTitle\"},\"url\":{\"@id\":\"http://ex.com/url\"},\"name\":{\"@id\":\"http://ex.com/name\"}}";
+        jenaCtx.setJsonLdContextSubstitution(ctx);
+        jsonld = toString(m, RDFFormat.JSONLD_COMPACT_FLAT, jenaCtx);
+        assertTrue(jsonld.indexOf("http://ex.com/name") > -1);
     }
+    
+    @Test public void readingContextDefinedByUri() {
+        String jsonld = "{\"@id\":\"_:b0\",\"@type\":\"http://schema.org/Person\",\"jobTitle\":\"Professor\",\"name\":\"Jane Doe\",\"url\":\"http://www.janedoe.com\",\"@context\":\"http://schema.org/\"}";
+        Model m = parse(jsonld);
+        
+        System.out.println("-- TURTLE -------------");
+        m.write(System.out, "TURTLE");
+        
+        jsonld = toString(m, RDFFormat.JSONLD_EXPAND_PRETTY, null);
+        System.out.println("-- JSONLD_EXPAND_PRETTY -------------");
+        System.out.println(jsonld);
+        
+        jsonld = toString(m, RDFFormat.JSONLD_COMPACT_PRETTY, null);
+        System.out.println("-- JSONLD_COMPACT_PRETTY -------------");
+        System.out.println(jsonld);
+        
+        JsonLDWriteContext jenaCtx = new JsonLDWriteContext();
+        Map mapCtx = new HashMap();
+        mapCtx.put("@context", "http://schema.org/");
+        jenaCtx.set(JsonLDWriter.JSONLD_CONTEXT,mapCtx);
+        jsonld = toString(m, RDFFormat.JSONLD_COMPACT_PRETTY, jenaCtx);
+        System.out.println("-- JSONLD_COMPACT_PRETTY WITH URI BASED CONTEXT (?) -------------");
+        System.out.println(jsonld);
+        
+    }
+ 
+    @Test public void foo1() throws JsonParseException, IOException, JsonLdError {
+        // String jsonld = "{\"@id\":\"_:b0\",\"@type\":\"http://schema.org/Person\",\"jobTitle\":\"Professor\",\"name\":\"Jane Doe\",\"url\":\"http://www.janedoe.com\",\"@context\":\"http://schema.org/\"}";
+        String jsonld = "{\"@id\":\"_:b0\",\"jobTitle\":\"Professor\",\"name\":\"Jane Doe\",\"@context\":{\"jobTitle\":{\"@id\":\"http://schema.org/jobTitle\"},\"name\":{\"@id\":\"http://schema.org/name\"}}}";
+        Model m = parse(jsonld);
+        m.write(System.out, "TURTLE");
+        
+    }
+    
+    private String simpleContextJsonld() {
+        return "{\"@id\":\"_:b0\",\"jobTitle\":\"Professor\",\"name\":\"Jane Doe\",\"@context\":{\"jobTitle\":{\"@id\":\"http://schema.org/jobTitle\"},\"name\":{\"@id\":\"http://schema.org/name\"}}}";
+    }
+    
+    @Test public void test1() throws JsonParseException, IOException, JsonLdError {
+        String jsonld = simpleContextJsonld();
+        
+        Object jsonObject = JsonUtils.fromString(jsonld);
+        
+        JsonLdOptions options = new JsonLdOptions();
 
+        Object expand = JsonLdProcessor.expand(jsonObject, options);
+        //Print out the result (or don't, it's your call!)
+        
+        System.out.println("-- expand: ");
+        System.out.println(JsonUtils.toPrettyString(expand));
+    }
 
+    
+    @Test public void foo() throws JsonParseException, IOException, JsonLdError {
+        // String jsonld = "{\"@id\":\"_:b0\",\"@type\":\"http://schema.org/Person\",\"jobTitle\":\"Professor\",\"name\":\"Jane Doe\",\"url\":\"http://www.janedoe.com\",\"@context\":\"http://schema.org/\"}";
+        String jsonld = "{\"@id\":\"_:b0\",\"jobTitle\":\"Professor\",\"name\":\"Jane Doe\",\"@context\":{\"jobTitle\":{\"@id\":\"http://schema.org/jobTitle\"},\"name\":{\"@id\":\"http://schema.org/name\"}}}";
+        System.out.println(jsonld);
+        
+        Map jsonObject = (Map) JsonUtils.fromString(jsonld);
+        Map context = new HashMap();
+        // context.put("@context", "http://schema.org/");
+//        context.put("@context", jsonObject.get("@context"));
+        JsonLdOptions options = new JsonLdOptions();
+        // options.setExpandContext(context);
+     // Customise options...
+     // Call whichever JSONLD function you want! (e.g. compact)
+Object expand = JsonLdProcessor.expand(jsonObject, options);
+System.out.println(JsonUtils.toPrettyString(expand));
+     Object compact = JsonLdProcessor.compact(jsonObject, context, options);
+     // Print out the result (or don't, it's your call!)
+     System.out.println(JsonUtils.toPrettyString(compact));
+     
+     
+//         Model m = ModelFactory.createDefaultModel();
+//         String ns = "http://schema.org/";
+//         // Resource person = m.createResource(ns + "Person");
+//         Resource s = m.createResource();
+//         m.add(s, m.createProperty(ns + "name"), "Jane Doe");
+//         m.add(s, m.createProperty(ns + "jobTitle"), "Professor");
+//         // m.add(s, RDF.type, person);
+//         s = m.createResource();
+//         // m.setNsPrefix("sh", ns);
+//         jsonld = toString(m, RDFFormat.JSONLD_COMPACT_FLAT, null);
+//         System.out.println(jsonld);
+    }
+    
+    
+        
     /**
      * Checking frames
      */
@@ -342,6 +472,7 @@ public class TestJsonLDWriter extends BaseTest {
         frame.put("@type", ns +"Person");
         jenaCtx.set(JsonLDWriter.JSONLD_FRAME, JsonUtils.fromString(frame.toString()));
         String jsonld = toString(m, RDFFormat.JSONLD_FRAME_PRETTY, jenaCtx);
+
         Model m2 = parse(jsonld);
         // 2 subjects with a type in m2
         assertTrue(m2.listStatements((Resource) null, RDF.type, (RDFNode) null).toList().size() == 2);
@@ -439,18 +570,20 @@ public class TestJsonLDWriter extends BaseTest {
         // compactArrays is true -> string, not an array for props with one value
         assertTrue(jsonld.indexOf("\"jobTitle\" : \"Professor\"") > -1);
         
-        // now output using a value for JsonLdOptions in Context that set compactArrays to false
+        // now output using a value for JsonLdOptions in Context that sets compactArrays to false
+        
+        JsonLDWriteContext jenaCtx = new JsonLDWriteContext();
         
         JsonLdOptions opts = new JsonLdOptions(null);
         opts.setCompactArrays(false);       
-        Context jenaCtx = new Context();
-        jenaCtx.set(JsonLDWriter.JSONLD_OPTIONS, opts);
+ 
+        jenaCtx.setOptions(opts);
 
         jsonld = toString(m, RDFFormat.JSONLD, jenaCtx);
 
         // compactArrays is false -> a "@graph" node
         assertTrue(jsonld.indexOf("@graph") > -1);
-        // compactArrays is false -> an array for all props, when when there's only one value
+        // compactArrays is false -> an array for all props, even when there's only one value
         assertTrue(jsonld.indexOf("\"jobTitle\" : [ \"Professor\" ]") > -1);
     }
     
@@ -461,15 +594,12 @@ public class TestJsonLDWriter extends BaseTest {
     private String toString(Model m, RDFFormat f, Context jenaContext) {
         try {
             ByteArrayOutputStream out = new ByteArrayOutputStream();
-            // RDFDataMgr.write(out, m, f, jenaContext) ;
-
 
             WriterDatasetRIOT w = RDFDataMgr.createDatasetWriter(f) ;
             DatasetGraph g = DatasetFactory.create(m).asDatasetGraph();
             PrefixMap pm = RiotLib.prefixMap(g);
             String base = null;
-            w.write(out,  g, pm, base, jenaContext) ;
-
+            w.write(out, g, pm, base, jenaContext) ;
 
             out.flush();
             String x = out.toString("UTF-8");