Adds RDF/XML server output and upgrades quick_xml

pull/10/head
Tpt 5 years ago
parent 78a2d59e19
commit e1328bb204
  1. 2
      lib/Cargo.toml
  2. 4
      lib/src/sparql/json_results.rs
  3. 95
      lib/src/sparql/model.rs
  4. 132
      lib/src/sparql/xml_results.rs
  5. 26
      server/src/main.rs

@ -20,7 +20,7 @@ lazy_static = "1"
rocksdb = { version = "0.12", optional = true } rocksdb = { version = "0.12", optional = true }
uuid = { version = "0.7", features = ["v4"] } uuid = { version = "0.7", features = ["v4"] }
byteorder = { version = "1", features = ["i128"] } byteorder = { version = "1", features = ["i128"] }
quick-xml = "0.15" quick-xml = { version = "0.16", features = ["failure"] }
ordered-float = "1" ordered-float = "1"
num-traits = "0.2" num-traits = "0.2"
rust_decimal = "1" rust_decimal = "1"

@ -53,7 +53,7 @@ pub fn write_json_results<W: Write>(results: QueryResult<'_>, mut sink: W) -> Re
} }
Term::BlankNode(bnode) => { Term::BlankNode(bnode) => {
sink.write_all(b":{\"type\":\"bnode\",\"value\":")?; sink.write_all(b":{\"type\":\"bnode\",\"value\":")?;
sink.write_fmt(format_args!("{}", bnode.as_uuid().to_simple()))?; write!(sink, "{}", bnode.as_uuid().to_simple())?;
sink.write_all(b"}")?; sink.write_all(b"}")?;
} }
Term::Literal(literal) => { Term::Literal(literal) => {
@ -113,7 +113,7 @@ fn write_escaped_json_string(s: &str, sink: &mut impl Write) -> Result<()> {
} }
} }
} else { } else {
sink.write_fmt(format_args!("{}", c)) write!(sink, "{}", c)
} }
} }
}?; }?;

@ -1,8 +1,10 @@
use crate::model::*; use crate::model::*;
use crate::sparql::json_results::write_json_results; use crate::sparql::json_results::write_json_results;
use crate::sparql::xml_results::{read_xml_results, write_xml_results}; use crate::sparql::xml_results::{read_xml_results, write_xml_results};
use crate::{FileSyntax, Result}; use crate::{FileSyntax, GraphSyntax, Result};
use failure::format_err; use failure::format_err;
use quick_xml::events::*;
use quick_xml::Writer;
use std::fmt; use std::fmt;
use std::io::{BufRead, Write}; use std::io::{BufRead, Write};
use uuid::Uuid; use uuid::Uuid;
@ -31,6 +33,97 @@ impl<'a> QueryResult<'a> {
QueryResultSyntax::Json => write_json_results(self, writer), QueryResultSyntax::Json => write_json_results(self, writer),
} }
} }
pub fn write_graph<W: Write>(self, mut writer: W, syntax: GraphSyntax) -> Result<W> {
if let QueryResult::Graph(triples) = self {
match syntax {
GraphSyntax::NTriples | GraphSyntax::Turtle => {
for triple in triples {
writeln!(&mut writer, "{}", triple?)?
}
Ok(writer)
}
GraphSyntax::RdfXml => {
let mut writer = Writer::new(writer);
writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))?;
let mut rdf_open = BytesStart::borrowed_name(b"rdf:RDF");
rdf_open.push_attribute((
"xmlns:rdf",
"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
));
writer.write_event(Event::Start(rdf_open))?;
let mut current_subject = None;
for triple in triples {
let triple = triple?;
// We open a new rdf:Description if useful
if current_subject.as_ref() != Some(triple.subject()) {
if current_subject.is_some() {
writer.write_event(Event::End(BytesEnd::borrowed(
b"rdf:Description",
)))?;
}
let mut description_open =
BytesStart::borrowed_name(b"rdf:Description");
match triple.subject() {
NamedOrBlankNode::NamedNode(n) => {
description_open.push_attribute(("rdf:about", n.as_str()))
}
NamedOrBlankNode::BlankNode(n) => {
let id = n.as_uuid().to_simple().to_string();
description_open.push_attribute(("rdf:nodeID", id.as_str()))
}
}
writer.write_event(Event::Start(description_open))?;
}
let mut property_open = BytesStart::borrowed_name(b"prop:");
let mut content = None;
property_open.push_attribute(("xmlns:prop", triple.predicate().as_str()));
match triple.object() {
Term::NamedNode(n) => {
property_open.push_attribute(("rdf:resource", n.as_str()))
}
Term::BlankNode(n) => {
let id = n.as_uuid().to_simple().to_string();
property_open.push_attribute(("rdf:nodeID", id.as_str()))
}
Term::Literal(l) => {
if let Some(language) = l.language() {
property_open.push_attribute(("xml:lang", language.as_str()))
} else if !l.is_plain() {
property_open
.push_attribute(("rdf:datatype", l.datatype().as_str()))
}
content = Some(l.value());
}
}
if let Some(content) = content {
writer.write_event(Event::Start(property_open))?;
writer.write_event(Event::Text(BytesText::from_plain_str(&content)))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"prop:")))?;
} else {
writer.write_event(Event::Empty(property_open))?;
}
current_subject = Some(triple.subject_owned());
}
if current_subject.is_some() {
writer.write_event(Event::End(BytesEnd::borrowed(b"rdf:Description")))?;
}
writer.write_event(Event::End(BytesEnd::borrowed(b"rdf:RDF")))?;
Ok(writer.into_inner())
}
}
} else {
Err(format_err!(
"Bindings or booleans could not be formatted as an RDF graph"
))
}
}
} }
/// [SPARQL query](https://www.w3.org/TR/sparql11-query/) serialization formats /// [SPARQL query](https://www.w3.org/TR/sparql11-query/) serialization formats

@ -132,7 +132,7 @@ pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>
if ns != b"http://www.w3.org/2005/sparql-results#".as_ref() { if ns != b"http://www.w3.org/2005/sparql-results#".as_ref() {
return Err(format_err!( return Err(format_err!(
"Unexpected namespace found in RDF/XML query result: {}", "Unexpected namespace found in RDF/XML query result: {}",
reader.decode(ns) reader.decode(ns)?
)); ));
} }
} }
@ -144,20 +144,20 @@ pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>
if event.name() == b"sparql" { if event.name() == b"sparql" {
state = State::Sparql; state = State::Sparql;
} else { } else {
return Err(format_err!("Expecting <sparql> tag, found {}", reader.decode(event.name()))); return Err(format_err!("Expecting <sparql> tag, found {}", reader.decode(event.name())?));
} }
} }
State::Sparql => { State::Sparql => {
if event.name() == b"head" { if event.name() == b"head" {
state = State::Head; state = State::Head;
} else { } else {
return Err(format_err!("Expecting <head> tag, found {}", reader.decode(event.name()))); return Err(format_err!("Expecting <head> tag, found {}", reader.decode(event.name())?));
} }
} }
State::Head => if event.name() == b"variable" || event.name() == b"link" { State::Head => if event.name() == b"variable" || event.name() == b"link" {
return Err(format_err!("<variable> and <link> tag should be autoclosing")); return Err(format_err!("<variable> and <link> tag should be autoclosing"));
} else { } else {
return Err(format_err!("Expecting <variable> or <link> tag, found {}", reader.decode(event.name()))); return Err(format_err!("Expecting <variable> or <link> tag, found {}", reader.decode(event.name())?));
} }
State::AfterHead => { State::AfterHead => {
if event.name() == b"boolean" { if event.name() == b"boolean" {
@ -178,10 +178,10 @@ pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>
}), }),
))); )));
} else if event.name() != b"link" && event.name() != b"results" && event.name() != b"boolean" { } else if event.name() != b"link" && event.name() != b"results" && event.name() != b"boolean" {
return Err(format_err!("Expecting sparql tag, found {}", reader.decode(event.name()))); return Err(format_err!("Expecting sparql tag, found {}", reader.decode(event.name())?));
} }
} }
State::Boolean => return Err(format_err!("Unexpected tag inside of <boolean> tag: {}", reader.decode(event.name()))) State::Boolean => return Err(format_err!("Unexpected tag inside of <boolean> tag: {}", reader.decode(event.name())?))
}, },
Event::Empty(event) => match state { Event::Empty(event) => match state {
State::Head => { State::Head => {
@ -194,7 +194,7 @@ pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>
} else if event.name() == b"link" { } else if event.name() == b"link" {
// no op // no op
} else { } else {
return Err(format_err!("Expecting <variable> or <link> tag, found {}", reader.decode(event.name()))); return Err(format_err!("Expecting <variable> or <link> tag, found {}", reader.decode(event.name())?));
} }
}, },
State::AfterHead => { State::AfterHead => {
@ -204,10 +204,10 @@ pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>
Box::new(empty()), Box::new(empty()),
))) )))
} else { } else {
return Err(format_err!("Unexpected autoclosing tag <{}>", reader.decode(event.name()))) return Err(format_err!("Unexpected autoclosing tag <{}>", reader.decode(event.name())?))
} }
} }
_ => return Err(format_err!("Unexpected autoclosing tag <{}>", reader.decode(event.name()))) _ => return Err(format_err!("Unexpected autoclosing tag <{}>", reader.decode(event.name())?))
}, },
Event::Text(event) => { Event::Text(event) => {
let value = event.unescaped()?; let value = event.unescaped()?;
@ -218,10 +218,10 @@ pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>
} else if value.as_ref() == b"false" { } else if value.as_ref() == b"false" {
Ok(QueryResult::Boolean(false)) Ok(QueryResult::Boolean(false))
} else { } else {
Err(format_err!("Unexpected boolean value. Found {}", reader.decode(&value))) Err(format_err!("Unexpected boolean value. Found {}", reader.decode(&value)?))
}; };
} }
_ => Err(format_err!("Unexpected textual value found: {}", reader.decode(&value))) _ => Err(format_err!("Unexpected textual value found: {}", reader.decode(&value)?))
}; };
}, },
Event::End(_) => if let State::Head = state { Event::End(_) => if let State::Head = state {
@ -247,6 +247,12 @@ impl<R: BufRead> Iterator for ResultsIterator<R> {
type Item = Result<Vec<Option<Term>>>; type Item = Result<Vec<Option<Term>>>;
fn next(&mut self) -> Option<Result<Vec<Option<Term>>>> { fn next(&mut self) -> Option<Result<Vec<Option<Term>>>> {
self.read_next().transpose()
}
}
impl<R: BufRead> ResultsIterator<R> {
fn read_next(&mut self) -> Result<Option<Vec<Option<Term>>>> {
enum State { enum State {
Start, Start,
Result, Result,
@ -266,19 +272,15 @@ impl<R: BufRead> Iterator for ResultsIterator<R> {
let mut lang = None; let mut lang = None;
let mut datatype = None; let mut datatype = None;
loop { loop {
let (ns, event) = match self let (ns, event) = self
.reader .reader
.read_namespaced_event(&mut self.buffer, &mut self.namespace_buffer) .read_namespaced_event(&mut self.buffer, &mut self.namespace_buffer)?;
{
Ok(v) => v,
Err(error) => return Some(Err(error.into())),
};
if let Some(ns) = ns { if let Some(ns) = ns {
if ns != b"http://www.w3.org/2005/sparql-results#".as_ref() { if ns != b"http://www.w3.org/2005/sparql-results#".as_ref() {
return Some(Err(format_err!( return Err(format_err!(
"Unexpected namespace found in RDF/XML query result: {}", "Unexpected namespace found in RDF/XML query result: {}",
self.reader.decode(ns) self.reader.decode(ns)?
))); ));
} }
} }
match event { match event {
@ -287,10 +289,10 @@ impl<R: BufRead> Iterator for ResultsIterator<R> {
if event.name() == b"result" { if event.name() == b"result" {
state = State::Result; state = State::Result;
} else { } else {
return Some(Err(format_err!( return Err(format_err!(
"Expecting <result>, found {}", "Expecting <result>, found {}",
self.reader.decode(event.name()) self.reader.decode(event.name())?
))); ));
} }
} }
State::Result => { State::Result => {
@ -300,29 +302,26 @@ impl<R: BufRead> Iterator for ResultsIterator<R> {
.filter_map(|attr| attr.ok()) .filter_map(|attr| attr.ok())
.find(|attr| attr.key == b"name") .find(|attr| attr.key == b"name")
{ {
Some(attr) => match attr.unescaped_value() { Some(attr) => current_var = Some(attr.unescaped_value()?.to_vec()),
Ok(var) => current_var = Some(var.to_vec()),
Err(error) => return Some(Err(error.into())),
},
None => { None => {
return Some(Err(format_err!( return Err(format_err!(
"No name attribute found for the <binding> tag" "No name attribute found for the <binding> tag"
))); ));
} }
} }
state = State::Binding; state = State::Binding;
} else { } else {
return Some(Err(format_err!( return Err(format_err!(
"Expecting <binding>, found {}", "Expecting <binding>, found {}",
self.reader.decode(event.name()) self.reader.decode(event.name())?
))); ));
} }
} }
State::Binding => { State::Binding => {
if term.is_some() { if term.is_some() {
return Some(Err(format_err!( return Err(format_err!(
"There is already a value for the current binding" "There is already a value for the current binding"
))); ));
} }
if event.name() == b"uri" { if event.name() == b"uri" {
state = State::Uri; state = State::Uri;
@ -332,43 +331,30 @@ impl<R: BufRead> Iterator for ResultsIterator<R> {
for attr in event.attributes() { for attr in event.attributes() {
if let Ok(attr) = attr { if let Ok(attr) = attr {
if attr.key == b"xml:lang" { if attr.key == b"xml:lang" {
match attr.unescape_and_decode_value(&self.reader) { lang = Some(attr.unescape_and_decode_value(&self.reader)?);
Ok(val) => lang = Some(val),
Err(error) => return Some(Err(error.into())),
}
} else if attr.key == b"datatype" { } else if attr.key == b"datatype" {
match attr.unescaped_value() { datatype = Some(NamedNode::parse(
Ok(val) => { attr.unescape_and_decode_value(&self.reader)?,
match NamedNode::parse( )?);
self.reader.decode(&val).to_string(),
) {
Ok(iri) => datatype = Some(iri),
Err(error) => return Some(Err(error)),
}
}
Err(error) => return Some(Err(error.into())),
}
} }
} }
} }
state = State::Literal; state = State::Literal;
} else { } else {
return Some(Err(format_err!( return Err(format_err!(
"Expecting <uri>, <bnode> or <literal> found {}", "Expecting <uri>, <bnode> or <literal> found {}",
self.reader.decode(event.name()) self.reader.decode(event.name())?
))); ));
} }
} }
_ => (), _ => (),
}, },
Event::Text(event) => match event.unescaped() { Event::Text(event) => {
Ok(data) => match state { let data = event.unescaped()?;
State::Uri => match NamedNode::parse(self.reader.decode(&data)) { match state {
Ok(uri) => { State::Uri => {
term = Some(uri.into()); term = Some(NamedNode::parse(self.reader.decode(&data)?)?.into())
} }
Err(error) => return Some(Err(error)),
},
State::BNode => { State::BNode => {
term = Some( term = Some(
self.bnodes_map self.bnodes_map
@ -379,33 +365,33 @@ impl<R: BufRead> Iterator for ResultsIterator<R> {
) )
} }
State::Literal => { State::Literal => {
let value = self.reader.decode(&data).to_string(); term = Some(
term = Some(build_literal(value, &lang, &datatype).into()); build_literal(self.reader.decode(&data)?, &lang, &datatype).into(),
);
} }
_ => { _ => {
return Some(Err(format_err!( return Err(format_err!(
"Unexpected textual value found: {}", "Unexpected textual value found: {}",
self.reader.decode(&data) self.reader.decode(&data)?
))); ));
}
}
} }
},
Err(error) => return Some(Err(error.into())),
},
Event::End(_) => match state { Event::End(_) => match state {
State::Start => state = State::End, State::Start => state = State::End,
State::Result => return Some(Ok(new_bindings)), State::Result => return Ok(Some(new_bindings)),
State::Binding => { State::Binding => {
match (&current_var, &term) { match (&current_var, &term) {
(Some(var), Some(term)) => { (Some(var), Some(term)) => {
new_bindings[self.mapping[var]] = Some(term.clone()) new_bindings[self.mapping[var]] = Some(term.clone())
} }
(Some(var), None) => { (Some(var), None) => {
return Some(Err(format_err!( return Err(format_err!(
"No variable found for variable {}", "No variable found for variable {}",
self.reader.decode(&var) self.reader.decode(&var)?
))); ));
} }
_ => return Some(Err(format_err!("No name found for <binding> tag"))), _ => return Err(format_err!("No name found for <binding> tag")),
} }
term = None; term = None;
state = State::Result; state = State::Result;
@ -420,7 +406,7 @@ impl<R: BufRead> Iterator for ResultsIterator<R> {
} }
_ => (), _ => (),
}, },
Event::Eof => return None, Event::Eof => return Ok(None),
_ => (), _ => (),
} }
} }

@ -10,7 +10,6 @@ use rudf::{
DatasetSyntax, FileSyntax, GraphSyntax, MemoryRepository, Repository, RepositoryConnection, DatasetSyntax, FileSyntax, GraphSyntax, MemoryRepository, Repository, RepositoryConnection,
RocksDbRepository, RocksDbRepository,
}; };
use std::fmt::Write;
use std::io::{BufReader, Read}; use std::io::{BufReader, Read};
use std::sync::Arc; use std::sync::Arc;
@ -143,11 +142,13 @@ fn evaluate_sparql_query<R: RepositoryConnection>(
) -> Response { ) -> Response {
//TODO: stream //TODO: stream
match connection.prepare_query(query, None) { match connection.prepare_query(query, None) {
Ok(query) => match query.exec().unwrap() { Ok(query) => {
QueryResult::Graph(triples) => { let results = query.exec().unwrap();
if let QueryResult::Graph(_) = results {
let supported_formats = [ let supported_formats = [
GraphSyntax::NTriples.media_type(), GraphSyntax::NTriples.media_type(),
GraphSyntax::Turtle.media_type(), GraphSyntax::Turtle.media_type(),
GraphSyntax::RdfXml.media_type(),
]; ];
let format = if let Some(accept) = request.header("Accept") { let format = if let Some(accept) = request.header("Accept") {
if let Some(media_type) = if let Some(media_type) =
@ -165,13 +166,12 @@ fn evaluate_sparql_query<R: RepositoryConnection>(
} else { } else {
GraphSyntax::NTriples GraphSyntax::NTriples
}; };
let mut result = String::default();
for triple in triples { Response::from_data(
writeln!(&mut result, "{}", triple.unwrap()).unwrap() format.media_type(),
} results.write_graph(Vec::default(), format).unwrap(),
Response::from_data(format.media_type(), result.into_bytes()) )
} } else {
result => {
let supported_formats = [ let supported_formats = [
QueryResultSyntax::Xml.media_type(), QueryResultSyntax::Xml.media_type(),
QueryResultSyntax::Json.media_type(), QueryResultSyntax::Json.media_type(),
@ -190,15 +190,15 @@ fn evaluate_sparql_query<R: RepositoryConnection>(
.with_status_code(415); .with_status_code(415);
} }
} else { } else {
QueryResultSyntax::Xml QueryResultSyntax::Json
}; };
Response::from_data( Response::from_data(
format.media_type(), format.media_type(),
result.write(Vec::default(), format).unwrap(), results.write(Vec::default(), format).unwrap(),
) )
} }
}, }
Err(error) => Response::text(error.to_string()).with_status_code(400), Err(error) => Response::text(error.to_string()).with_status_code(400),
} }
} }

Loading…
Cancel
Save