Custom Serializer for Foreign Type

I'm creating an app that takes input from a file. The file will be edited by hand and read using toml-rs.

Here is an example:

foo = 7300
bar = "0x4563918244f40000"

This is the struct:

use serde::Deserialize;
use web3::types::U256;

#[derive(Deserialize)]
struct FooBar {
    foo: U256,
    bar: u32,
}

Here, U256 is a foreign type. It isn't part of my code base.

This works, but there are two problems. First, foo is scaled quantity. It represents "7.3 percent". Second, bar = "0x4563918244f40000" is a more than I really want to type.

I would rather do this:

foo = "7.3" # implied conversation
bar = "5.0" # implied conversation

How can this be achieved?

You can always implement Deserialize manually instead of relying on the derive. By doing so, you can perform any conversion you need right in the deserialization code.

I believe that you should also be able to use the deserialize_with field attribute.

untested, but something like:

use serde::Deserialize;
use web3::types::U256;

#[derive(Deserialize)]
struct FooBar {
    #[serde(deserialize_with = "deserialize_percentage")]
    foo: U256,
    bar: u32,
}

fn deserialize_percentage<'de, D>(deserializer: D) -> Result<U256, D::Error>
where
    D: Deserializer<'de>,
{
  // Something
}
1 Like

What is deserializer?

I don't think I can implement a visitor for U256. I don't own the type.

You can create a visitor, because the visitor type would be local to your crate, as you created the visitor.

However in this case there's no need to.

fn deserialize_percentage<'de, D>(deserializer: D) -> Result<U256, D::Error>
where
    D: Deserializer<'de>,
{
    let s: String = Deserialize::deserialize(deserializer)?;
    Ok(convert_string_to_u256(&s))
}

fn convert_string_to_u256(s: &str) -> U256 {
    ...
}
3 Likes