serde-rs / json

Strongly typed JSON library for Rust
Apache License 2.0
4.91k stars 560 forks source link

[suggestion] Hope to add configuration function #1216

Closed victory-wu closed 2 days ago

victory-wu commented 2 days ago

problem

For example, time formatting, custom data types, currency formatting, ignoring empty fields, and more You can refer to the idea of Jackson ObjectiMapper.

Current code usage cases

Will use a lot of macros, These are all repetitive codes and tasks that can be further optimized based on macros

#[derive(Serialize)]
pub struct User {
    pub username: String,
    pub nickname: Option<String>,
    pub login_ip: Option<String>,
    #[serde(with="date_format")]
    pub login_date: Option<NaiveDateTime>,
    pub remark: Option<String> 
    #[serde(with = "date_format")]
    pub create_time: Option<NaiveDateTime>,
    #[serde(with = "date_format")]                       
    pub update_time: Option<NaiveDateTime>,
}

serde_json::to_string(&user)

When encountering a specified type, corresponding serialization processing should be done

I expect the improved code

#[derive(Serialize)]
pub struct User {
    pub username: String,
    pub nickname: Option<String>,
    pub login_ip: Option<String>,
    pub login_date: Option<NaiveDateTime>,
    pub remark: Option<String> 
    pub create_time: Option<NaiveDateTime>,
    pub update_time: Option<NaiveDateTime>,
}

step1 
create config;

let config  = {
    ignore_output_none: true,
    ....
}

Register serialization interpreter
config.typeSerRegs.insert(NaiveDateTime,  serDateFunc );
config.typeSerRegs.insert(Money,  serMoneyfunc )
config.typeSerRegs.insert(NaiveDate,  func )
....

step2  config params
serde_json::to_string(&user, &config)

OR 

step2  OOP

let serdeJson = SerdeJson::new(config)

serdeJson::to_string(&user)

my test code

use std::any::{Any, TypeId};
use std::collections::HashMap;

trait TypeRegistry {
    fn register_serialize_type<T: 'static>(&mut self, ser: fn(&Config, &dyn Any));
    fn serialize(&self, cfg: &Config, data: &dyn Any) ;
}

struct MyTypeRegistry {
    types: HashMap<TypeId, fn(&Config, &dyn Any)>,
}

impl MyTypeRegistry {
    fn new() -> Self {
        MyTypeRegistry {
            types: HashMap::new(),
        }
    }
}

impl TypeRegistry for MyTypeRegistry {
    fn register_serialize_type<T: 'static>(&mut self, ser: fn(&Config, &dyn Any))
    {
        self.types.insert(TypeId::of::<T>(), ser);
    }

    fn serialize(&self, cfg: &Config, data: &dyn Any) {
        for (type_id, ser) in &self.types {
            if data.type_id() == *type_id {
                &ser(cfg, data);
                return;
            }
        }
        println!("{}", "not found".to_string())
    }
}

struct MyStruct {
    field: i32,
}

struct Config {
    ignore_none_field: bool
}

fn hello_int (cfg: &Config, val: &dyn Any) {
    println!("Hello, {}!", cfg.ignore_none_field);
    if let Some(v) = val.downcast_ref::<i32>() {
        println!("Hello int: {}", &v);
    }
}

fn hello_string (cfg: &Config, val: &dyn Any) {
    if let Some(v) = val.downcast_ref::<String>() {
        println!("Hello string {}", v);
    }
}

fn hello_struct (cfg: &Config, val: &dyn Any) {
    if let Some(v) = val.downcast_ref::<MyStruct>() {
        println!("Hello struct {:?}", v.field);
    }
}

#[test]
fn test_main() {
    let mut registry = MyTypeRegistry::new();

    // create serialize type
    registry.register_serialize_type::<i32>(hello_int);
    registry.register_serialize_type::<String>(hello_string);
    registry.register_serialize_type::<MyStruct>(hello_struct);

    let SERDE_DEFAULT_CONFIG = Config {
        ignore_none_field: true
    };

    let dbJsonCfg = Config::from(SERDE_DEFAULT_CONFIG);

    let integer = 42;
    let string = "Hello".to_string();
    let my_struct = MyStruct { field: 10 };

    registry.serialize(&dbJsonCfg, &integer);
    registry.serialize(&dbJsonCfg, &string);
    registry.serialize(&dbJsonCfg, &my_struct);
}
dtolnay commented 2 days ago

I think this could be neat, but I would prefer to leave it to a different library instead of building something into this crate.