fhir 1.2.0

Fast Healthcare Interoperability Resources (FHIR) data model for Rust: the complete FHIR R5, R4, and R3 resources, datatypes, and code systems as serde-serializable types, plus a spec-driven code generator.
//! FHIR R3 XML serialization (feature `xml`).
//!
//! FHIR XML differs from JSON: a primitive is `<field value="…"/>`, an element's
//! `id` (and an extension's `url`) are XML attributes, complex elements nest, and
//! repeating elements are simply repeated. The conversion itself does not vary
//! by release and lives in [`crate::xml`]; this module binds it to the R3
//! [`meta`](crate::r3::meta) table.
//!
//! ```
//! use fhir::r3::resources::Patient;
//! use fhir::r3::xml::{to_xml, from_xml};
//!
//! let patient: Patient = serde_json::from_value(serde_json::json!({
//!     "resourceType": "Patient", "id": "pat-1", "active": true,
//!     "name": [{ "family": "Chalmers", "given": ["Peter"] }]
//! })).unwrap();
//!
//! let xml = to_xml(&patient, "Patient");
//! assert!(xml.contains("<active value=\"true\"/>"));
//!
//! let back: Patient = from_xml(&xml).unwrap();
//! assert_eq!(back, patient);
//! ```

use ::serde::de::DeserializeOwned;
use ::serde_json::Value;

use crate::r3::meta;

pub use crate::xml::{to_xml, XmlError};

/// Parse a FHIR R3 XML string into a resource.
pub fn from_xml<T: DeserializeOwned>(xml: &str) -> Result<T, XmlError> {
    crate::xml::from_xml(meta::elements(), xml)
}

/// Parse a FHIR R3 XML string into untyped JSON.
pub fn xml_to_value(xml: &str) -> Result<Value, XmlError> {
    crate::xml::xml_to_value(meta::elements(), xml)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::r3::resources::{Observation, Patient};

    fn roundtrip_patient(json: ::serde_json::Value) {
        let patient: Patient = ::serde_json::from_value(json).unwrap();
        let xml = to_xml(&patient, "Patient");
        let back: Patient = from_xml(&xml).unwrap_or_else(|e| panic!("from_xml failed: {e}\n{xml}"));
        assert_eq!(back, patient, "\nXML was:\n{xml}");
    }

    #[test]
    fn patient_roundtrips() {
        roundtrip_patient(::serde_json::json!({
            "resourceType": "Patient",
            "id": "pat-1",
            "active": true,
            "gender": "male",
            "birthDate": "1970-03-25",
            "name": [{ "family": "Chalmers", "given": ["Peter", "James"] }],
            "telecom": [{ "system": "phone", "value": "555" }]
        }));
    }

    #[test]
    fn primitive_written_as_value_attribute() {
        let patient: Patient = ::serde_json::from_value(::serde_json::json!({
            "resourceType": "Patient", "active": true
        }))
        .unwrap();
        let xml = to_xml(&patient, "Patient");
        assert!(xml.contains("<active value=\"true\"/>"), "{xml}");
        assert!(xml.contains("xmlns=\"http://hl7.org/fhir\""), "{xml}");
    }

    #[test]
    fn choice_element_roundtrips() {
        // Observation.value[x] — a choice — plus a required 1..1 code.
        let obs: Observation = ::serde_json::from_value(::serde_json::json!({
            "resourceType": "Observation",
            "status": "final",
            "code": { "text": "temp" },
            "valueString": "warm"
        }))
        .unwrap();
        let xml = to_xml(&obs, "Observation");
        let back: Observation = from_xml(&xml).unwrap_or_else(|e| panic!("{e}\n{xml}"));
        assert_eq!(back, obs, "\nXML was:\n{xml}");
    }
}