在 Rust 中使用 Serde 反序列化递归 XML

问题描述

我正在尝试在 Rust 中使用 Serde 和 Quick-XML 反序列化 MathML。由于 MathML 的递归性质,我在尝试编写结构时遇到了麻烦。这是一个最小的、可重现的示例:

  /// REQUIRES: outputs is not nullptr if `output_tensor_names` is non-empty.
  virtual Status Run(const std::vector<std::pair<string,Tensor> >& inputs,const std::vector<string>& output_tensor_names,const std::vector<string>& target_node_names,std::vector<Tensor>* outputs) = 0;

存在堆栈溢出,可能是由于herehere 建议的无限循环所致。我试图实施他们的建议,但无济于事。

解决方法

这是一个关于如何使用 serde 解析嵌套 XML 结构的示例。此代码基于 this 答案。示例 XML 具有称为测量值的节点,该节点放置在 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_>,Debug)]
pub(crate) struct MeasurementSiteReference {
    pub(crate) id: u16,targetClass: String,version: u16,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,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,Default)]
pub(crate) struct PrecipitationDetail {
    #[serde(default)]
    pub(crate) precipitationIntensity: PrecipitationIntensity,Default)]
pub(crate) struct PrecipitationIntensity {
    #[serde(default = "precipitation_intensity")]
    pub(crate) field_description: String,#[serde(default)]
    pub(crate) millimetresPerHourIntensity: MillimetresPerHourIntensity,Default)]
pub(crate) struct MillimetresPerHourIntensity {
    #[serde(rename = "$value")]
    pub(crate) millimetresPerHourIntensity: f32,Default)]
pub(crate) struct Temperature {
    #[serde(rename = "$value")]
    pub(crate) temperature: f32,}

// windSpeed
#[derive(Deserialize,Default)]
pub(crate) struct Wind {
    #[serde(default)]
    pub(crate) windSpeed: WindSpeed,Default)]
pub(crate) struct WindSpeed {
    #[serde(default = "wind_speed")]
    pub(crate) field_description: String,#[serde(default)]
    pub(crate) speed: Speed,Default)]
pub(crate) struct Speed {
    #[serde(rename = "$value")]
    pub(crate) speed: f32,}

// airTemperature
#[derive(Deserialize,Default)]
pub(crate) struct Temperature_ {
    #[serde(default)]
    pub(crate) airTemperature: AirTemperature,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,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,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>