使用rustc_serialize编码JSON时,省略Option :: None的值

时间:2015-05-13 12:45:26

标签: json rust

我有一个我想要编码为JSON的结构。此结构包含类型为Option<i32>的字段。我们说

extern crate rustc_serialize;
use rustc_serialize::json;

#[derive(RustcEncodable)]
struct TestStruct {
    test: Option<i32>
}

fn main() {
    let object = TestStruct {
        test: None
    };

    let obj = json::encode(&object).unwrap();

    println!("{}", obj);
}

这会给我输出

{"test": null}

是否有一种方便的方法可以省略值为Option的{​​{1}}字段?在这种情况下,我想要输出

None

3 个答案:

答案 0 :(得分:2)

如果有人和我一样带着同样的问题来到这里,serde 现在有一个选项 skip_serializing_none 来做到这一点。

https://docs.rs/serde_with/1.8.0/serde_with/attr.skip_serializing_none.html

答案 1 :(得分:0)

通过纯粹从结构中进行似乎不可能,所以我将结构转换为字符串,然后将其转换为JSON对象。此方法要求所有Option类型都是相同的类型。我建议你是否需要在结构中使用变量类型将它们首先变成字符串。

field_vecfield_name_vec必须在编译时填充所有字段,因为我无法找到获取字段值的方法,而字段名称在不知道它们生锈的情况下运行时间。

extern crate rustc_serialize;

use rustc_serialize::json::Json;

fn main() {


    #[derive(RustcEncodable)]
    struct TestStruct {
        test: Option<i32>
    }

    impl TestStruct {
        fn to_json(&self) -> String {
            let mut json_string = String::new();
            json_string.push('{');

            let field_vec = vec![self.test];
            let field_name_vec = vec![stringify!(self.test)];
            let mut previous_field = false;
            let mut count = 0;
            for field in field_vec {
                if previous_field {
                    json_string.push(',');
                }
                match field {
                    Some(value) => {
                        let opt_name = field_name_vec[count].split(". ").collect::<Vec<&str>>()[1];
                        json_string.push('"');
                        json_string.push_str(opt_name);
                        json_string.push('"');
                        json_string.push(':');
                        json_string.push_str(&value.to_string());
                        previous_field = true;
                    },
                    None => {},
                }
                count += 1;
            }
            json_string.push('}');
            json_string
        }
    }

    let object = TestStruct {
        test: None
    };

    let object2 = TestStruct {
        test: Some(42)
    };

    let obj = Json::from_str(&object.to_json()).unwrap();
    let obj2 = Json::from_str(&object2.to_json()).unwrap();

    println!("{:?}", obj);
    println!("{:?}", obj2);
}

答案 2 :(得分:0)

要省略Option<T>字段,您可以创建Encodable特征的实现(而不是使用#[derive(RustcEncodable)])。

在这里,我更新了您的示例以执行此操作。

extern crate rustc_serialize;
use rustc_serialize::json::{ToJson, Json};
use rustc_serialize::{Encodable,json};

use std::collections::BTreeMap;

#[derive(PartialEq, RustcDecodable)]
struct TestStruct {
    test: Option<i32>
}

impl Encodable for TestStruct {
    fn encode<S: rustc_serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        self.to_json().encode(s)
    }
}

impl ToJson for TestStruct {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self.test {
            Some(value) => { d.insert("test".to_string(), value.to_json()); },
            None => {},
        }
        Json::Object(d)
    }
}

fn main() {
    let object = TestStruct {
        test: None
    };

    let obj = json::encode(&object).unwrap();

    println!("{}", obj);

    let decoded: TestStruct = json::decode(&obj).unwrap();
    assert!(decoded==object);
}

最好实现一个自定义#[derive]宏,它会自动为Option字段执行此操作,因为这样就不需要Encodable这样的自定义实现。