-
I'd like to (de-)serialize the type pub(crate) struct LocalPyDictCustom<K, V>(PhantomData<(K, V)>);
impl<K, V> SerializeAs<Py<PyDict>> for LocalPyDictCustom<K, V>
where
for<'a> K: Serialize + Clone + pyo3::FromPyObject<'a>,
V: Serialize + Clone + PyClass,
{
fn serialize_as<S>(source: &Py<PyDict>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
crate::custom_serde::pydict_ser_custom::<K, V, _>(source, serializer)
}
}
impl<'de, K, V> DeserializeAs<'de, Py<PyDict>> for LocalPyDictCustom<K, V>
where
K: Deserialize<'de> + ToPyObject,
V: Deserialize<'de> + PyClass + PyTypeInfo + Into<PyClassInitializer<V>>,
<V as pyo3::PyTypeInfo>::BaseLayout: pyo3::type_object::PyBorrowFlagLayout<<V as pyo3::PyTypeInfo>::BaseType>,
{
fn deserialize_as<D>(deserializer: D) -> Result<Py<PyDict>, D::Error>
where
D: Deserializer<'de>,
{
crate::custom_serde::pydict_de_custom::<K, V, _>(deserializer)
}
} The additional trait bounds are necessary, because I cannot (de-)serialize arbitrary types, they need to implement some traits to interact with Python. What exactly those traits are probably doesn't matter. However, the nested types may require another serde_as-type, e.g. let's say I want to use the following: #[serde_as(as = "LocalPyDictCustom<PyObject, Terminal>")]
pub text: Py<PyDict>, This doesn't work, because PyObject is a remote type that does not implement (de-)serialize. Thus, I replace it with a local type that does implement those traits via serde_as: #[serde_as(as = "LocalPyDictCustom<LocalPyObject, Terminal>")]
pub text: Py<PyDict>, This works when trying to (de-)serialize a simple PyObject, but in this case the implementation of LocalPyDictCustom (or rather: its trait implementations) rely on the nested types implementing certain traits and my replacement type (LocalPyObject) obviously does not implement any of them. For all intents and purposes the Am I doing something wrong, is this simply not supported yet or are these fundamental limitations of Rust? I realize I can probably get around this by creating yet another local type that basically hardcodes the key type, but I'm rather fond of serde_as and like how - so far - it was easy to mix and match local and remote types, even in nested (generic) types. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
Hi @Lehona, I am confused what exactly your code does, since you seem to be using different definitions throughout your message and not all code is posted. Correct me if I'm wrong. I think you want to define a custom serialize/deserialize implementation for the The definition for these two is this macro Lines 66 to 86 in df2d69c Expanded Macroimpl<K, KU, V, VU> SerializeAs<BTreeMap<K, V>> for BTreeMap<KU, VU>
where
KU: SerializeAs<K>,
VU: SerializeAs<V>,
K: Ord,
{
fn serialize_as<S>(source: &BTreeMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_map(source.iter().map(|(k, v)| {
(
SerializeAsWrap::<K, KU>::new(k),
SerializeAsWrap::<V, VU>::new(v),
)
}))
}
}
impl<K, KU, V, VU, H> SerializeAs<HashMap<K, V, H>> for HashMap<KU, VU, H>
where
KU: SerializeAs<K>,
VU: SerializeAs<V>,
K: Eq + Hash,
H: BuildHasher,
{
fn serialize_as<S>(source: &HashMap<K, V, H>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_map(source.iter().map(|(k, v)| {
(
SerializeAsWrap::<K, KU>::new(k),
SerializeAsWrap::<V, VU>::new(v),
)
}))
}
} The way to do this is adding a trait bound like If this is not helping you, maybe you could share more of the (non-)working code. |
Beta Was this translation helpful? Give feedback.
-
It was difficult to distill the issue without including too much fluff (and it seems like I removed too much). You managed to figure it out anyway 👍 I had to add some more type parameters to my new SerializeAs-type ( I suggest documenting the SerializeAsWrap-types a bit more, it wasn't really clear to me how to use them before you gave me an example. For completeness's sake I'm leavig my solution here. At some point I'd like to get rid of the hardcoded pub(crate) struct LocalPyDictCustom<K, V, KU, VU>(PhantomData<(K, V, KU, VU)>);
impl<'de, K, V, KU, VU> DeserializeAs<'de, Py<PyDict>> for LocalPyDictCustom<K, V, KU, VU>
where
KU: DeserializeAs<'de, K>,
VU: DeserializeAs<'de, Py<V>>,
K: ToPyObject,
V: PyClass + PyTypeInfo + Into<PyClassInitializer<V>>,
<V as pyo3::PyTypeInfo>::BaseLayout: pyo3::type_object::PyBorrowFlagLayout<<V as pyo3::PyTypeInfo>::BaseType>,
{
fn deserialize_as<D>(deserializer: D) -> Result<Py<PyDict>, D::Error>
where
D: Deserializer<'de>,
{
struct MapVisitor<K, V, KU, VU> {
marker: PhantomData<(K, V, KU, VU)>,
}
impl<'de, K, V, KU, VU> Visitor<'de> for MapVisitor<K, V, KU, VU>
where
KU: DeserializeAs<'de, K>,
VU: DeserializeAs<'de, Py<V>>,
K: ToPyObject,
V: PyClass + PyTypeInfo + Into<PyClassInitializer<V>>,
<V as pyo3::PyTypeInfo>::BaseLayout: pyo3::type_object::PyBorrowFlagLayout<<V as pyo3::PyTypeInfo>::BaseType>,
{
type Value = Py<PyDict>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("A Python Dictionary")
}
#[inline]
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let gil = Python::acquire_gil();
let py = gil.python();
let dict = PyDict::new(py);
while let Some((key, value)) = (map.next_entry())?.map(|(k, v): (DeserializeAsWrap::<K, KU>, DeserializeAsWrap::<Py<V>, VU>)| (k.into_inner(), v.into_inner())) {
dict.set_item(key, value).expect("Unable to set item for deserialize");
}
let py_dict: Py<PyDict> = dict.into();
Ok(py_dict)
}
}
let visitor = MapVisitor::<K, V, KU, VU> { marker: PhantomData };
deserializer.deserialize_map(visitor)
}
}
impl<K, KU, V, VU> SerializeAs<Py<PyDict>> for LocalPyDictCustom<K, V, KU, VU>
where
KU: SerializeAs<K>,
VU: SerializeAs<Py<V>>,
for<'a> K: Clone + pyo3::FromPyObject<'a>,
V: PyClass + Clone,
{
fn serialize_as<S>(source: &Py<PyDict>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let gil = Python::acquire_gil();
let py = gil.python();
let mut map = s.serialize_map(Some(source.as_ref(py).len()))?;
for (key, value) in source.as_ref(py).iter() {
let key: K = key.extract().unwrap();
let value: Py<V> = value.extract().unwrap();
map.serialize_entry(
&SerializeAsWrap::<K, KU>::new(&key),
&SerializeAsWrap::<Py<V>, VU>::new(&value),
)?;
}
map.end()
}
} |
Beta Was this translation helpful? Give feedback.
Hi @Lehona,
I am confused what exactly your code does, since you seem to be using different definitions throughout your message and not all code is posted.
Correct me if I'm wrong. I think you want to define a custom serialize/deserialize implementation for the
PyDict
type which allows you to change how the key/value are converted. So similar to howHashMap
andBTreeMap
work but forPyDict
?The definition for these two is this macro
serde_with/src/ser/impls.rs
Lines 66 to 86 in df2d69c