使用Serde将json字符串序列化为对象

5us2dqdw  于 2023-06-25  发布在  其他
关注(0)|答案(2)|浏览(136)

我有以下结构

#[derive(Serialize)]
pub struct MyStruct {
    pub id: String,
    pub score: f32,
    pub json: String,
}

json字段始终包含已字符串化的有效JSON对象。
给定一个示例,我想用JSON内容序列化它。类似于:

let a = MyStruct {
    id: "my-id".to_owned(),
    score: 20.3,
    json: r#"{
       "ffo": 4
    }"#,
};
let r = to_string(&a).unwrap();
assert_eq!(r, r#"{
        "id": "my-id",
        "score": 20.3,
        "json": {
            "ffo": 4
        }
    }"#);

注意:我不需要支持不同的序列化格式,只需要支持JSON。NB2:我确信json字段始终包含有效的JSON对象。NB3:我通常使用serde,但我愿意使用不同的库。
我该怎么做?
编辑:如果可能的话,我希望避免在序列化过程中对字符串进行反序列化。

tvmytwxo

tvmytwxo1#

serde_json有一个raw_value的特性,类似于这样的功能:
Cargo.toml

# ...
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["raw_value"] }

lib.rs

use serde::{Serializer, Serialize};
use serde_json::{self, value::RawValue};

#[derive(Serialize)]
pub struct MyStruct {
    pub id: String,
    pub score: f32,
    #[serde(serialize_with = "serialize_raw_json")]
    pub json: String,
}

fn serialize_raw_json<S>(json: &str, s: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    // This should be pretty efficient: it just checks that the string is valid;
    // it doesn't parse it into a new data structure.
    let v: &RawValue = serde_json::from_str(json).expect("invalid json");
    v.serialize(s)
}

#[test]
fn test_serialize() {
    let a = MyStruct {
        id: "my-id".to_owned(),
        score: 20.3,
        json: r#"{
           "ffo": 4
        }"#
        .to_string(),
    };

    let r = serde_json::to_string(&a).unwrap();
    assert_eq!(
        r,
        r#"{"id":"my-id","score":20.3,"json":{
           "ffo": 4
        }}"#
    );
}

但最简单(也是最容易出错和最不可扩展)的解决方案是简单的字符串操作:

#[derive(Serialize)]
pub struct MyStruct {
    pub id: String,
    pub score: f32,
    // IMPORTANT: don't serialize this field at all
    #[serde(skip)]
    pub json: String,
}

fn serialize(a: &MyStruct) -> String {
    let mut r = serde_json::to_string(&a).unwrap();

    // get rid of trailing '}'
    r.pop();
    // push the key
    r.push_str(r#","json":"#);
    // push the value
    r.push_str(&a.json);
    // push the closing brace
    r.push('}');
    
    r
}

#[test]
fn test_serialize() {
    let a = MyStruct {
        id: "my-id".to_owned(),
        score: 20.3,
        json: r#"{
           "ffo": 4
        }"#
        .to_string(),
    };

    let r = serialize(&a);
    assert_eq!(
        r,
        r#"{"id":"my-id","score":20.3,"json":{
           "ffo": 4
        }}"#
    );
}
3zwtqj6y

3zwtqj6y2#

您可以这样做,但必须以某种方式覆盖默认的序列化行为。您可以通过将json字段 Package 在一个新类型中(如struct JsonString(String),并为该类型手动实现Serialize,或者您可以使用#[serde(serialize_with = "...")]字段属性临时更改json字段的序列化。下面是使用serialize_with字段属性的示例:

use serde::{ser::Error, Serialize, Serializer};

use serde_json::Value;

#[derive(Serialize)]
pub struct MyStruct {
    pub id: String,
    pub score: f32,
    #[serde(serialize_with = "as_json_object")]
    pub json: String,
}

fn as_json_object<S>(v: &str, s: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let v: Value =
        serde_json::from_str(v).map_err(|_| Error::custom("error parsing serialized json"))?;

    v.serialize(s)
}

fn main() {
    let a = MyStruct {
        id: "my-id".to_owned(),
        score: 20.3,
        json: r#"{
           "ffo": 4
        }"#
        .to_owned(),
    };

    let r = serde_json::to_string(&a).unwrap();

    assert_eq!(r, r#"{"id":"my-id","score":20.3,"json":{"ffo":4}}"#);
}

Playground.

相关问题