Here is an example on how to parse a nested XML-structure using serde. This code is based on this answer. The sample XML have nodes called measuredValue that is placed into a Vec<structs::Data>
.
Cargo.toml:
[dependencies]
serde_derive = "1.0"
serde = "1.0"
serde-xml-rs = "0.4"
serde_json = "1.0.64"
structs.rs:
// XML-root
#[derive(Deserialize, Debug)]
pub(crate) struct D2LogicalModel {
pub(crate) payloadPublication: PayloadPublication,
}
// payloadPublication
#[derive(Deserialize, Debug)]
pub(crate) struct PayloadPublication {
lang: String,
pub(crate) publicationTime: PublicationTime,
pub(crate) siteMeasurements: Vec<SiteMeasurements>,
}
#[derive(Deserialize, Debug)]
pub(crate) struct PublicationTime {
#[serde(rename = "$value")]
pub(crate) publicationTime: String,
}
// // siteMeasurements, the various weather measurements are below (sub-root).
#[derive(Deserialize, Debug)]
pub(crate) struct SiteMeasurements {
pub(crate) measurementSiteReference: MeasurementSiteReference,
pub(crate) measurementTimeDefault: MeasurementTimeDefault,
#[serde(default)]
pub(crate) measuredValue: Vec<MeasuredValue_>,
}
#[derive(Deserialize, Debug)]
pub(crate) struct MeasurementSiteReference {
pub(crate) id: u16,
targetClass: String,
version: u16,
}
#[derive(Deserialize, Debug)]
pub(crate) struct MeasurementTimeDefault {
#[serde(rename = "$value")]
pub(crate) measurementTimeDefault: String,
}
// Common for all measurements.
#[derive(Deserialize, Debug)]
pub(crate) struct MeasuredValue_ {
pub(crate) index: u16,
pub(crate) measuredValue: MeasuredValue,
}
#[derive(Deserialize, Debug)]
pub(crate) struct MeasuredValue {
pub(crate) basicData: BasicData,
}
// Split based on type of measurement. Below this point the tree is different.
#[derive(Deserialize, Debug, Default)]
pub(crate) struct BasicData {
#[serde(default)]
pub(crate) precipitationDetail: PrecipitationDetail,
#[serde(default)]
pub(crate) wind: Wind,
#[serde(default)]
pub(crate) temperature: Temperature_, // Add underscore since this collides with another struct
}
// precipitationIntensity
#[derive(Deserialize, Debug, Default)]
pub(crate) struct PrecipitationDetail {
#[serde(default)]
pub(crate) precipitationIntensity: PrecipitationIntensity,
}
#[derive(Deserialize, Debug, Default)]
pub(crate) struct PrecipitationIntensity {
#[serde(default = "precipitation_intensity")]
pub(crate) field_description: String,
#[serde(default)]
pub(crate) millimetresPerHourIntensity: MillimetresPerHourIntensity,
}
#[derive(Deserialize, Debug, Default)]
pub(crate) struct MillimetresPerHourIntensity {
#[serde(rename = "$value")]
pub(crate) millimetresPerHourIntensity: f32,
}
#[derive(Deserialize, Debug, Default)]
pub(crate) struct Temperature {
#[serde(rename = "$value")]
pub(crate) temperature: f32,
}
// windSpeed
#[derive(Deserialize, Debug, Default)]
pub(crate) struct Wind {
#[serde(default)]
pub(crate) windSpeed: WindSpeed,
}
#[derive(Deserialize, Debug, Default)]
pub(crate) struct WindSpeed {
#[serde(default = "wind_speed")]
pub(crate) field_description: String,
#[serde(default)]
pub(crate) speed: Speed,
}
#[derive(Deserialize, Debug, Default)]
pub(crate) struct Speed {
#[serde(rename = "$value")]
pub(crate) speed: f32,
}
// airTemperature
#[derive(Deserialize, Debug, Default)]
pub(crate) struct Temperature_ {
#[serde(default)]
pub(crate) airTemperature: AirTemperature,
}
#[derive(Deserialize, Debug, Default)]
pub(crate) struct AirTemperature {
#[serde(default = "air_temperature")]
pub(crate) field_description: String,
#[serde(default)]
pub(crate) temperature: Temperature,
}
// Add default values in serde.
fn precipitation_intensity() -> String {
"precipitation_intensity".to_string()
}
fn wind_speed() -> String {
"wind_speed".to_string()
}
fn air_temperature() -> String {
"air_temperature".to_string()
}
#[derive(Serialize)]
pub(crate) struct WeatherMeasurement {
pub(crate) measurement_time_default: String,
pub(crate) id: u16,
pub(crate) data: Vec<Data>,
}
#[derive(Serialize)]
pub(crate) struct Data {
pub(crate) index: u16,
pub(crate) field_description: String,
pub(crate) measurement: f32,
}
main.rs:
#![allow(non_snake_case)]
mod structs;
use std::fs;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_xml_rs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Goodbye XML!");
let filename = "./sample.xml";
let content= fs::read_to_string(filename).expect("Unable to read file");
let d2LogicalModel: structs::D2LogicalModel = serde_xml_rs::from_str(&*content).unwrap();
let mut measurements: Vec<structs::WeatherMeasurement> = Vec::new();
for site in &d2LogicalModel.payloadPublication.siteMeasurements {
// The actual weather data
let mut readings: Vec<structs::Data> = Vec::new();
let measurement_time_default = &site.measurementTimeDefault.measurementTimeDefault;
let id = &site.measurementSiteReference.id;
for measured_value in &site.measuredValue {
let index = &measured_value.index;
let weather_node = &measured_value.measuredValue.basicData;
// precipitationIntensity
let field_description = &weather_node
.precipitationDetail
.precipitationIntensity
.field_description;
if !field_description.is_empty() {
let measurement = &weather_node
.precipitationDetail
.precipitationIntensity
.millimetresPerHourIntensity
.millimetresPerHourIntensity;
let r = structs::Data {
index: *index,
field_description: field_description.clone(),
measurement: *measurement,
};
readings.push(r);
/*println!("measurement time-default: {}, id: {}, index: {}, field description: {}, measurement: {}",
measurement_time_default, id, index, field_description, measurement);*/
};
// windSpeed
let field_description = &weather_node.wind.windSpeed.field_description;
if !field_description.is_empty() {
let measurement = &weather_node.wind.windSpeed.speed.speed;
let r = structs::Data {
index: *index,
field_description: field_description.clone(),
measurement: *measurement,
};
readings.push(r);
/*println!("measurement time-default: {}, id: {}, index: {}, field description: {}, measurement: {}",
measurement_time_default, id, index, field_description, measurement);*/
};
// airTemperature
let field_description = &weather_node.temperature.airTemperature.field_description;
if !field_description.is_empty() {
let measurement = &weather_node
.temperature
.airTemperature
.temperature
.temperature;
let r = structs::Data {
index: *index,
field_description: field_description.clone(),
measurement: *measurement,
};
readings.push(r);
/*println!("measurement time-default: {}, id: {}, index: {}, field description: {}, measurement: {}",
measurement_time_default, id, index, field_description, measurement);*/
};
}
let wm = structs::WeatherMeasurement {
measurement_time_default: measurement_time_default.clone(),
id: *id,
data: readings,
};
measurements.push(wm);
// Add final struct here
}
let jm = serde_json::to_string(&measurements)?;
println!("{:?}", &jm);
Ok(())
}
sample.xml:
<d2LogicalModel modelBaseVersion="2" xmlns="http://datex2.eu/schema/2/2_0">
<payloadPublication lang="nob" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="MeasuredDataPublication">
<publicationTime>2021-03-24T21:02:28.762+01:00</publicationTime>
<siteMeasurements>
<measurementSiteReference id="228" targetClass="MeasurementSiteRecord" version="3576"/>
<measurementTimeDefault>2021-03-24T20:50:00+01:00</measurementTimeDefault>
<measuredValue index="2501">
<measuredValue>
<basicData xsi:type="PrecipitationInformation">
<precipitationDetail>
<precipitationIntensity>
<millimetresPerHourIntensity>0.0</millimetresPerHourIntensity>
</precipitationIntensity>
</precipitationDetail>
</basicData>
</measuredValue>
</measuredValue>
<measuredValue index="901">
<measuredValue>
<basicData xsi:type="WindInformation">
<wind>
<windSpeed>
<speed>21.24</speed>
</windSpeed>
</wind>
</basicData>
</measuredValue>
</measuredValue>
<measuredValue index="101">
<measuredValue>
<basicData xsi:type="TemperatureInformation">
<temperature>
<airTemperature>
<temperature>0.2</temperature>
</airTemperature>
</temperature>
</basicData>
</measuredValue>
</measuredValue>
</siteMeasurements>
</payloadPublication>
</d2LogicalModel>