use serde_json::{Number, Value};
use thiserror::Error;
use xmltree::Element;
use xmltree::Error as XmlWriteError;
#[derive(Debug, Error)]
pub enum ScjsonError {
#[error("XML parse error: {0}")]
Xml(#[from] xmltree::ParseError),
#[error("XML write error: {0}")]
XmlWrite(#[from] XmlWriteError),
#[error("JSON parse error: {0}")]
Json(#[from] serde_json::Error),
#[error("unsupported document")]
Unsupported,
}
pub fn xml_to_json(xml: &str, omit_empty: bool) -> Result<String, ScjsonError> {
let root = Element::parse(xml.as_bytes())?;
if root.name != "scxml" {
return Err(ScjsonError::Unsupported);
}
let version = root
.attributes
.get("version")
.and_then(|v| v.parse::<f64>().ok())
.unwrap_or(1.0);
let datamodel = root
.attributes
.get("datamodel")
.map(|s| s.as_str())
.unwrap_or("null");
let mut obj = serde_json::Map::new();
let ver_value = if (version.fract() - 0.0).abs() < f64::EPSILON {
Value::Number(Number::from(version as i64))
} else {
Value::Number(Number::from_f64(version).unwrap())
};
obj.insert("version".into(), ver_value);
obj.insert(
"datamodel_attribute".into(),
Value::String(datamodel.to_string()),
);
let value = Value::Object(obj);
if omit_empty {
Ok(serde_json::to_string_pretty(&value)?)
} else {
Ok(serde_json::to_string_pretty(&value)?)
}
}
pub fn json_to_xml(json_str: &str) -> Result<String, ScjsonError> {
let v: Value = serde_json::from_str(json_str)?;
let obj = v.as_object().ok_or(ScjsonError::Unsupported)?;
let version = obj.get("version").and_then(|v| v.as_f64()).unwrap_or(1.0);
let datamodel = obj
.get("datamodel_attribute")
.and_then(|v| v.as_str())
.unwrap_or("null");
let mut root = Element::new("scxml");
root.attributes
.insert("xmlns".into(), "http://www.w3.org/2005/07/scxml".into());
root.attributes
.insert("version".into(), version.to_string());
root.attributes
.insert("datamodel".into(), datamodel.to_string());
let mut out = Vec::new();
root.write(&mut out)?;
Ok(String::from_utf8(out).unwrap())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_simple() {
let xml = "<scxml xmlns=\"http://www.w3.org/2005/07/scxml\"/>";
let json = xml_to_json(xml, true).unwrap();
assert!(json.contains("version"));
let xml_rt = json_to_xml(&json).unwrap();
assert!(xml_rt.contains("scxml"));
}
}