【问题标题】:Implementing a complex custom struct serializer实现一个复杂的自定义结构序列化器
【发布时间】:2021-02-22 19:23:27
【问题描述】:

我正在试验 Rust 并尝试为 Rust 中的 Elasticsearch 查询编写一个包装器。我已经实现了一个查询并且它工作正常,但是我真的不喜欢我使用json! 宏的方式。

fn main() {
    let actual = Query {
        field: "field_name".into(),
        values: vec![1, 2, 3],
        boost: Some(2),
        name: Some("query_name".into()),
    };

    let expected = serde_json::json!({
        "terms": {
            "field_name": [1, 2, 3],
            "boost": 2,
            "_name": "query_name"
        }
    });

    let actual_str = serde_json::to_string(&actual).unwrap();
    let expected_str = serde_json::to_string(&expected).unwrap();

    assert_eq!(actual_str, expected_str);
}

#[derive(Debug)]
struct Query {
    field: String,
    values: Vec<i32>,
    boost: Option<i32>,
    name: Option<String>,
}

impl serde::Serialize for Query {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let field = self.field.as_str();
        let values = self.values.as_slice();

        let value = match (&self.boost, &self.name) {
            (None, None) => serde_json::json!({ field: values }),
            (None, Some(name)) => serde_json::json!({ field: values, "_name": name }),
            (Some(boost), None) => serde_json::json!({ field: values, "boost": boost }),
            (Some(boost), Some(name)) => {
                serde_json::json!({ field: values, "boost": boost, "_name": name })
            }
        };

        serde_json::json!({ "terms": value }).serialize(serializer)
    }
}

我想知道如何使用 serde 的内置特征(例如 SerializeStructSerializeMap 等)实现这样的序列化程序。基本上我想避免使用 json 宏或创建中间数据结构。

【问题讨论】:

  • 我没有得到您想要避免实施序列化的问题?另外,json 是无序的,所以不推荐比较字符串输出。

标签: rust serde


【解决方案1】:

如果你让你的数据结构与 JSON 更匹配,那么你可以使用几个 #[serde] 注释来完成整个序列化,这更易于维护,并且不太可能比手动实现慢。 编辑:动态命名的字段实际上使这变得很棘手,因此在这种情况下,Serialize 的自定义实现实际上可能是最好的)。

Serde 序列化通过委托给 Serialize 的其他实现来工作,因此很难在单个实现中从平面结构创建自定义嵌套映射。

您可以像这样为嵌套创建一个临时结构:

// Need to import SerializeMap to call its methods
use serde::{Serialize, ser::SerializeMap};

// temporary wrapper for serializing Query as the inner object
struct InnerQuery<'a>(&'a Query);

impl<'a> serde::Serialize for InnerQuery<'a> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let query = self.0;
        // We don't know the length of the map at this point, so it's None
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry(&query.field, &query.values)?;
        if let Some(boost) = &query.boost {
            map.serialize_entry("boost", boost)?;
        }
        if let Some(name) = &query.name {
            map.serialize_entry("_name", name)?;
        }
        map.end()
    }
}

这将为您提供内部部分:

{
    "field_name": [1, 2, 3],
    "boost": 2,
    "_name": "query_name"
}

然后为主结构实现序列化,例如:

impl serde::Serialize for Query {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let mut map = serializer.serialize_map(Some(1))?;
        map.serialize_entry("terms", &InnerQuery(&self))?;
        map.end()
    }
}

请注意,键的顺序不是确定性的,因此需要更改您的测试。一种方法是将其解析回serde_json::Value,它将按照您的预期比较相等:

let actual_str = serde_json::to_string(&actual).unwrap();
let actual: serde_json::Value = serde_json::from_str(&actual_str).unwrap();

assert_eq!(actual, expected);

【讨论】:

    【解决方案2】:

    如果你想尽可能地避免手动执行,我认为下面的解决方案还不错。您的要求很奇怪,所以我建议您避免这种要求。

    我的解决方案可重复用于其他结构,CustomName 可以在很多上下文中使用,您只需使用 serde 属性 flat 它。

    下面也只比较jsonValue,这样比较好,因为json是无序的。

    use serde::ser::SerializeMap; // 1.0.120
    
    struct Query {
        field: String,
        values: Vec<i32>,
        boost: Option<i32>,
        name: Option<String>,
    }
    
    fn main() {
        let actual = Query {
            field: "field_name".into(),
            values: vec![1, 2, 3],
            boost: Some(2),
            name: Some("query_name".into()),
        };
    
        let expected = serde_json::json!({
            "terms": {
                "field_name": [1, 2, 3],
                "boost": 2,
                "_name": "query_name"
            }
        });
    
        let result = serde_json::to_value(&actual).unwrap();
    
        assert_eq!(result, expected);
    }
    
    // the following concern serrialize implemenation
    
    struct CustomName<'a, 'b> {
        field: &'a str,
        values: &'b [i32],
    }
    
    #[derive(serde::Serialize)]
    struct SerializeQuery<'a, 'b, 'c, 'd> {
        boost: Option<&'a i32>,
        #[serde(rename = "_name")]
        name: Option<&'b str>,
        #[serde(flatten)]
        custom_name: CustomName<'c, 'd>,
    }
    
    #[derive(serde::Serialize)]
    struct Terms<'a, 'b, 'c, 'd> {
        terms: SerializeQuery<'a, 'b, 'c, 'd>,
    }
    
    impl<'a> From<&'a Query> for Terms<'a, 'a, 'a, 'a> {
        fn from(query: &'a Query) -> Self {
            Self {
                terms: SerializeQuery {
                    boost: query.boost.as_ref(),
                    name: query.name.as_deref(),
                    custom_name: CustomName {
                        field: &query.field,
                        values: &query.values,
                    },
                },
            }
        }
    }
    
    impl serde::Serialize for Query {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: serde::Serializer,
        {
            Terms::from(self).serialize(serializer)
        }
    }
    
    impl<'a, 'b> serde::Serialize for CustomName<'a, 'b> {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: serde::Serializer,
        {
            let mut map = serializer.serialize_map(Some(1))?;
            map.serialize_entry(self.field, self.values)?;
            map.end()
        }
    }
    
    

    如您所见,我们在序列化实现方面做的不多,我认为我们无法摆脱使用serialize_map,因为字段名称是动态的。

    【讨论】:

    • 不幸的是,这是 Elasticsearch 提供的数据结构,所以没有办法选择其他的。 :) 但是谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-30
    • 1970-01-01
    • 2022-11-23
    • 1970-01-01
    • 1970-01-01
    • 2021-05-14
    • 1970-01-01
    相关资源
    最近更新 更多