如何用 serde 重命名 `start` 和 `end` 范围值?

How to rename `start` and `end` range values with serde?

我有 JSON 个格式如下的对象:

{
  "name": "foo",
  "value": 1234,
  "upper_bound": 5000,
  "lower_bound": 1000
}

我想使用 serde 来处理这些对象,结构类似于

struct MyObject {
  name: String,
  value: i32,
  bound: Range<i32>,
}

在没有任何修改的情况下,序列化这些结构之一会产生

{
  "name": "foo",
  "value": 1234,
  "bound": {
    "start": 1000,
    "end": 5000
  }
}

我可以申请#[serde(flatten)]靠近,屈服

{
  "name": "foo",
  "value": 1234,
  "start": 1000,
  "end": 5000
}

但是添加 #[serde(rename...)] 似乎没有任何改变,无论我尝试为重命名提供什么样的参数。是否可以展平范围并重命名参数?

你可以使用 serde 属性 with 并且只使用一个中间结构让真正的实现到 serde:

use core::ops::Range;
use serde::{Deserialize, Serialize};
use serde_json::Error;

#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
struct Foo {
    name: String,
    value: i32,
    #[serde(with = "range_aux", flatten)]
    bound: Range<i32>,
}

mod range_aux {
    use core::ops::Range;
    use serde::{Deserialize, Deserializer, Serialize, Serializer};

    #[derive(Serialize, Deserialize)]
    struct RangeAux {
        upper_bound: i32,
        lower_bound: i32,
    }

    pub fn serialize<S>(range: &Range<i32>, ser: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        RangeAux::serialize(
            &RangeAux {
                upper_bound: range.end,
                lower_bound: range.start,
            },
            ser,
        )
    }

    pub fn deserialize<'de, D>(d: D) -> Result<Range<i32>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let range_aux: RangeAux = RangeAux::deserialize(d)?;
        Ok(Range {
            start: range_aux.lower_bound,
            end: range_aux.upper_bound,
        })
    }
}

fn main() -> Result<(), Error> {
    let data = r#"{"name":"foo","value":1234,"upper_bound":5000,"lower_bound":1000}"#;

    let foo: Foo = serde_json::from_str(data)?;

    assert_eq!(
        foo,
        Foo {
            name: "foo".to_string(),
            value: 1234,
            bound: 1000..5000
        }
    );

    let output = serde_json::to_string(&foo)?;

    assert_eq!(data, output);

    Ok(())
}

非常接近 remote pattern but this doesn't work with generic see serde#1844

可能的通用版本:

use core::ops::Range;
use serde::{Deserialize, Serialize};
use serde_json::Error;

#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
struct Foo {
    name: String,
    value: i32,
    #[serde(with = "range_aux", flatten)]
    bound: Range<i32>,
}

mod range_aux {
    use core::ops::Range;
    use serde::{Deserialize, Deserializer, Serialize, Serializer};

    pub fn serialize<S, Idx: Serialize>(range: &Range<Idx>, ser: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        // could require Idx to be Copy or Clone instead of borrowing Idx
        #[derive(Serialize)]
        struct RangeAux<'a, Idx> {
            upper_bound: &'a Idx,
            lower_bound: &'a Idx,
        }
        RangeAux::serialize(
            &RangeAux {
                upper_bound: &range.end,
                lower_bound: &range.start,
            },
            ser,
        )
    }

    pub fn deserialize<'de, D, Idx: Deserialize<'de>>(d: D) -> Result<Range<Idx>, D::Error>
    where
        D: Deserializer<'de>,
    {
        #[derive(Deserialize)]
        struct RangeAux<Idx> {
            upper_bound: Idx,
            lower_bound: Idx,
        }
        let range_aux: RangeAux<Idx> = RangeAux::deserialize(d)?;
        Ok(Range {
            start: range_aux.lower_bound,
            end: range_aux.upper_bound,
        })
    }
}

fn main() -> Result<(), Error> {
    let data = r#"{"name":"foo","value":1234,"upper_bound":5000,"lower_bound":1000}"#;

    let foo: Foo = serde_json::from_str(data)?;

    assert_eq!(
        foo,
        Foo {
            name: "foo".to_string(),
            value: 1234,
            bound: 1000..5000
        }
    );

    let output = serde_json::to_string(&foo)?;

    assert_eq!(data, output);

    Ok(())
}

不一定比自定义序列化程序更简洁,但肯定更简单一点的是 [serde(frominto)] 的解决方案。 (我觉得我在每个 serde 问题上都发布了这个。:/)

您定义了一个辅助的、可序列化的结构,它具有您想要的 JSON 结构:

#[derive(Deserialize, Serialize, Clone)]
struct AuxMyObject {
    name: String,
    value: i32,
    upper_bound: i32,
    lower_bound: i32,
}

然后你向 rust 解释你的辅助结构与原始结构的关系。这有点乏味(但很简单),可能有一些宏箱可以帮助减轻打字负担:

impl From<MyObject> for AuxMyObject {
    fn from(from: MyObject) -> Self {
        Self {
            name: from.name,
            value: from.value,
            lower_bound: from.bound.start,
            upper_bound: from.bound.end,
        }
    }
}

impl From<AuxMyObject> for MyObject {
    fn from(from: AuxMyObject) -> Self {
        Self {
            name: from.name,
            value: from.value,
            bound: Range {
                start: from.lower_bound,
                end: from.upper_bound,
            },
        }
    }
}

最后,您告诉 serde 在序列化时用辅助结构替换主结构:

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[serde(from = "AuxMyObject", into = "AuxMyObject")]
struct MyObject { … }

Playground