use serde::Deserialize;
use serde::de::Deserializer;
use serde::de::Error as _;
use serde_json::Value as JsonValue;
use crate::ValueArray;
use crate::ValueHash;
use crate::errors::Error;
use super::{Value, ValueData};
impl<'de> Deserialize<'de> for ValueData
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let json = serde_json::Value::deserialize(deserializer)?;
let value = lift_json(json).map_err(D::Error::custom)?;
Ok(value.value)
}
}
fn is_typed_value(map: &serde_json::Map<String, serde_json::Value>) -> bool
{
(map.len() == 2
|| (map.len() == 3
&& map.get("type").is_some_and(|x| match x
{
serde_json::Value::String(string) => string == "literal",
_ => false,
})))
&& map
.get("datatype")
.is_some_and(|x| matches!(x, serde_json::Value::String(_)))
&& map.contains_key("value")
}
fn lift_json(j: JsonValue) -> Result<Value, crate::Error>
{
Ok(match j
{
JsonValue::Null => ValueData::Null.into(),
JsonValue::Bool(b) => b.into(),
JsonValue::Number(n) =>
{
if let Some(i) = n.as_i64()
{
ValueData::Integer64(i).into()
}
else if let Some(f) = n.as_f64()
{
ValueData::Float64(f).into()
}
else
{
return Err(Error::UnsupportedNumber);
}
}
JsonValue::String(s) => s.into(),
JsonValue::Array(arr) =>
{
let values: ValueArray = arr.into_iter().map(lift_json).collect::<Result<_, _>>()?;
values.into()
}
JsonValue::Object(map) =>
{
if is_typed_value(&map)
{
return serde_json::from_value(JsonValue::Object(map)).map_err(|e| e.into());
}
let values: ValueHash = map
.into_iter()
.map(|(k, v)| lift_json(v).map(|val| (k, val)))
.collect::<Result<_, _>>()?;
values.into()
}
})
}
#[derive(Deserialize)]
struct RawValue
{
#[serde(default)]
datatype: Option<String>,
value: ValueData,
}
fn guess_datatype(value: &ValueData) -> &'static str
{
match value
{
ValueData::Null => "http://www.w3.org/2001/XMLSchema#nil",
ValueData::Boolean(_) => "http://www.w3.org/2001/XMLSchema#bool",
ValueData::Integer64(_) => "http://www.w3.org/2001/XMLSchema#long",
ValueData::Float64(_) => "http://www.w3.org/2001/XMLSchema#float64",
ValueData::String(_) => "http://www.w3.org/2001/XMLSchema#string",
ValueData::Map(_) => "http://askco.re/datatype#valuehash",
ValueData::Array(_) => "http://askco.re/datatype#valuelist",
}
}
impl<'de> Deserialize<'de> for Value
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let raw = RawValue::deserialize(deserializer)?;
let datatype = match raw.datatype
{
Some(dt) => dt,
None => guess_datatype(&raw.value).to_string(),
};
Ok(Value {
datatype,
value: raw.value,
})
}
}
#[cfg(test)]
mod tests
{
use super::*;
use crate::{ValueArray, ValueHash, test::validate_dataset_0};
#[test]
fn test_agent_deserialization()
{
let raw_json = serde_json::json!({
"datatype": "http://askco.re/datatype#valuelist",
"type": "literal",
"value": [
{
"datatype": "http://askco.re/datatype#valuehash",
"value": {
"object_uri": {
"datatype": "http://www.w3.org/2001/XMLSchema#anyURI",
"value": "http://example.org/agent/0"
},
"properties": {
"datatype": "http://askco.re/datatype#valuehash",
"value": {
"http://askco.re/agent#agent_type": {
"datatype": "http://www.w3.org/2001/XMLSchema#anyURI",
"value": "http://askco.re/agent#uav"
},
"http://askco.re/agent#frame": {
"datatype": "http://www.w3.org/2001/XMLSchema#string",
"value": "agent0"
},
"http://xmlns.com/foaf/0.1/name": {
"datatype": "http://www.w3.org/2001/XMLSchema#string",
"value": "test agent"
}
}
},
"type_uri": {
"datatype": "http://www.w3.org/2001/XMLSchema#anyURI",
"value": "http://askco.re/agent#uav"
}
}
}
]
});
let agents: Value = serde_json::from_value(raw_json).unwrap();
let agents: ValueArray = agents.to_owned().try_into().unwrap();
let agent_0: ValueHash = agents.into_iter().next().unwrap().try_into().unwrap();
crate::test::validate_agent_0(agent_0);
}
#[test]
fn test_dataset_deserialization()
{
let raw_json = serde_json::json!({
"datatype": "http://askco.re/datatype#valuehash",
"value": {
"object_uri": {
"datatype": "http://www.w3.org/2001/XMLSchema#anyURI",
"value": "http://example.org/dataset/0"
},
"type_uri": {
"datatype": "http://www.w3.org/2001/XMLSchema#anyURI",
"value": "http://askco.re/dataset#point_cloud_dataset"
},
"properties": {
"datatype": "http://askco.re/datatype#valuehash",
"value": {
"http://askco.re/dataset#content_type": {
"datatype": "http://www.w3.org/2001/XMLSchema#anyURI",
"value": "http://askco.re/sensing#point_cloud"
},
"http://askco.re/sensing#point_density": {
"datatype": "http://askco.re/datatype#quantityDecimal",
"value": {
"value": 10,
"unit": "point/m^2"
}
},
"http://www.opengis.net/ont/geosparql#hasGeometry": {
"datatype": "http://www.opengis.net/ont/geosparql#Geometry",
"value": {
"type": "Polygon",
"coordinates": [
[
[30, 10],
[40, 40],
[20, 40],
[10, 20],
[30, 10]
]
]
}
}
}
}
}
});
let dataset_0: Value = serde_json::from_value(raw_json).unwrap();
let dataset_0: ValueHash = dataset_0.to_owned().try_into().unwrap();
validate_dataset_0(dataset_0);
}
}