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

标签 rust serde serde-json

我有以下格式的 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模式但这不适用于通用请参阅 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(())
}

关于rust - 如何使用 serde 重命名 `start` 和 `end` 范围值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72483890/

相关文章:

data-structures - 从可变结构字段的值调用可变方法

serialization - Serde 使用远程对象的代理类型序列化

json - 如何在没有中间结构的情况下有效地将 JSON 的一部分提取为 Vec?

rust - 序列化 null 值或默认值时如何避免生成 JSON?

rust - 将 Claims 字段反序列化为字符串会返回其他字符

casting - 如何克隆一个 Rc 特征对象并将其转换为另一个特征对象?

rust - WindowCreationError "Failed to create input context"对 libEGL 和 ggez 意味着什么?

android - 无法解析相应的 JNI 函数 Java_com_mozilla_greetings_RustGreetings_greeting

rust - 是否可以将原始字符串直接反序列化为特定类型?

rust - 是否有更简单的方法在反序列化时转换类型?