Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,19 @@ impl<'de> de::MapAccess<'de> for MapAccess {
fn next_key_seed<K: de::DeserializeSeed<'de>>(&mut self, seed: K) -> Result<Option<K::Value>> {
debug_assert!(self.next_value.is_none());

Ok(match self.iter.next().transpose()? {
Some(pair) => {
let (key, value) = convert_pair(pair);
self.next_value = Some(value);
Some(seed.deserialize(key)?)
let key_seed = loop {
match self.iter.next().transpose()? {
Some(pair) => {
let Some((key, value)) = convert_pair(pair) else {
continue;
};
self.next_value = Some(value);
break Some(seed.deserialize(key)?);
}
None => break None,
}
None => None,
})
};
Ok(key_seed)
}

fn next_value_seed<V: de::DeserializeSeed<'de>>(&mut self, seed: V) -> Result<V::Value> {
Expand Down Expand Up @@ -180,9 +185,14 @@ impl<'de> IntoDeserializer<'de, Error> for Deserializer {
}

/// Destructures a JS `[key, value]` pair into a tuple of [`Deserializer`]s.
fn convert_pair(pair: JsValue) -> (Deserializer, Deserializer) {
fn convert_pair(pair: JsValue) -> Option<(Deserializer, Deserializer)> {
let pair = pair.unchecked_into::<Array>();
(pair.get(0).into(), pair.get(1).into())
let value = pair.get(1);
if value.is_undefined() {
None
} else {
Some((pair.get(0).into(), value.into()))
}
}

impl Deserializer {
Expand Down Expand Up @@ -553,7 +563,9 @@ impl<'de> de::Deserializer<'de> for Deserializer {
match js_sys::try_iter(&self.value)? {
Some(iter) => visitor.visit_map(MapAccess::new(iter)),
None => match self.as_object_entries() {
Some(arr) => visitor.visit_map(MapDeserializer::new(arr.iter().map(convert_pair))),
Some(arr) => {
visitor.visit_map(MapDeserializer::new(arr.iter().filter_map(convert_pair)))
}
None => self.invalid_type(visitor),
},
}
Expand Down Expand Up @@ -597,7 +609,12 @@ impl<'de> de::Deserializer<'de> for Deserializer {
return Err(de::Error::invalid_length(entries.length() as _, &"1"));
}
let entry = entries.get(0);
let (tag, payload) = convert_pair(entry);
let Some((tag, payload)) = convert_pair(entry) else {
return Err(de::Error::invalid_type(
de::Unexpected::Unit,
&"a single key-value pair",
));
};
EnumAccess { tag, payload }
} else {
return self.invalid_type(visitor);
Expand Down
23 changes: 23 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,29 @@ fn serde_json_value_with_default() {
);
}

#[wasm_bindgen_test]
fn serde_json_value_with_js_value() {
let map = js_sys::Map::new();
map.set(&"a".into(), &"foo".into());
map.set(&"b".into(), &42.into());
map.set(&"c".into(), &JsValue::UNDEFINED);
map.set(&"d".into(), &JsValue::NULL);
let arr = js_sys::Array::new();
arr.set(0, JsValue::UNDEFINED);
arr.set(1, JsValue::NULL);
arr.set(2, JsValue::from_bool(true));
map.set(&"e".into(), &arr);

let obj = js_sys::Object::from_entries(&map).unwrap();
let obj_value: serde_json::Value = from_value(obj.clone().into()).unwrap();
let map_value: serde_json::Value = from_value(map.clone().into()).unwrap();
assert_eq!(map_value, obj_value);
assert_eq!(
js_sys::JSON::stringify(&obj).unwrap(),
serde_json::to_string(&obj_value).unwrap()
);
}

#[wasm_bindgen_test]
fn preserved_value() {
#[derive(serde::Deserialize, serde::Serialize, PartialEq, Clone, Debug)]
Expand Down
Loading