Add rdf_to_object serialization algorithm

This commit is contained in:
asonix 2022-12-12 20:38:36 -06:00
parent 91e35246ac
commit 3982ddfcfa
3 changed files with 245 additions and 1 deletions

View file

@ -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
View 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
View 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)
}
}
}