【问题标题】:How to collate an array of JSON objects by a property into a struct containing vectors of each object's properties?如何按属性将 JSON 对象数组整理成包含每个对象属性向量的结构?
【发布时间】:2018-04-25 00:25:33
【问题描述】:

我正在接收一个传感器数据流,我需要汇总并执行基本统计数据(平均值、最大值、最小值等)。有多个值,但传感器数据可能不一致,并且某些值可能会丢失。

从阅读本书来看,当luminositycolor 的值丢失但我对此感到困惑时,似乎应该使用Option

这是我的传感器数据的示例:

[
    {
        "sensor": "left",
        "luminosity": "50",
        "color": "(255,0,0)"
    },
    {
        "sensor": "left",
        "color": "#0f0"
    },
    {
        "sensor": "right",
        "luminosity": "20"
    },
    {
        "sensor": "right",
        "luminosity": "40",
        "color": "(255,0,0)"
    },
    {
        "sensor": "left",
        "luminosity": "30"
    },
    {
        "sensor": "top",
        "luminosity": "10"
    },
    {
        "sensor": "right",
        "color": "(0,0,0)"
    }
]

每个传感器的数据将存储在以下结构的实例中:

struct Data {
    pub luminosity: Vec<String>,
    pub color: Vec<String>,
}

我想遍历上述 JSON 对象,将传感器与正确的结构实例匹配(“正确”传感器与“正确”传感器结构)并将每个 JSON 观察的内容推送到向量上(在每个结构实例内)。

需要记录缺失值,以便对于每个“观察”,对于相应传感器的结构实例,结构中的每个向量都有一个推送操作。

【问题讨论】:

标签: json vector struct rust serde


【解决方案1】:

这样的事情应该可以工作。它使用Serde 将每个 JSON 数组元素读入一个辅助结构体,其中包含所需的传感器名称String,以及传感器每个值的Option&lt;String&gt; 数据。然后它遍历这些读数并将它们插入到一个映射中,其中键是传感器名称,值是每个传感器值的数据向量。


#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

use std::collections::BTreeMap as Map;
use std::error::Error;

#[derive(Debug, Default)]
struct Data {
    luminosity: Vec<Option<String>>,
    color: Vec<Option<String>>,
}

fn main() {
    let input = r##"[
                      {
                        "sensor": "left",
                        "luminosity": "50",
                        "color": "(255,0,0)"
                      },
                      {
                        "sensor": "left",
                        "color": "#0f0"
                      },
                      {
                        "sensor": "right",
                        "luminosity": "20"
                      },
                      {
                        "sensor": "right",
                        "luminosity": "40",
                        "color": "(255,0,0)"
                      },
                      {
                        "sensor": "left",
                        "luminosity": "30"
                      },
                      {
                        "sensor": "top",
                        "luminosity": "10"
                      },
                      {
                        "sensor": "right",
                        "color": "(0,0,0)"
                      }
                    ]"##;
    let m = read_sensor_data(input).unwrap();
    println!("{:#?}", m);
}

fn read_sensor_data(input: &str) -> Result<Map<String, Data>, Box<Error>> {
    // Private helper struct that matches the format of the raw JSON
    #[derive(Deserialize)]
    struct RawReading {
        sensor: String,
        luminosity: Option<String>,
        color: Option<String>,
    }

    // Deserialize the raw data
    let raw_readings: Vec<RawReading> = serde_json::from_str(input)?;

    // Loop over raw data and insert each reading into the right sensor's struct
    let mut m = Map::new();
    for raw in raw_readings {
        // Look up this sensor's Data struct
        let sensor = m.entry(raw.sensor).or_insert_with(Data::default);

        // One push for every vector in the struct, even for missing observations
        sensor.luminosity.push(raw.luminosity);
        sensor.color.push(raw.color);
    }
    Ok(m)
}

【讨论】:

    【解决方案2】:

    您可以以更多代码为代价稍微提高效率。如果您创建自己的 Visitor 实现,则不需要反序列化 Vec

    extern crate serde;
    #[macro_use]
    extern crate serde_derive;
    extern crate serde_json;
    
    use std::collections::HashMap;
    use std::fmt;
    use serde::de::{Deserialize, Deserializer, Visitor};
    
    #[derive(Debug, Default)]
    struct Data {
        luminosity: Vec<Option<String>>,
        color: Vec<Option<String>>,
    }
    
    struct Wrapper(HashMap<String, Data>);
    
    impl<'de> Deserialize<'de> for Wrapper {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
        {
            deserializer.deserialize_seq(WrapperVisitor)
        }
    }
    
    struct WrapperVisitor;
    
    impl<'de> Visitor<'de> for WrapperVisitor {
        type Value = Wrapper;
    
        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a sequence of measurement objects")
        }
    
        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
        where
            A: serde::de::SeqAccess<'de>,
        {
            #[derive(Debug, Deserialize)]
            struct DataPoint {
                sensor: String,
                luminosity: Option<String>,
                color: Option<String>,
            }
    
            let mut all_data = HashMap::new();
    
            while let Some(data_point) = seq.next_element::<DataPoint>()? {
                let data = all_data
                    .entry(data_point.sensor)
                    .or_insert_with(Data::default);
                data.luminosity.push(data_point.luminosity);
                data.color.push(data_point.color);
            }
    
            Ok(Wrapper(all_data))
        }
    }
    
    fn main() {
        let input = r###"
    [
        {
            "sensor": "left",
            "luminosity": "50",
            "color": "(255,0,0)"
        },
        {
            "sensor": "left",
            "color": "#0f0"
        },
        {
            "sensor": "right",
            "luminosity": "20"
        },
        {
            "sensor": "right",
            "luminosity": "40",
            "color": "(255,0,0)"
        },
        {
            "sensor": "left",
            "luminosity": "30"
        },
        {
            "sensor": "top",
            "luminosity": "10"
        },
        {
            "sensor": "right",
            "color": "(0,0,0)"
        }
    ]
    "###;
    
        let data = serde_json::from_str::<Wrapper>(input).expect("Nope");
        let data = data.0;
    
        println!("{:#?}", data);
    }
    

    这会产生输出:

    {
        "left": Data {
            luminosity: [
                Some("50"),
                None,
                Some("30")
            ],
            color: [
                Some("(255,0,0)"),
                Some("#0f0"),
                None
            ]
        },
        "right": Data {
            luminosity: [
                Some("20"),
                Some("40"),
                None
            ],
            color: [
                None,
                Some("(255,0,0)"),
                Some("(0,0,0)")
            ]
        },
        "top": Data {
            luminosity: [
                Some("10")
            ],
            color: [
                None
            ]
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-13
      • 2019-03-07
      • 2022-01-18
      相关资源
      最近更新 更多