strixonomy_owl/
serialize.rs1use crate::error::{OwlError, Result};
4use curie::PrefixMapping;
5use horned_owl::model::{RcAnnotatedComponent, RcStr};
6use horned_owl::ontology::component_mapped::ComponentMappedOntology;
7use std::collections::BTreeMap;
8use std::io::Cursor;
9
10pub fn serialize_rdf_xml(
12 ont: &ComponentMappedOntology<RcStr, RcAnnotatedComponent>,
13) -> Result<String> {
14 let mut buf = Vec::new();
15 horned_owl::io::rdf::writer::write(&mut buf, ont)
16 .map_err(|e| OwlError::SerializeFailed(e.to_string()))?;
17 String::from_utf8(buf).map_err(|e| OwlError::SerializeFailed(e.to_string()))
18}
19
20pub fn serialize_owl_xml(
24 ont: &ComponentMappedOntology<RcStr, RcAnnotatedComponent>,
25 namespaces: &BTreeMap<String, String>,
26) -> Result<String> {
27 let mut mapping = PrefixMapping::default();
28 for (prefix, iri) in namespaces {
29 if prefix.is_empty() {
30 continue;
31 }
32 let _ = mapping.add_prefix(prefix, iri);
33 }
34 for (prefix, iri) in [
35 ("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"),
36 ("rdfs", "http://www.w3.org/2000/01/rdf-schema#"),
37 ("owl", "http://www.w3.org/2002/07/owl#"),
38 ("xsd", "http://www.w3.org/2001/XMLSchema#"),
39 ] {
40 let _ = mapping.add_prefix(prefix, iri);
41 }
42
43 let mut buf = Vec::new();
44 horned_owl::io::owx::writer::write(&mut buf, ont, Some(&mapping))
45 .map_err(|e| OwlError::SerializeFailed(e.to_string()))?;
46 String::from_utf8(buf).map_err(|e| OwlError::SerializeFailed(e.to_string()))
47}
48
49pub fn load_rdf_xml_ontology(
51 source: &str,
52) -> Result<(ComponentMappedOntology<RcStr, RcAnnotatedComponent>, bool)> {
53 let mut cursor = Cursor::new(source.as_bytes());
54 let parse = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
56 horned_owl::io::rdf::reader::read(&mut cursor, Default::default())
57 }));
58 let (concrete, incomplete) = match parse {
59 Ok(Ok(pair)) => pair,
60 Ok(Err(e)) => return Err(OwlError::LoadFailed(e.to_string())),
61 Err(payload) => {
62 let msg = if let Some(s) = payload.downcast_ref::<String>() {
63 s.clone()
64 } else if let Some(s) = payload.downcast_ref::<&str>() {
65 (*s).to_string()
66 } else {
67 "RDF/XML parser panicked on malformed input".into()
68 };
69 return Err(OwlError::LoadFailed(msg));
70 }
71 };
72 let mapped: ComponentMappedOntology<RcStr, RcAnnotatedComponent> = concrete.into();
73 Ok((mapped, !incomplete.is_complete()))
74}
75
76#[allow(clippy::type_complexity)]
81pub fn load_owl_xml_ontology(
82 source: &str,
83) -> Result<(ComponentMappedOntology<RcStr, RcAnnotatedComponent>, BTreeMap<String, String>, bool)>
84{
85 use horned_owl::io::owx::reader::read as read_owx;
86 use horned_owl::ontology::set::SetOntology;
87
88 let mut cursor = Cursor::new(source.as_bytes());
89 let (set_ont, mapping): (SetOntology<RcStr>, _) = read_owx(&mut cursor, Default::default())
90 .map_err(|e| OwlError::LoadFailed(e.to_string()))?;
91 let mut namespaces = BTreeMap::new();
92 for (prefix, iri) in mapping.mappings() {
93 namespaces.insert(prefix.clone(), iri.clone());
94 }
95 let mapped: ComponentMappedOntology<RcStr, RcAnnotatedComponent> = set_ont.into();
96 let incomplete = match serialize_rdf_xml(&mapped) {
97 Ok(rdf) => load_rdf_xml_ontology(&rdf).map(|(_, inc)| inc).unwrap_or(true),
98 Err(_) => true,
99 };
100 Ok((mapped, namespaces, incomplete))
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn rdf_xml_roundtrip_preserves_department_label() {
109 let src = include_str!("../../../examples/protege-roundtrip/organization.owl");
110 let (ont, incomplete) = load_rdf_xml_ontology(src).expect("load");
111 assert!(!incomplete);
112 let out = serialize_rdf_xml(&ont).expect("serialize");
113 let (again, _) = load_rdf_xml_ontology(&out).expect("reload");
114 let bridge = crate::bridge_ontology(again, "doc", &out, &BTreeMap::new());
115 assert!(bridge.entities.iter().any(|e| e.short_name == "Department"));
116 assert!(bridge.entities.iter().any(|e| e.labels.iter().any(|l| l == "Department")));
117 }
118
119 #[test]
120 fn owl_xml_roundtrip_preserves_department() {
121 let src = include_str!("../../../examples/protege-roundtrip/example.owx");
122 let (ont, ns, incomplete) = load_owl_xml_ontology(src).expect("load");
123 assert!(!incomplete);
124 let out = serialize_owl_xml(&ont, &ns).expect("serialize");
125 let (again, _, _) = load_owl_xml_ontology(&out).expect("reload");
126 let bridge = crate::bridge_ontology(again, "doc", &out, &BTreeMap::new());
127 assert!(bridge.entities.iter().any(|e| e.short_name == "Department"));
128 }
129}