|
|
|
//! Implementation of [SPARQL Query Results XML Format](https://www.w3.org/TR/sparql11-results-json/)
|
|
|
|
|
|
|
|
use crate::model::*;
|
|
|
|
use crate::sparql::model::*;
|
|
|
|
use crate::Result;
|
|
|
|
use failure::format_err;
|
|
|
|
use std::io::Write;
|
|
|
|
|
|
|
|
pub fn write_json_results<W: Write>(results: QueryResult<'_>, mut sink: W) -> Result<W> {
|
|
|
|
match results {
|
|
|
|
QueryResult::Boolean(value) => {
|
|
|
|
sink.write_all(b"{\"head\":{},\"boolean\":")?;
|
|
|
|
sink.write_all(if value { b"true" } else { b"false" })?;
|
|
|
|
sink.write_all(b"}")?;
|
|
|
|
}
|
|
|
|
QueryResult::Bindings(bindings) => {
|
|
|
|
let (variables, results) = bindings.destruct();
|
|
|
|
sink.write_all(b"{\"head\":{\"vars\":[")?;
|
|
|
|
let mut start_vars = true;
|
|
|
|
for variable in &variables {
|
|
|
|
if start_vars {
|
|
|
|
start_vars = false;
|
|
|
|
} else {
|
|
|
|
sink.write_all(b",")?;
|
|
|
|
}
|
|
|
|
write_escaped_json_string(variable.name()?, &mut sink)?;
|
|
|
|
}
|
|
|
|
sink.write_all(b"]},\"results\":{\"bindings\":[")?;
|
|
|
|
let mut start_bindings = true;
|
|
|
|
for result in results {
|
|
|
|
if start_bindings {
|
|
|
|
start_bindings = false;
|
|
|
|
} else {
|
|
|
|
sink.write_all(b",")?;
|
|
|
|
}
|
|
|
|
sink.write_all(b"{")?;
|
|
|
|
|
|
|
|
let result = result?;
|
|
|
|
let mut start_binding = true;
|
|
|
|
for (i, value) in result.into_iter().enumerate() {
|
|
|
|
if let Some(term) = value {
|
|
|
|
if start_binding {
|
|
|
|
start_binding = false;
|
|
|
|
} else {
|
|
|
|
sink.write_all(b",")?;
|
|
|
|
}
|
|
|
|
write_escaped_json_string(variables[i].name()?, &mut sink)?;
|
|
|
|
match term {
|
|
|
|
Term::NamedNode(uri) => {
|
|
|
|
sink.write_all(b":{\"type\":\"uri\",\"value\":")?;
|
|
|
|
write_escaped_json_string(uri.as_str(), &mut sink)?;
|
|
|
|
sink.write_all(b"}")?;
|
|
|
|
}
|
|
|
|
Term::BlankNode(bnode) => {
|
|
|
|
sink.write_all(b":{\"type\":\"bnode\",\"value\":")?;
|
|
|
|
write!(sink, "{}", bnode.as_str())?;
|
|
|
|
sink.write_all(b"}")?;
|
|
|
|
}
|
|
|
|
Term::Literal(literal) => {
|
|
|
|
sink.write_all(b":{\"type\":\"literal\",\"value\":")?;
|
|
|
|
write_escaped_json_string(literal.value(), &mut sink)?;
|
|
|
|
if let Some(language) = literal.language() {
|
|
|
|
sink.write_all(b",\"xml:lang\":")?;
|
|
|
|
write_escaped_json_string(language, &mut sink)?;
|
|
|
|
} else if !literal.is_plain() {
|
|
|
|
sink.write_all(b",\"datatype\":")?;
|
|
|
|
write_escaped_json_string(
|
|
|
|
literal.datatype().as_str(),
|
|
|
|
&mut sink,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
sink.write_all(b"}")?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sink.write_all(b"}")?;
|
|
|
|
}
|
|
|
|
sink.write_all(b"]}}")?;
|
|
|
|
}
|
|
|
|
QueryResult::Graph(_) => {
|
|
|
|
return Err(format_err!(
|
|
|
|
"Graphs could not be formatted to SPARQL query results XML format"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(sink)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_escaped_json_string(s: &str, sink: &mut impl Write) -> Result<()> {
|
|
|
|
sink.write_all(b"\"")?;
|
|
|
|
for c in s.chars() {
|
|
|
|
match c {
|
|
|
|
'\\' => sink.write_all(b"\\\\"),
|
|
|
|
'"' => sink.write_all(b"\\\""),
|
|
|
|
c => {
|
|
|
|
if c < char::from(32) {
|
|
|
|
match c {
|
|
|
|
'\u{08}' => sink.write_all(b"\\b"),
|
|
|
|
'\u{0C}' => sink.write_all(b"\\f"),
|
|
|
|
'\n' => sink.write_all(b"\\n"),
|
|
|
|
'\r' => sink.write_all(b"\\r"),
|
|
|
|
'\t' => sink.write_all(b"\\t"),
|
|
|
|
c => {
|
|
|
|
let mut c = c as u8;
|
|
|
|
let mut result = [b'\\', b'u', 0, 0, 0, 0];
|
|
|
|
for i in (2..6).rev() {
|
|
|
|
let ch = c % 16;
|
|
|
|
result[i] = ch + if ch < 10 { b'0' } else { b'A' };
|
|
|
|
c /= 16;
|
|
|
|
}
|
|
|
|
sink.write_all(&result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
write!(sink, "{}", c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}?;
|
|
|
|
}
|
|
|
|
sink.write_all(b"\"")?;
|
|
|
|
Ok(())
|
|
|
|
}
|