Add rdf_to_object serialization algorithm
This commit is contained in:
parent
91e35246ac
commit
3982ddfcfa
|
@ -1,5 +1,5 @@
|
|||
[workspace]
|
||||
members = ["./normalization"]
|
||||
members = ["./normalization", "./serialization"]
|
||||
|
||||
[patch.crates-io]
|
||||
rdf-types = { git = "https://github.com/asonix/rdf-types", branch = "asonix/fix-string-literal-display" }
|
||||
|
|
18
serialization/Cargo.toml
Normal file
18
serialization/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "json-ld-serialization"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
contextual = "0.1.3"
|
||||
iref = "2.2.0"
|
||||
json-ld = "0.9.1"
|
||||
json-ld-syntax = "0.9.1"
|
||||
json-number = "0.4.1"
|
||||
json-syntax = "0.8.14"
|
||||
locspan = "0.7.9"
|
||||
rdf-types = "0.12.4"
|
||||
smallvec = "1.10.0"
|
||||
static-iref = "2.0.0"
|
226
serialization/src/lib.rs
Normal file
226
serialization/src/lib.rs
Normal file
|
@ -0,0 +1,226 @@
|
|||
use iref::Iri;
|
||||
use json_ld::{
|
||||
object::{Literal, LiteralString},
|
||||
rdf::RdfDirection,
|
||||
Direction, Id, LangString, LenientLanguageTagBuf, Node, Object, ValidId as Subject, Value,
|
||||
};
|
||||
use json_ld_syntax::Parse;
|
||||
use locspan::Meta;
|
||||
use rdf_types::{BlankIdVocabulary, IriVocabulary, Quad, Vocabulary};
|
||||
use smallvec::SmallVec;
|
||||
use static_iref::iri;
|
||||
|
||||
pub(crate) type QuadSubject<N> =
|
||||
Subject<<N as IriVocabulary>::Iri, <N as BlankIdVocabulary>::BlankId>;
|
||||
pub(crate) type QuadValue<N> =
|
||||
json_ld::rdf::Value<<N as IriVocabulary>::Iri, <N as BlankIdVocabulary>::BlankId>;
|
||||
|
||||
pub(crate) type NormalizingQuad<N> =
|
||||
Quad<QuadSubject<N>, QuadSubject<N>, QuadValue<N>, QuadSubject<N>>;
|
||||
|
||||
type SerializedObject<N, M> =
|
||||
Object<<N as IriVocabulary>::Iri, <N as BlankIdVocabulary>::BlankId, M>;
|
||||
|
||||
fn expect_iri<N>(id: &N::Iri, iri: Iri<'_>, vocabulary: &N) -> bool
|
||||
where
|
||||
N: Vocabulary,
|
||||
{
|
||||
get_iri(id, vocabulary) == iri
|
||||
}
|
||||
|
||||
fn get_iri<'a, N>(id: &'a N::Iri, vocabulary: &'a N) -> Iri<'a>
|
||||
where
|
||||
N: Vocabulary,
|
||||
{
|
||||
vocabulary.iri(id).expect("Id in vocabulary")
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidJson;
|
||||
|
||||
pub fn rdf_to_object<N, M>(
|
||||
value: QuadValue<N>,
|
||||
meta: M,
|
||||
rdf_direction: RdfDirection,
|
||||
use_native_types: bool,
|
||||
vocabulary: &N,
|
||||
) -> Result<SerializedObject<N, M>, InvalidJson>
|
||||
where
|
||||
N: Vocabulary,
|
||||
N::Iri: Clone,
|
||||
M: Clone,
|
||||
{
|
||||
match value {
|
||||
// step 1
|
||||
json_ld::rdf::Value::Reference(subject) => Ok(Object::Node(Box::new(Node::with_id(
|
||||
json_ld_syntax::Entry::new(meta.clone(), Meta(Id::Valid(subject), meta)),
|
||||
)))),
|
||||
// step 2
|
||||
json_ld::rdf::Value::Literal(literal) => {
|
||||
// step 2.1
|
||||
let result: Object<N::Iri, N::BlankId, M>;
|
||||
|
||||
// step 2.2
|
||||
let converted_value: Value<N::Iri, M>;
|
||||
|
||||
// step 2.3 is no-op, type will be included
|
||||
|
||||
converted_value = match literal {
|
||||
// step 2.4
|
||||
// step 2.4.1
|
||||
rdf_types::Literal::TypedString(value, value_ty)
|
||||
if use_native_types
|
||||
&& expect_iri(&value_ty, iri!("xsd:string"), vocabulary) =>
|
||||
{
|
||||
// TODO: un-clone this line
|
||||
let new_value = value.as_str().to_string();
|
||||
|
||||
Value::Literal(
|
||||
Literal::String(LiteralString::Inferred(new_value)),
|
||||
Some(value_ty),
|
||||
)
|
||||
}
|
||||
// step 2.4.2
|
||||
rdf_types::Literal::TypedString(value, value_ty)
|
||||
if use_native_types
|
||||
&& expect_iri(&value_ty, iri!("xsd:boolean"), vocabulary) =>
|
||||
{
|
||||
if value.as_str() == "true" {
|
||||
Value::Literal(Literal::Boolean(true), Some(value_ty))
|
||||
} else if value.as_str() == "false" {
|
||||
Value::Literal(Literal::Boolean(false), Some(value_ty))
|
||||
} else {
|
||||
// TODO: un-clone this line
|
||||
let new_value = value.as_str().to_string();
|
||||
|
||||
Value::Literal(
|
||||
Literal::String(LiteralString::Inferred(new_value)),
|
||||
Some(value_ty),
|
||||
)
|
||||
}
|
||||
}
|
||||
// step 2.4.3
|
||||
rdf_types::Literal::TypedString(value, value_ty)
|
||||
if use_native_types
|
||||
&& (expect_iri(&value_ty, iri!("xsd:integer"), vocabulary)
|
||||
|| expect_iri(&value_ty, iri!("xsd:double"), vocabulary)) =>
|
||||
{
|
||||
if let Ok(number_buf) =
|
||||
json_number::SmallNumberBuf::new(SmallVec::from_slice(value.as_bytes()))
|
||||
{
|
||||
Value::Literal(Literal::Number(number_buf), Some(value_ty))
|
||||
} else {
|
||||
// TODO: un-clone this line
|
||||
let new_value = value.as_str().to_string();
|
||||
|
||||
Value::Literal(
|
||||
Literal::String(LiteralString::Inferred(new_value)),
|
||||
Some(value_ty),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// step 2.5
|
||||
rdf_types::Literal::TypedString(value, value_ty)
|
||||
if expect_iri(&value_ty, iri!("rdf:JSON"), vocabulary) =>
|
||||
{
|
||||
let meta = meta.clone();
|
||||
let value = json_syntax::Value::parse_str(value.as_str(), |_| meta.clone())
|
||||
.map_err(|_| InvalidJson)?;
|
||||
Value::Json(value)
|
||||
}
|
||||
|
||||
// step 2.6
|
||||
rdf_types::Literal::TypedString(value, value_ty)
|
||||
if get_iri(&value_ty, vocabulary)
|
||||
.as_str()
|
||||
.starts_with("https://www.w3.org/ns/i18n#")
|
||||
&& rdf_direction == RdfDirection::I18nDatatype =>
|
||||
{
|
||||
// TODO: un-clone this line
|
||||
let new_value = value.as_str().to_string();
|
||||
|
||||
// step 2.6.2
|
||||
let iri = get_iri(&value_ty, vocabulary);
|
||||
let lang = iri
|
||||
.as_str()
|
||||
.trim_start_matches("https://www.w3.org/ns/i18n#");
|
||||
let (lang, direction) = lang.split_once("_").expect("Invalid language");
|
||||
|
||||
let language = if lang.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(LenientLanguageTagBuf::new(lang.to_string()).0)
|
||||
};
|
||||
|
||||
// step 2.6.3
|
||||
let direction = if direction == "ltr" {
|
||||
Some(Direction::Ltr)
|
||||
} else if direction == "rtl" {
|
||||
Some(Direction::Rtl)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// step 2.6.1
|
||||
match LangString::new(LiteralString::Inferred(new_value), language, direction) {
|
||||
Ok(lang_string) => Value::LangString(lang_string),
|
||||
Err(literal_string) => {
|
||||
Value::Literal(Literal::String(literal_string), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
// step 2.7
|
||||
rdf_types::Literal::LangString(value, language_tag_buf) => {
|
||||
// TODO: un-clone this line
|
||||
let new_value = value.as_str().to_string();
|
||||
|
||||
match LangString::new(
|
||||
LiteralString::Inferred(new_value),
|
||||
Some(language_tag_buf.into()),
|
||||
None,
|
||||
) {
|
||||
Ok(lang_string) => Value::LangString(lang_string),
|
||||
Err(literal_string) => {
|
||||
Value::Literal(Literal::String(literal_string), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 2.8
|
||||
rdf_types::Literal::TypedString(value, value_ty)
|
||||
if !expect_iri(&value_ty, iri!("xsd:string"), vocabulary) =>
|
||||
{
|
||||
// TODO: un-clone this line
|
||||
let new_value = value.as_str().to_string();
|
||||
|
||||
Value::Literal(
|
||||
Literal::String(LiteralString::Inferred(new_value)),
|
||||
Some(value_ty),
|
||||
)
|
||||
}
|
||||
|
||||
// step 2.2, all remaining matches
|
||||
rdf_types::Literal::TypedString(value, _) => {
|
||||
// TODO: un-clone this line
|
||||
let new_value = value.as_str().to_string();
|
||||
|
||||
Value::Literal(Literal::String(LiteralString::Inferred(new_value)), None)
|
||||
}
|
||||
rdf_types::Literal::String(value) => {
|
||||
// TODO: un-clone this line
|
||||
let new_value = value.as_str().to_string();
|
||||
|
||||
Value::Literal(Literal::String(LiteralString::Inferred(new_value)), None)
|
||||
}
|
||||
};
|
||||
|
||||
// step 2.9
|
||||
result = Object::Value(converted_value);
|
||||
|
||||
// step 2.10 is no-op, type is included already
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue