Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to serialize/deserialize based on another field? #253

Closed
duncanfinney opened this issue Oct 15, 2018 · 4 comments
Closed

How to serialize/deserialize based on another field? #253

duncanfinney opened this issue Oct 15, 2018 · 4 comments

Comments

@duncanfinney
Copy link

@duncanfinney duncanfinney commented Oct 15, 2018

Hi guys,

Very awesome product btw! I'm using it to make a network coprocessor API wrapper and it's worked great for 99% of a massive API.

The couple structures that are giving me a bit of problems are like this:

#[derive(Serialize, Deserialize, Clone)]
pub struct TestStruct {
    pub list_one_length: u8
    pub list_two_length: u8
    pub list_one: Vec<u8>,
    pub output_cluster_list: Vec<u8>,
}

// Ex:
TestStruct {
  list_one_length: 1,
  list_two_length: 2,
  lsit_one: vec![3]
  list_two: vec![4,5]
}

// should serialize to: [ 0x01, 0x02, 0x03, 0x04, 0x05 ] 

Is there a good way to express this with serde/bincode?

With a lot of time I was able to come up with a #[serde(with="") serializer/deserializer that would prepend each vector with a u8 of its length. But, I wasn't able to figure out how to look back more than one field on the struct.

Any help you guys could give would be really appreciated!!

@TyOverby
Copy link
Collaborator

@TyOverby TyOverby commented Oct 15, 2018

This is not possible with bincode.

@TyOverby TyOverby closed this Oct 15, 2018
@TyOverby
Copy link
Collaborator

@TyOverby TyOverby commented Oct 15, 2018

For parsing binary data, I recommend nom

@dtolnay
Copy link
Collaborator

@dtolnay dtolnay commented Oct 15, 2018

I would write this as:

use std::fmt;
use serde::de::{self, Deserialize, DeserializeSeed, Deserializer, SeqAccess, Visitor};
use serde::ser::{Serialize, SerializeTuple, Serializer};

#[derive(Debug)]
struct TestStruct {
    list_one: Vec<u8>,
    list_two: Vec<u8>,
}

impl Serialize for TestStruct {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        struct BytesToFixedSize<'a>(&'a [u8]);

        impl<'a> Serialize for BytesToFixedSize<'a> {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: Serializer,
            {
                let mut bytes = serializer.serialize_tuple(self.0.len())?;
                for x in self.0 {
                    bytes.serialize_element(x)?;
                }
                bytes.end()
            }
        }

        let len_one = self.list_one.len();
        let len_two = self.list_two.len();
        assert!(len_one <= u8::max_value() as usize);
        assert!(len_two <= u8::max_value() as usize);

        let mut bytes = serializer.serialize_tuple(4)?;
        bytes.serialize_element(&(len_one as u8))?;
        bytes.serialize_element(&(len_two as u8))?;
        bytes.serialize_element(&BytesToFixedSize(&self.list_one))?;
        bytes.serialize_element(&BytesToFixedSize(&self.list_two))?;
        bytes.end()
    }
}

impl<'de> Deserialize<'de> for TestStruct {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct FixedSizeToBytes(usize);

        impl<'de> DeserializeSeed<'de> for FixedSizeToBytes {
            type Value = Vec<u8>;

            fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
            where
                D: Deserializer<'de>,
            {
                deserializer.deserialize_tuple(self.0 as usize, self)
            }
        }

        impl<'de> Visitor<'de> for FixedSizeToBytes {
            type Value = Vec<u8>;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                write!(formatter, "{} bytes", self.0)
            }

            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
            where
                A: SeqAccess<'de>,
            {
                let mut bytes = Vec::new();
                for i in 0..self.0 {
                    let x = seq
                        .next_element()?
                        .ok_or_else(|| de::Error::invalid_length(i, &self))?;
                    bytes.push(x);
                }
                Ok(bytes)
            }
        }

        struct TestStructVisitor;

        impl<'de> Visitor<'de> for TestStructVisitor {
            type Value = TestStruct;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("struct TestStruct")
            }

            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
            where
                A: SeqAccess<'de>,
            {
                let len_one: u8 = seq
                    .next_element()?
                    .ok_or_else(|| de::Error::missing_field("len_one"))?;
                let len_two: u8 = seq
                    .next_element()?
                    .ok_or_else(|| de::Error::missing_field("len_two"))?;
                let list_one = seq
                    .next_element_seed(FixedSizeToBytes(len_one as usize))?
                    .ok_or_else(|| de::Error::missing_field("list_one"))?;
                let list_two = seq
                    .next_element_seed(FixedSizeToBytes(len_two as usize))?
                    .ok_or_else(|| de::Error::missing_field("list_two"))?;
                Ok(TestStruct { list_one, list_two })
            }
        }

        deserializer.deserialize_tuple(4, TestStructVisitor)
    }
}

fn main() {
    let test_struct = TestStruct {
        list_one: vec![3],
        list_two: vec![4, 5],
    };

    // [0x01, 0x02, 0x03, 0x04, 0x05]
    let bytes = bincode::serialize(&test_struct).unwrap();
    println!("{:?}", bytes);

    let back = bincode::deserialize::<TestStruct>(&bytes).unwrap();
    println!("{:#?}", back);
}
@duncanfinney
Copy link
Author

@duncanfinney duncanfinney commented Nov 3, 2018

@dtolnay : I completely missed your post until today.

That's awesome! Thank you for the in-depth reply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.