You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by ki...@apache.org on 2018/04/27 11:08:16 UTC

[5/5] jena git commit: JENA-632: Generate JSON from SPARQL directly

JENA-632: Generate JSON from SPARQL directly

This closes #114


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

Branch: refs/heads/master
Commit: 4bf5e3c0ad18d61542ce9e37804e8ec727283d96
Parents: e5cbf13
Author: Bruno P. Kinoshita <br...@yahoo.com.br>
Authored: Sun Dec 27 23:51:24 2015 +1300
Committer: Bruno P. Kinoshita <ki...@users.noreply.github.com>
Committed: Fri Apr 27 23:02:18 2018 +1200

----------------------------------------------------------------------
 jena-arq/Grammar/arq.jj                         |   41 +
 jena-arq/Grammar/master.jj                      |   46 +-
 jena-arq/Grammar/tokens.txt                     |    1 +
 .../main/java/org/apache/jena/query/Query.java  |   18 +
 .../org/apache/jena/query/QueryExecution.java   |   10 +-
 .../org/apache/jena/query/QueryVisitor.java     |    3 +-
 .../apache/jena/query/ResultSetFormatter.java   |   37 +-
 .../apache/jena/sparql/core/QueryCompare.java   |    5 +
 .../apache/jena/sparql/core/QueryHashCode.java  |    4 +
 .../apache/jena/sparql/engine/JsonIterator.java |  100 +
 .../jena/sparql/engine/QueryExecutionBase.java  |   45 +-
 .../sparql/engine/http/QueryEngineHTTP.java     |   15 +
 .../apache/jena/sparql/lang/arq/ARQParser.java  | 1192 ++++++------
 .../sparql/lang/arq/ARQParserConstants.java     |  379 ++--
 .../sparql/lang/arq/ARQParserTokenManager.java  | 1738 +++++++++---------
 .../apache/jena/sparql/lib/RDFTerm2Json.java    |    2 +-
 .../jena/sparql/resultset/SPARQLResult.java     |   30 +
 .../jena/sparql/serializer/QuerySerializer.java |   28 +-
 .../syntaxtransform/QueryTransformOps.java      |    5 +
 .../jena/query/TS_ResultSetFormatter.java       |   30 +
 .../jena/query/TestResultSetFormatter.java      |   84 +
 .../org/apache/jena/sparql/api/TestAPI.java     |   87 +-
 .../apache/jena/sparql/engine/TS_Engine.java    |    2 +
 .../apache/jena/sparql/engine/TestJsonEval.java |   77 +
 .../jena/sparql/engine/TestJsonIterator.java    |   86 +
 .../jena/fuseki/servlets/ResponseJson.java      |  150 ++
 .../jena/fuseki/servlets/SPARQL_Query.java      |   11 +
 .../java/org/apache/jena/fuseki/TestQuery.java  |   44 +
 28 files changed, 2662 insertions(+), 1608 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/4bf5e3c0/jena-arq/Grammar/arq.jj
----------------------------------------------------------------------
diff --git a/jena-arq/Grammar/arq.jj b/jena-arq/Grammar/arq.jj
index 5169735..d43d40c 100644
--- a/jena-arq/Grammar/arq.jj
+++ b/jena-arq/Grammar/arq.jj
@@ -49,6 +49,7 @@ void Query() : { }
 {
   Prologue()
   ( SelectQuery() | ConstructQuery() | DescribeQuery() | AskQuery()
+    | JsonQuery()
   )
   ValuesClause()
 }
@@ -176,6 +177,34 @@ void AskQuery() : {}
   WhereClause()
   SolutionModifier()
 }
+void JsonQuery() : {}
+{
+  JsonClause()
+  ( DatasetClause() )*
+  WhereClause()
+  SolutionModifier()
+}
+void JsonClause() : {}
+{
+  <JSON> { getQuery().setQueryJsonType() ; }
+  <LBRACE>
+  JsonObjectMember() ( <COMMA> JsonObjectMember() ) *
+  <RBRACE>
+}
+void JsonObjectMember() : { Node o ; String s ; Token t; }
+{
+  s = String()
+  t = < PNAME_NS > {
+    if ( ! t.image.equals(":") )
+      throwParseException("Prefix name expression not legal at this point : "+t.image, t.beginLine, t.beginColumn) ;
+  }
+  (
+    o = Var() { getQuery().addResultVar((Var)o) ; getQuery().addJsonMapping(s, o) ; }
+  | o = RDFLiteral() { getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s, o) ; }
+  | o = NumericLiteral() { getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s, o) ; }
+  | o = BooleanLiteral() { getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s, o) ; }
+  )
+}
 void DatasetClause() : {}
 {
   <FROM>
@@ -1563,6 +1592,17 @@ String String() : { Token t ; String lex ; }
       return lex ;
     }
 }
+Number Number() : { Token t ; Number number ; }
+{
+  (
+    t = < INTEGER > { number = integerValue(t.image) ; }
+  | t = < DECIMAL > { number = doubleValue(t.image) ; }
+  | t = < DOUBLE > { number = doubleValue(t.image) ; }
+  )
+  {
+    return number ;
+  }
+}
 String iri() : { String iri ; }
 {
   iri = IRIREF() { return iri ; }
@@ -1621,6 +1661,7 @@ TOKEN [IGNORE_CASE] :
 | < SELECT: "select" >
 | < DISTINCT: "distinct" >
 | < REDUCED: "reduced" >
+| < JSON: "json" >
 | < DESCRIBE: "describe" >
 | < CONSTRUCT: "construct" >
 | < ASK: "ask" >

http://git-wip-us.apache.org/repos/asf/jena/blob/4bf5e3c0/jena-arq/Grammar/master.jj
----------------------------------------------------------------------
diff --git a/jena-arq/Grammar/master.jj b/jena-arq/Grammar/master.jj
index effbe1b..5c7af35 100644
--- a/jena-arq/Grammar/master.jj
+++ b/jena-arq/Grammar/master.jj
@@ -121,10 +121,10 @@ void QueryUnit(): { }
 void Query() : { }
 {    
   Prologue()
-  ( SelectQuery() | ConstructQuery() | DescribeQuery() | AskQuery() 
-// #ifdef ARQ
-//   | JsonTemplateQuery()
-// #endif
+  ( SelectQuery() | ConstructQuery() | DescribeQuery() | AskQuery()
+#ifdef ARQ
+    | JsonQuery()
+#endif
   )
   ValuesClause()
 }
@@ -326,6 +326,41 @@ void AskQuery() : {}
   SolutionModifier()
 }
 
+#ifdef ARQ
+void JsonQuery() : {}
+{
+  JsonClause()
+  ( DatasetClause() )*
+  WhereClause()
+  SolutionModifier()
+}
+
+void JsonClause() : {}
+{
+  <JSON> { getQuery().setQueryJsonType() ; }
+  <LBRACE>
+  JsonObjectMember() ( <COMMA> JsonObjectMember() ) *
+  <RBRACE> 
+}
+
+void JsonObjectMember() : { Node o ; String s ; Token t; }
+{
+  s = String()
+  // PNAME_NS would eval true before COLON (default namescape) so we make sure we got what we were expecting
+  t = < PNAME_NS > {
+    if ( ! t.image.equals(":") )
+      throwParseException("Prefix name expression not legal at this point : "+t.image, t.beginLine, t.beginColumn) ;
+  }
+  (
+    o = Var() { getQuery().addResultVar((Var)o) ; getQuery().addJsonMapping(s, o) ; }
+  | o = RDFLiteral() { getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s, o) ; }
+  | o = NumericLiteral() { getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s, o) ; }
+  | o = BooleanLiteral() { getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s, o) ; }
+  )
+}
+
+#endif
+
 // ----
 
 void DatasetClause() : {}
@@ -2276,6 +2311,9 @@ TOKEN [IGNORE_CASE] :
 |  < SELECT:      "select" >
 |  < DISTINCT:    "distinct" >
 |  < REDUCED:     "reduced" >
+#ifdef ARQ
+|  < JSON:        "json" >
+#endif
 |  < DESCRIBE:    "describe" >
 |  < CONSTRUCT:   "construct" >
 |  < ASK:         "ask" >

http://git-wip-us.apache.org/repos/asf/jena/blob/4bf5e3c0/jena-arq/Grammar/tokens.txt
----------------------------------------------------------------------
diff --git a/jena-arq/Grammar/tokens.txt b/jena-arq/Grammar/tokens.txt
index a95d6a5..a5ac75e 100644
--- a/jena-arq/Grammar/tokens.txt
+++ b/jena-arq/Grammar/tokens.txt
@@ -48,6 +48,7 @@
 [<CONSTRUCT>]  ::= 'CONSTRUCT'
 //[<CONSTRUCT_WHERE>]  ::= 'CONSTRUCT WHERE'
 [<ASK>]        ::= 'ASK'
+[<JSON>]       ::= 'JSON'
 [<DISTINCT>]   ::= 'DISTINCT'
 [<REDUCED>]    ::= 'REDUCED'
 [<WHERE>]      ::= 'WHERE'

http://git-wip-us.apache.org/repos/asf/jena/blob/4bf5e3c0/jena-arq/src/main/java/org/apache/jena/query/Query.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/query/Query.java b/jena-arq/src/main/java/org/apache/jena/query/Query.java
index 3eea849..bf50817 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/Query.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/Query.java
@@ -62,6 +62,7 @@ public class Query extends Prologue implements Cloneable, Printable
     public static final int QueryTypeConstruct  = 222 ;
     public static final int QueryTypeDescribe   = 333 ;
     public static final int QueryTypeAsk        = 444 ;
+    public static final int QueryTypeJson       = 555 ;
     int queryType = QueryTypeUnknown ; 
     
     // If no model is provided explicitly, the query engine will load
@@ -135,6 +136,7 @@ public class Query extends Prologue implements Cloneable, Printable
     public void setQueryConstructType()         { queryType = QueryTypeConstruct ; queryResultStar = true ; }
     public void setQueryDescribeType()          { queryType = QueryTypeDescribe ; }
     public void setQueryAskType()               { queryType = QueryTypeAsk ; }
+    public void setQueryJsonType()              { queryType = QueryTypeJson ; }
     
     public int getQueryType()                   { return queryType ; }
     
@@ -146,6 +148,8 @@ public class Query extends Prologue implements Cloneable, Printable
 
     public boolean isAskType()                  { return queryType == QueryTypeAsk ; }
 
+    public boolean isJsonType()                 { return queryType == QueryTypeJson ; }
+
     public boolean isUnknownType()              { return queryType == QueryTypeUnknown ; }
     
     public boolean isConstructQuad()            { return isConstructType() && constructTemplate.containsRealQuad() ; }
@@ -515,6 +519,18 @@ public class Query extends Prologue implements Cloneable, Printable
         havingExprs.add(expr) ;
     }
 
+    // SELECT JSON
+
+    private Map<String, Node> jsonMapping = new LinkedHashMap<>();
+
+    public void addJsonMapping(String key, Node value) {
+        jsonMapping.put(key, value);
+    }
+
+    public Map<String, Node> getJsonMapping() {
+        return Collections.unmodifiableMap(jsonMapping);
+    }
+
     // ---- Aggregates
 
     // Record allocated aggregations.
@@ -707,6 +723,8 @@ public class Query extends Prologue implements Cloneable, Printable
             visitor.visitDescribeResultForm(this) ;
         if ( this.isAskType() )
             visitor.visitAskResultForm(this) ;
+        if ( this.isJsonType() )
+            visitor.visitJsonResultForm(this) ;
         visitor.visitDatasetDecl(this) ;
         visitor.visitQueryPattern(this) ;
         visitor.visitGroupBy(this) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/4bf5e3c0/jena-arq/src/main/java/org/apache/jena/query/QueryExecution.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/query/QueryExecution.java b/jena-arq/src/main/java/org/apache/jena/query/QueryExecution.java
index ada0d00..201b422 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/QueryExecution.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/QueryExecution.java
@@ -21,6 +21,8 @@ package org.apache.jena.query;
 import java.util.Iterator;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.jena.atlas.json.JsonArray;
+import org.apache.jena.atlas.json.JsonObject;
 import org.apache.jena.graph.Triple;
 import org.apache.jena.rdf.model.Model;
 import org.apache.jena.sparql.core.Quad;
@@ -153,7 +155,13 @@ public interface QueryExecution extends AutoCloseable
 
     /** Execute an ASK query */
     public boolean execAsk();
-    
+
+    /** Execute a JSON query and return a json array */
+    public JsonArray execJson() ;
+
+    /** Execute a JSON query and return an interator */
+    public Iterator<JsonObject> execJsonItems() ;
+
 	/** Stop in mid execution.
 	 *  This method can be called in parallel with other methods on the
      *  QueryExecution object.

http://git-wip-us.apache.org/repos/asf/jena/blob/4bf5e3c0/jena-arq/src/main/java/org/apache/jena/query/QueryVisitor.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/query/QueryVisitor.java b/jena-arq/src/main/java/org/apache/jena/query/QueryVisitor.java
index 7368701..3d0afac 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/QueryVisitor.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/QueryVisitor.java
@@ -32,7 +32,8 @@ public interface QueryVisitor
     public void visitConstructResultForm(Query query) ;
     public void visitDescribeResultForm(Query query) ;
     public void visitAskResultForm(Query query) ;
-    
+    public void visitJsonResultForm(Query query);
+
     public void visitDatasetDecl(Query query) ;
     public void visitQueryPattern(Query query) ;
     

http://git-wip-us.apache.org/repos/asf/jena/blob/4bf5e3c0/jena-arq/src/main/java/org/apache/jena/query/ResultSetFormatter.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/query/ResultSetFormatter.java b/jena-arq/src/main/java/org/apache/jena/query/ResultSetFormatter.java
index 3d2aa83..359de91 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/ResultSetFormatter.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/ResultSetFormatter.java
@@ -31,6 +31,8 @@ import java.util.ArrayList ;
 import java.util.Iterator ;
 import java.util.List ;
 
+import org.apache.jena.atlas.io.IndentedWriter;
+import org.apache.jena.atlas.json.JsonObject;
 import org.apache.jena.atlas.logging.Log ;
 import org.apache.jena.rdf.model.RDFNode ;
 import org.apache.jena.riot.Lang;
@@ -322,18 +324,43 @@ public class ResultSetFormatter {
     public static void output(boolean result, Lang resultFormat) {
         output(System.out, result, resultFormat);
     }
-    // ---- General Output
     
     public static void output(OutputStream outStream, boolean result, Lang resultFormat) {
         ResultsWriter.create().lang(resultFormat).build().write(outStream, result);
     }
+
+    /** Output an iterator of JSON values.
+     *
+     * @param outStream output stream
+     * @param jsonItems The JSON values
+     */
+    public static void output(OutputStream outStream, Iterator<JsonObject> jsonItems)
+    {
+        IndentedWriter out = new IndentedWriter(outStream) ;
+        out.println("[") ;
+        out.incIndent() ;
+        while (jsonItems.hasNext())
+        {
+            JsonObject jsonItem = jsonItems.next() ;
+            jsonItem.output(out) ;
+            if ( jsonItems.hasNext() )
+                out.println(" ,");
+            else
+                out.println();
+        }
+        out.decIndent();
+        out.println("]");
+        out.flush();
+    }
+
+    // ---- General Output
+
+    // ---- XML Output
+
     /** Output a result set in the XML format
      * 
      * @param qresults      result set
      */
-    
-    // ---- XML Output
-
     static public void outputAsXML(ResultSet qresults)
     { outputAsXML(System.out, qresults) ; }
 
@@ -517,7 +544,7 @@ public class ResultSetFormatter {
     
     static public void outputAsJSON(OutputStream outStream, boolean booleanResult)
     { output(outStream, booleanResult, SPARQLResultSetJSON) ; }
-    
+
     // ---- SSE
     
     /** Output a boolean result in the SSE format

http://git-wip-us.apache.org/repos/asf/jena/blob/4bf5e3c0/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryCompare.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryCompare.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryCompare.java
index a2c2ea9..7a6df46 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryCompare.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryCompare.java
@@ -105,6 +105,11 @@ public class QueryCompare implements QueryVisitor
     }
 
     @Override
+    public void visitJsonResultForm(Query query) {
+        check("Not both JSON queries", query2.isJsonType()) ;
+    }
+
+    @Override
     public void visitDatasetDecl(Query query1)
     {
         boolean b1 = Lib.equalsListAsSet(query1.getGraphURIs(), query2.getGraphURIs()) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/4bf5e3c0/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryHashCode.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryHashCode.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryHashCode.java
index 543ccaf..0ca768e 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryHashCode.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryHashCode.java
@@ -81,6 +81,10 @@ public class QueryHashCode
         { }
 
         @Override
+        public void visitJsonResultForm(Query query)
+        { }
+
+        @Override
         public void visitDatasetDecl(Query query)
         {
             x ^= query.getNamedGraphURIs().hashCode() ; 

http://git-wip-us.apache.org/repos/asf/jena/blob/4bf5e3c0/jena-arq/src/main/java/org/apache/jena/sparql/engine/JsonIterator.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/JsonIterator.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/JsonIterator.java
new file mode 100644
index 0000000..6306bd9
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/JsonIterator.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.engine;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.jena.atlas.json.JsonObject;
+import org.apache.jena.atlas.json.JsonValue;
+import org.apache.jena.atlas.lib.Closeable;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.sparql.engine.binding.Binding;
+import org.apache.jena.sparql.lib.RDFTerm2Json;
+
+/** A JSON iterator for JsonObject's, that wraps a QueryIterator, and a list
+ * of result variables.
+ */
+public class JsonIterator implements Iterator<JsonObject>
+{
+
+    private final QueryIterator queryIterator ;
+    private final List<String> resultVars ;
+
+    public JsonIterator(QueryIterator queryIterator, List<String> resultVars)
+    {
+        this.queryIterator = queryIterator ;
+        this.resultVars = Collections.unmodifiableList(resultVars) ;
+    }
+
+    @Override
+    public boolean hasNext()
+    {
+        if (queryIterator == null)
+            return false;
+        boolean r = queryIterator.hasNext() ;
+        if (!r)
+            close() ;
+        return r ;
+    }
+
+    @Override
+    public JsonObject next()
+    {
+        if (queryIterator == null)
+            throw new NoSuchElementException(this.getClass() + ".next") ;
+        try
+        {
+            Binding binding = queryIterator.next() ;
+            JsonObject jsonObject = new JsonObject() ;
+            for (String resultVar : resultVars)
+            {
+                Node n = binding.get(Var.alloc(resultVar)) ;
+                JsonValue value = RDFTerm2Json.fromNode(n) ;
+                jsonObject.put(resultVar, value);
+            }
+            return jsonObject ;
+        }
+        catch (NoSuchElementException ex)
+        {
+            close() ;
+            throw ex ;
+        }
+    }
+
+    @Override
+    public void remove()
+    {
+        throw new UnsupportedOperationException(this.getClass().getName() + ".remove") ;
+    }
+
+    /**
+     * Closes the QueryIterator, if it is an instance of Closeable.
+     */
+    private void close()
+    {
+        if (queryIterator instanceof Closeable)
+            ((Closeable) queryIterator).close();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/4bf5e3c0/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java
index 1dc292e..0e5d134 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java
@@ -24,6 +24,9 @@ import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.jena.atlas.json.JsonArray;
+import org.apache.jena.atlas.json.JsonObject;
+import org.apache.jena.atlas.json.JsonValue;
 import org.apache.jena.atlas.lib.Alarm ;
 import org.apache.jena.atlas.lib.AlarmClock;
 import org.apache.jena.atlas.logging.Log;
@@ -36,6 +39,7 @@ import org.apache.jena.shared.PrefixMapping;
 import org.apache.jena.sparql.ARQConstants;
 import org.apache.jena.sparql.core.DatasetGraph;
 import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.core.Var;
 import org.apache.jena.sparql.core.describe.DescribeHandler;
 import org.apache.jena.sparql.core.describe.DescribeHandlerRegistry;
 import org.apache.jena.sparql.engine.binding.Binding;
@@ -43,6 +47,7 @@ import org.apache.jena.sparql.engine.binding.BindingRoot;
 import org.apache.jena.sparql.engine.binding.BindingUtils;
 import org.apache.jena.sparql.engine.iterator.QueryIteratorWrapper;
 import org.apache.jena.sparql.graph.GraphFactory;
+import org.apache.jena.sparql.lib.RDFTerm2Json;
 import org.apache.jena.sparql.modify.TemplateLib;
 import org.apache.jena.sparql.syntax.ElementGroup;
 import org.apache.jena.sparql.syntax.Template;
@@ -335,7 +340,45 @@ public class QueryExecutionBase implements QueryExecution
     }
 
     @Override
-    public void setTimeout(long timeout, TimeUnit timeUnit) {
+    public JsonArray execJson()
+    {
+        checkNotClosed() ;
+        if ( ! query.isJsonType() )
+            throw new QueryExecException("Attempt to get a JSON result from a " + labelForQuery(query)+" query") ;
+
+        startQueryIterator() ;
+
+        JsonArray jsonArray = new JsonArray() ;
+        List<String> resultVars = query.getResultVars() ;
+
+        while (queryIterator.hasNext())
+        {
+            Binding binding = queryIterator.next() ;
+            JsonObject jsonObject = new JsonObject() ; 
+            for (String resultVar : resultVars) {
+                Node n = binding.get(Var.alloc(resultVar)) ;
+                JsonValue value = RDFTerm2Json.fromNode(n) ;
+                jsonObject.put(resultVar, value) ;
+            }
+            jsonArray.add(jsonObject) ;
+        }
+
+        return jsonArray ;
+    }
+
+    @Override
+    public Iterator<JsonObject> execJsonItems() 
+    {
+        checkNotClosed() ;
+        if ( ! query.isJsonType() )
+            throw new QueryExecException("Attempt to get a JSON result from a " + labelForQuery(query)+" query") ;
+        startQueryIterator() ;
+        return new JsonIterator(queryIterator, query.getResultVars()) ;
+    }
+
+    @Override
+    public void setTimeout(long timeout, TimeUnit timeUnit)
+    {
         // Overall timeout - recorded as (UNSET,N)
         long x = asMillis(timeout, timeUnit);
         this.timeout1 = TIMEOUT_UNSET;

http://git-wip-us.apache.org/repos/asf/jena/blob/4bf5e3c0/jena-arq/src/main/java/org/apache/jena/sparql/engine/http/QueryEngineHTTP.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/http/QueryEngineHTTP.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/http/QueryEngineHTTP.java
index 7c7491d..91c3ed5 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/http/QueryEngineHTTP.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/http/QueryEngineHTTP.java
@@ -31,6 +31,9 @@ import org.apache.http.client.protocol.HttpClientContext;
 import org.apache.http.protocol.HttpContext ;
 import org.apache.jena.atlas.RuntimeIOException;
 import org.apache.jena.atlas.io.IO ;
+import org.apache.jena.atlas.json.JsonArray;
+import org.apache.jena.atlas.json.JsonObject;
+import org.apache.jena.atlas.lib.NotImplemented;
 import org.apache.jena.atlas.lib.Pair ;
 import org.apache.jena.graph.Triple ;
 import org.apache.jena.query.* ;
@@ -532,6 +535,18 @@ public class QueryEngineHTTP implements QueryExecution {
         }
     }
 
+    @Override
+    public JsonArray execJson()
+    {
+        throw new NotImplemented("JSON queries not implemented for remote calls") ;
+    }
+
+    @Override
+    public Iterator<JsonObject> execJsonItems()
+    {
+        throw new NotImplemented("JSON queries not implemented for remote calls") ;
+    }
+
     private void checkNotClosed() {
         if ( closed )
             throw new QueryExecException("HTTP QueryExecution has been closed") ;