Adds RDF/XML server output and upgrades quick_xml

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

@ -19,8 +19,8 @@ travis-ci = { repository = "Tpt/rudf" }
lazy_static = "1"
rocksdb = { version = "0.12", optional = true }
uuid = { version = "0.7", features = ["v4"] }
byteorder = {version="1", features = ["i128"] }
quick-xml = "0.15"
byteorder = { version = "1", features = ["i128"] }
quick-xml = { version = "0.16", features = ["failure"] }
ordered-float = "1"
num-traits = "0.2"
rust_decimal = "1"

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

@ -1,8 +1,10 @@
use crate::model::*;
use crate::sparql::json_results::write_json_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 quick_xml::events::*;
use quick_xml::Writer;
use std::fmt;
use std::io::{BufRead, Write};
use uuid::Uuid;
@ -31,6 +33,97 @@ impl<'a> QueryResult<'a> {
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

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

@ -11,6 +11,6 @@ Rudf based SPARQL server
edition = "2018"
[dependencies]
rudf = {path = "../lib", features=["rocksdb"]}
rudf = {path = "../lib", features = ["rocksdb"] }
clap = "2"
rouille = "3"

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

Loading…
Cancel
Save