This is an automated email from the ASF dual-hosted git repository.
andy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jena.git
The following commit(s) were added to refs/heads/master by this push:
new e87a872 JENA-1881: Support RDF* triple terms in SPARQL XML results
new 7e71664 Merge pull request #742 from afs/jena-1881_sparql_xml
e87a872 is described below
commit e87a8729f7416f183f0cd82819ab2ec0b5ed9d56
Author: Andy Seaborne <[email protected]>
AuthorDate: Mon May 11 18:58:40 2020 +0100
JENA-1881: Support RDF* triple terms in SPARQL XML results
---
.../riot/resultset/rw/ResultSetWriterJSON.java | 6 +-
.../jena/riot/resultset/rw/ResultSetWriterXML.java | 135 ++++++++++++-----
.../apache/jena/riot/resultset/rw/ResultsStAX.java | 168 +++++++++++++++------
.../apache/jena/riot/resultset/rw/XMLResults.java | 10 ++
.../org/apache/jena/sparql/util/ModelUtils.java | 3 +
.../testing/ARQ/RDF-Star/SPARQL-star/manifest.ttl | 2 +-
.../RDF-Star/SPARQL-star/sparql-star-basic-1.srj | 25 ---
.../RDF-Star/SPARQL-star/sparql-star-basic-1.srx | 42 ++++++
8 files changed, 275 insertions(+), 116 deletions(-)
diff --git
a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterJSON.java
b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterJSON.java
index 508df6e..e5f74e9 100644
---
a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterJSON.java
+++
b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterJSON.java
@@ -346,17 +346,17 @@ public class ResultSetWriterJSON implements
ResultSetWriter {
// ---
print(out, quoteName(kSubject), ": ");
- print(out, " ");
+ print(out, " ");
writeValue(out, triple.getSubject(), multiLineInnerValue);
println(out, " ,");
- print(out, quoteName(kPredicate), ": ");
+ print(out, quoteName(kProperty), ": ");
writeValue(out, triple.getPredicate(), multiLineInnerValue);
println(out, " ,");
print(out, quoteName(kObject), ": ");
- print(out, " ");
+ print(out, " ");
writeValue(out, triple.getObject(), multiLineInnerValue);
// End of triple object.
decIndent(out);
diff --git
a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterXML.java
b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterXML.java
index 92fd5c4..8dead39 100644
---
a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterXML.java
+++
b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultSetWriterXML.java
@@ -24,12 +24,13 @@ import java.util.Objects;
import org.apache.jena.atlas.io.IndentedWriter;
import org.apache.jena.atlas.logging.Log;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Node_Triple;
+import org.apache.jena.graph.Triple;
import org.apache.jena.query.ARQ;
import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.ResultSet;
-import org.apache.jena.rdf.model.Literal;
import org.apache.jena.rdf.model.RDFNode;
-import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.impl.Util;
import org.apache.jena.riot.out.NodeToLabel;
import org.apache.jena.riot.resultset.ResultSetLang;
@@ -243,8 +244,8 @@ public class ResultSetWriterXML implements ResultSetWriter {
}
@Override
- public void binding(String varName, RDFNode node) {
- if ( node == null && !outputExplicitUnbound )
+ public void binding(String varName, RDFNode rdfNode) {
+ if ( rdfNode == null && !outputExplicitUnbound )
return;
out.print("<");
@@ -253,15 +254,15 @@ public class ResultSetWriterXML implements
ResultSetWriter {
out.print(varName);
out.println("\">");
out.incIndent(INDENT);
- printBindingValue(node);
+ printBindingValue(rdfNode);
out.decIndent(INDENT);
out.print("</");
out.print(dfBinding);
out.println(">");
}
- void printBindingValue(RDFNode node) {
- if ( node == null ) {
+ private void printBindingValue(RDFNode rdfNode) {
+ if ( rdfNode == null ) {
// Unbound
out.print("<");
out.print(dfUnbound);
@@ -269,68 +270,122 @@ public class ResultSetWriterXML implements
ResultSetWriter {
return;
}
- if ( node instanceof Literal ) {
- printLiteral((Literal)node);
+ Node node = rdfNode.asNode();
+ printBindingValue(node);
+ }
+
+ private void printBindingValue(Node node) {
+ if ( node == null )
+ return;
+
+ if ( node.isLiteral() ) {
+ printLiteral(node);
return;
}
- if ( node instanceof Resource ) {
- printResource((Resource)node);
+ if ( node.isURI() ) {
+ printURI(node);
+ return;
+ }
+
+ if ( node.isBlank() ) {
+ printBlankNode(node);
+ return;
+ }
+ if ( node.isNodeTriple() ) {
+ printTripleTerm(node);
return;
}
+
+ if ( node.isNodeGraph() )
+ throw new UnsupportedOperationException("Graph terms");
- Log.warn(this, "Unknown RDFNode type in result set: " +
node.getClass());
+ Log.warn(this, "Unknown RDFNode type in result set: " + node);
}
- void printLiteral(Literal literal) {
+ private void printURI(Node nodeURI) {
+ String uri = nodeURI.getURI();
out.print("<");
- out.print(dfLiteral);
+ out.print(dfURI);
+ out.print(">");
+ out.print(xml_escape(uri));
+ out.print("</");
+ out.print(dfURI);
+ out.println(">");
+ }
+
+ private void printBlankNode(Node node) {
+ String label = bNodeMap.get(null, node);
+ // Comes with leading "_:"
+ label = label.substring(2);
+ out.print("<");
+ out.print(dfBNode);
+ out.print(">");
+ out.print(xml_escape(label));
+ out.print("</");
+ out.print(dfBNode);
+ out.println(">");
+ }
+ private void printLiteral(Node literal) {
+ out.print("<");
+ out.print(dfLiteral);
+
if ( Util.isLangString(literal) ) {
- String lang = literal.getLanguage();
+ String lang = literal.getLiteralLanguage();
out.print(" xml:lang=\"");
- out.print(literal.getLanguage());
+ out.print(lang);
out.print("\"");
} else if ( !Util.isSimpleString(literal) ) {
// Datatype
// (RDF 1.1) not xsd:string nor rdf:langString.
// (RDF 1.0) any datatype.
- String datatype = literal.getDatatypeURI();
+ String datatype = literal.getLiteralDatatypeURI();
out.print(" ");
out.print(dfAttrDatatype);
out.print("=\"");
out.print(datatype);
out.print("\"");
}
-
+
out.print(">");
- out.print(xml_escape(literal.getLexicalForm()));
+ out.print(xml_escape(literal.getLiteralLexicalForm()));
out.print("</");
out.print(dfLiteral);
out.println(">");
}
- void printResource(Resource r) {
- if ( r.isAnon() ) {
- String label = bNodeMap.get(null, r.asNode());
- // Comes with leading "_:"
- label = label.substring(2);
- out.print("<");
- out.print(dfBNode);
- out.print(">");
- out.print(xml_escape(label));
- out.print("</");
- out.print(dfBNode);
- out.println(">");
- } else {
- out.print("<");
- out.print(dfURI);
- out.print(">");
- out.print(xml_escape(r.getURI()));
- out.print("</");
- out.print(dfURI);
- out.println(">");
- }
+ private void printTripleTerm(Node node) {
+ Triple triple = Node_Triple.triple(node);
+ openTag(dfTriple);
+
+ // Subject
+ openTag(dfSubject);
+ printBindingValue(triple.getSubject());
+ closeTag(dfSubject);
+ // Property
+ openTag(dfProperty);
+ printBindingValue(triple.getPredicate());
+ closeTag(dfProperty);
+ // Object
+ openTag(dfObject);
+ printBindingValue(triple.getObject());
+ closeTag(dfObject);
+
+ closeTag(dfTriple);
+ }
+
+ private void openTag(String name) {
+ out.print("<");
+ out.print(name);
+ out.println(">");
+ out.incIndent();
+ }
+ private void closeTag(String name) {
+ out.decIndent();
+ out.print("</");
+ out.print(name);
+ out.println(">");
}
private static String xml_escape(String string) {
diff --git
a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultsStAX.java
b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultsStAX.java
index cb1949d..6407f53 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultsStAX.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/ResultsStAX.java
@@ -23,6 +23,7 @@ import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.Objects;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
@@ -53,7 +54,7 @@ import org.apache.jena.sparql.resultset.ResultSetException;
import org.apache.jena.sparql.resultset.SPARQLResult;
import org.apache.jena.sparql.util.Context;
-/** Public only for use by XMLOuptu (legacy) */
+/** Public only for use by XMLOutput (legacy) */
public class ResultsStAX implements ResultSet, Closeable {
public static SPARQLResult read(InputStream in, Model model, Context
context) {
XMLInputFactory xf = XMLInputFactory.newInstance() ;
@@ -90,7 +91,8 @@ public class ResultsStAX implements ResultSet, Closeable {
private QuerySolution current = null;
private XMLStreamReader parser = null;
private List<String> variables = new ArrayList<>();
- private Binding binding = null;
// Current
+ // Current binding/query solution.
+ private Binding binding = null;
private boolean inputGraphLabels =
ARQ.isTrue(ARQ.inputGraphBNodeLabels);
private final LabelToNode bNodes;
@@ -386,53 +388,13 @@ public class ResultsStAX implements ResultSet, Closeable {
varName = parser.getAttributeValue(null,
XMLResults.dfAttrVarName) ;
break ;
}
- // URI, literal, bNode, unbound.
- if ( isTag(tag, XMLResults.dfBNode) ) {
- String label = parser.getElementText() ;
- Node node = null ;
- // if ( inputGraphLabels.getValue() )
- if ( inputGraphLabels )
- node = NodeFactory.createBlankNode(label) ;
- else
- node = bNodes.get(null, label);
- addBinding(binding, Var.alloc(varName), node) ;
- break ;
- }
-
- if ( isTag(tag, XMLResults.dfLiteral) ) {
- String datatype = parser.getAttributeValue(null,
XMLResults.dfAttrDatatype) ;
-
- // String langTag = parser.getAttributeValue(null,
- // "lang") ;
-
- // Woodstox needs XML_NS despite the javadoc of StAX
- // "If the namespaceURI is null the namespace is not
checked for equality"
- // StAX(.codehaus.org) copes both ways round
- String langTag = parser.getAttributeValue(XML_NS,
"lang") ;
-
- // Works for XML literals (returning them as a
- // string)
- String text = parser.getElementText() ;
-
- RDFDatatype dType = null ;
- if ( datatype != null )
- dType =
TypeMapper.getInstance().getSafeTypeByName(datatype) ;
-
- Node n = NodeFactory.createLiteral(text, langTag,
dType) ;
+
+ Node value = parseOneTerm(tag);
+ if ( value != null ) {
if ( varName == null )
throw new ResultSetException("No name for
variable") ;
- addBinding(binding, Var.alloc(varName), n) ;
- break ;
- }
-
- if ( isTag(tag, XMLResults.dfUnbound) ) {
- break ;
- }
- if ( isTag(tag, XMLResults.dfURI) ) {
- String uri = parser.getElementText() ;
- Node node = NodeFactory.createURI(uri) ;
- addBinding(binding, Var.alloc(varName), node) ;
- break ;
+ addBinding(binding, Var.alloc(varName), value) ;
+ break;
}
break ;
default :
@@ -442,6 +404,118 @@ public class ResultsStAX implements ResultSet, Closeable {
return null ;
}
+ private Node parseOneTerm(String tag) throws XMLStreamException {
+ // URI, literal, bNode, unbound, triple
+ if ( isTag(tag, XMLResults.dfBNode) ) {
+ String label = parser.getElementText() ;
+ Node node = null ;
+ // if ( inputGraphLabels.getValue() )
+ if ( inputGraphLabels )
+ node = NodeFactory.createBlankNode(label) ;
+ else
+ node = bNodes.get(null, label);
+ return node;
+ }
+
+ if ( isTag(tag, XMLResults.dfLiteral) ) {
+ String datatype = parser.getAttributeValue(null,
XMLResults.dfAttrDatatype) ;
+
+ // String langTag = parser.getAttributeValue(null, "lang") ;
+ // Woodstox needs XML_NS despite the javadoc of StAX
+ // "If the namespaceURI is null the namespace is not checked for
equality"
+ // StAX(.codehaus.org) copes both ways round
+ String langTag = parser.getAttributeValue(XML_NS, "lang") ;
+
+ // Works for XML literals (returning them as a string)
+ String text = parser.getElementText() ;
+
+ RDFDatatype dType = null ;
+ if ( datatype != null )
+ dType = TypeMapper.getInstance().getSafeTypeByName(datatype) ;
+
+ Node n = NodeFactory.createLiteral(text, langTag, dType) ;
+ return n ;
+ }
+
+ if ( isTag(tag, XMLResults.dfUnbound) ) {
+ return null;
+ }
+
+ if ( isTag(tag, XMLResults.dfURI) ) {
+ String uri = parser.getElementText() ;
+ Node node = NodeFactory.createURI(uri) ;
+ return node;
+ }
+
+ if ( isTag(tag, XMLResults.dfTriple) ) {
+ // <triple>
+ Node s = null;
+ Node p = null;
+ Node o = null;
+ while (parser.hasNext()) {
+ // Skip CHARACTERS
+ int event2 = parser.nextTag() ;
+ if ( event2 == XMLStreamConstants.END_ELEMENT ) {
+ // Early end.
+ String tagx = parser.getLocalName() ;
+ if ( tagx.equals(XMLResults.dfTriple) )
+ staxError("Incomplete triple term");
+ else
+ staxError("Mismatched tag: "+tagx);
+ }
+ //XMLStreamConstants.START_ELEMENT
+
+ String tag2 = parser.getLocalName() ;
+ // <subject> <property> <object>
+ // One of subject, property, object (s,p,o)
+
+ if ( ! isOneOf(tag2, XMLResults.dfSubject,
XMLResults.dfProperty, XMLResults.dfPredicate, XMLResults.dfObject,
+ XMLResults.dfSubjectAlt,
XMLResults.dfPropertyAlt, XMLResults.dfObjectAlt) )
+ staxError("Unexpected tag in triple term: "+tag2);
+
+ int event3 = parser.nextTag() ;
+ String tag3 = parser.getLocalName() ;
+ Node x = parseOneTerm(tag3);
+
+ // Read end </subject> etc.
+ parser.nextTag() ;
+
+ // Check for double assignment.
+ if ( isOneOf(tag2, XMLResults.dfSubject,
XMLResults.dfSubjectAlt) )
+ s = x ;
+ else if ( isOneOf(tag2, XMLResults.dfProperty,
XMLResults.dfPredicate, XMLResults.dfPropertyAlt) )
+ p = x ;
+ else if ( isOneOf(tag2, XMLResults.dfObject,
XMLResults.dfObjectAlt) )
+ o = x ;
+ if ( s != null && p != null && o != null ) {
+ int event4 = parser.nextTag() ;
+ String tagx = parser.getLocalName() ;
+ if ( event4 == XMLStreamConstants.START_ELEMENT )
+ staxError("Too many terms for a triple");
+ // XMLStreamConstants.END_ELEMENT )
+ if ( ! tagx.equals(XMLResults.dfTriple) )
+ staxError("Expecting </triple>: "+tagx);
+ // </triple>
+ break;
+ }
+ }
+
+ if ( s == null || p == null || o == null )
+ staxError("Bad <triple> term");
+ Node node = NodeFactory.createTripleNode(s, p, o);
+ return node;
+ }
+ return null;
+ }
+
+ static private boolean isOneOf (String string, String...values) {
+ for ( String v : values ) {
+ if ( Objects.equals(string, v) )
+ return true;
+ }
+ return false;
+ }
+
static protected void addBinding(BindingMap binding, Var var, Node value) {
Node n = binding.get(var);
if ( n != null ) {
diff --git
a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/XMLResults.java
b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/XMLResults.java
index ae0c423..ff7565f 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/XMLResults.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/resultset/rw/XMLResults.java
@@ -43,6 +43,16 @@ public interface XMLResults
public static final String dfBNode = "bnode" ;
public static final String dfURI = "uri" ;
public static final String dfLiteral = "literal" ;
+ public static final String dfTriple = "triple" ;
+ public static final String dfSubject = "subject" ;
+ public static final String dfProperty = "property" ;
+ public static final String dfObject = "object" ;
+
+ // Alternatives
+ public static final String dfPredicate = "predicate" ;
+ public static final String dfSubjectAlt = "s" ;
+ public static final String dfPropertyAlt = "p" ;
+ public static final String dfObjectAlt = "o" ;
public static final String dfUnbound = "unbound" ;
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/ModelUtils.java
b/jena-arq/src/main/java/org/apache/jena/sparql/util/ModelUtils.java
index 9c4b0c5..8e351c2 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/util/ModelUtils.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/ModelUtils.java
@@ -60,6 +60,9 @@ public class ModelUtils
if ( node.isURI() || node.isBlank() )
return new ResourceImpl(node, null) ;
+
+ if ( node.isNodeTriple() )
+ return new ResourceImpl(node, null) ;
throw new ARQInternalErrorException("Unknown node type for node:
"+node) ;
}
diff --git a/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/manifest.ttl
b/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/manifest.ttl
index 04363de..55703b1 100644
--- a/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/manifest.ttl
+++ b/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/manifest.ttl
@@ -113,7 +113,7 @@ PREFIX : <#>
mf:action
[ qt:query <sparql-star-basic-1.rq> ;
qt:data <data1.ttl> ] ;
- mf:result <sparql-star-basic-1.srj>
+ mf:result <sparql-star-basic-1.srx>
.
:sparql-star-basic-2
diff --git a/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/sparql-star-basic-1.srj
b/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/sparql-star-basic-1.srj
deleted file mode 100644
index 067edb2..0000000
--- a/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/sparql-star-basic-1.srj
+++ /dev/null
@@ -1,25 +0,0 @@
-{ "head": {
- "vars": [ "s" , "p" , "o" ]
- } ,
- "results": {
- "bindings": [
- {
- "s": {
- "type": "triple" ,
- "value": {
- "subject": { "type": "uri" , "value": "http://example/s" } ,
- "predicate": { "type": "uri" , "value": "http://example/p" } ,
- "object": { "type": "uri" , "value": "http://example/o" }
- }
- } ,
- "p": { "type": "uri" , "value": "http://example/q" } ,
- "o": { "type": "literal" , "datatype":
"http://www.w3.org/2001/XMLSchema#integer" , "value": "123" }
- } ,
- {
- "s": { "type": "uri" , "value": "http://example/s" } ,
- "p": { "type": "uri" , "value": "http://example/p" } ,
- "o": { "type": "uri" , "value": "http://example/o" }
- }
- ]
- }
-}
diff --git a/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/sparql-star-basic-1.srx
b/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/sparql-star-basic-1.srx
new file mode 100644
index 0000000..d52ff7d
--- /dev/null
+++ b/jena-arq/testing/ARQ/RDF-Star/SPARQL-star/sparql-star-basic-1.srx
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<sparql xmlns="http://www.w3.org/2005/sparql-results#">
+ <head>
+ <variable name="s"/>
+ <variable name="p"/>
+ <variable name="o"/>
+ </head>
+ <results>
+ <result>
+ <binding name="s">
+ <triple>
+ <subject>
+ <uri>http://example/s</uri>
+ </subject>
+ <property>
+ <uri>http://example/p</uri>
+ </property>
+ <object>
+ <uri>http://example/o</uri>
+ </object>
+ </triple>
+ </binding>
+ <binding name="p">
+ <uri>http://example/q</uri>
+ </binding>
+ <binding name="o">
+ <literal
datatype="http://www.w3.org/2001/XMLSchema#integer">123</literal>
+ </binding>
+ </result>
+ <result>
+ <binding name="s">
+ <uri>http://example/s</uri>
+ </binding>
+ <binding name="p">
+ <uri>http://example/p</uri>
+ </binding>
+ <binding name="o">
+ <uri>http://example/o</uri>
+ </binding>
+ </result>
+ </results>
+</sparql>