Allow integers to be used as map keys again

This commit is contained in:
David Tolnay 2017-04-27 12:47:07 -07:00
parent 8e48fa0fb2
commit 83e3ec0a45
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
4 changed files with 240 additions and 61 deletions

View File

@ -852,7 +852,7 @@ impl<'de, 'a, R: Read<'de> + 'a> de::MapAccess<'de> for MapAccess<'a, R> {
};
match peek {
Some(b'"') => Ok(Some(try!(seed.deserialize(&mut *self.de)))),
Some(b'"') => seed.deserialize(MapKey { de: &mut *self.de }).map(Some),
Some(_) => Err(self.de.peek_error(ErrorCode::KeyMustBeAString)),
None => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)),
}
@ -973,6 +973,102 @@ impl<'de, 'a, R: Read<'de> + 'a> de::VariantAccess<'de> for UnitVariantAccess<'a
}
}
/// Only deserialize from this after peeking a '"' byte! Otherwise it may
/// deserialize invalid JSON successfully.
struct MapKey<'a, R: 'a> {
de: &'a mut Deserializer<R>,
}
macro_rules! deserialize_integer_key {
($deserialize:ident => $visit:ident) => {
fn $deserialize<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.de.eat_char();
self.de.str_buf.clear();
let string = try!(self.de.read.parse_str(&mut self.de.str_buf));
let integer = try!(string.parse().map_err(de::Error::custom));
visitor.$visit(integer)
}
}
}
impl<'de, 'a, R> de::Deserializer<'de> for MapKey<'a, R>
where
R: Read<'de>,
{
type Error = Error;
#[inline]
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.de.parse_value(visitor)
}
deserialize_integer_key!(deserialize_i8 => visit_i8);
deserialize_integer_key!(deserialize_i16 => visit_i16);
deserialize_integer_key!(deserialize_i32 => visit_i32);
deserialize_integer_key!(deserialize_i64 => visit_i64);
deserialize_integer_key!(deserialize_u8 => visit_u8);
deserialize_integer_key!(deserialize_u16 => visit_u16);
deserialize_integer_key!(deserialize_u32 => visit_u32);
deserialize_integer_key!(deserialize_u64 => visit_u64);
#[inline]
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
// Map keys cannot be null.
visitor.visit_some(self)
}
#[inline]
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
#[inline]
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.de.deserialize_enum(name, variants, visitor)
}
#[inline]
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.de.deserialize_bytes(visitor)
}
#[inline]
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.de.deserialize_bytes(visitor)
}
forward_to_deserialize_any! {
bool f32 f64 char str string unit unit_struct seq tuple tuple_struct map
struct identifier ignored_any
}
}
//////////////////////////////////////////////////////////////////////////////
/// Iterator that deserializes a stream into multiple JSON values.

View File

@ -7,6 +7,7 @@
// except according to those terms.
use std::{char, cmp, io, str};
use std::ops::Deref;
use iter::LineColIterator;
@ -82,6 +83,17 @@ pub enum Reference<'b, 'c, T: ?Sized + 'static> {
Copied(&'c T),
}
impl<'b, 'c, T: ?Sized + 'static> Deref for Reference<'b, 'c, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match *self {
Reference::Borrowed(b) => b,
Reference::Copied(c) => c,
}
}
}
/// JSON input source that reads from a std::io input stream.
pub struct IoRead<R>
where

View File

@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::fmt;
use std::i64;
use std::io;
@ -461,7 +462,8 @@ impl<'de> MapAccess<'de> for MapDeserializer {
match self.iter.next() {
Some((key, value)) => {
self.value = Some(value);
seed.deserialize(key.into_deserializer()).map(Some)
let key_de = MapKeyDeserializer { key: Cow::Owned(key) };
seed.deserialize(key_de).map(Some)
}
None => Ok(None),
}
@ -770,7 +772,8 @@ impl<'de> MapAccess<'de> for MapRefDeserializer<'de> {
match self.iter.next() {
Some((key, value)) => {
self.value = Some(value);
seed.deserialize((&**key).into_deserializer()).map(Some)
let key_de = MapKeyDeserializer { key: Cow::Borrowed(&**key) };
seed.deserialize(key_de).map(Some)
}
None => Ok(None),
}
@ -812,6 +815,76 @@ impl<'de> serde::Deserializer<'de> for MapRefDeserializer<'de> {
}
}
struct MapKeyDeserializer<'de> {
key: Cow<'de, str>,
}
macro_rules! deserialize_integer_key {
($deserialize:ident => $visit:ident) => {
fn $deserialize<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
let integer = try!(self.key.parse().map_err(serde::de::Error::custom));
visitor.$visit(integer)
}
}
}
impl<'de> serde::Deserializer<'de> for MapKeyDeserializer<'de> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.key.into_deserializer().deserialize_any(visitor)
}
deserialize_integer_key!(deserialize_i8 => visit_i8);
deserialize_integer_key!(deserialize_i16 => visit_i16);
deserialize_integer_key!(deserialize_i32 => visit_i32);
deserialize_integer_key!(deserialize_i64 => visit_i64);
deserialize_integer_key!(deserialize_u8 => visit_u8);
deserialize_integer_key!(deserialize_u16 => visit_u16);
deserialize_integer_key!(deserialize_u32 => visit_u32);
deserialize_integer_key!(deserialize_u64 => visit_u64);
#[inline]
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
// Map keys cannot be null.
visitor.visit_some(self)
}
#[inline]
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.key.into_deserializer().deserialize_enum(name, variants, visitor)
}
forward_to_deserialize_any! {
bool f32 f64 char str string bytes byte_buf unit unit_struct seq tuple
tuple_struct map struct identifier ignored_any
}
}
impl Value {
fn unexpected(&self) -> Unexpected {
match *self {

View File

@ -568,33 +568,6 @@ fn test_write_newtype_struct() {
test_encode_ok(&[(outer, r#"{"outer":{"inner":123}}"#)]);
}
#[test]
fn test_write_map_with_integer_keys_issue_221() {
let mut map = BTreeMap::new();
map.insert(0, "x"); // map with integer key
assert_eq!(
serde_json::to_value(&map).unwrap(),
json!({"0": "x"})
);
test_encode_ok(&[(&map, r#"{"0":"x"}"#)]);
#[derive(Eq, PartialEq, Ord, PartialOrd)]
struct Float;
impl Serialize for Float {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_f32(1.0)
}
}
let mut map = BTreeMap::new();
map.insert(Float, "x"); // map with float key
assert!(serde_json::to_value(&map).is_err());
}
fn test_parse_ok<T>(tests: Vec<(&str, T)>)
where
T: Clone + Debug + PartialEq + ser::Serialize + de::DeserializeOwned,
@ -1504,32 +1477,6 @@ fn test_serialize_rejects_adt_keys() {
assert_eq!(err.to_string(), "key must be a string");
}
#[test]
fn test_effectively_string_keys() {
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Serialize, Deserialize)]
enum Enum {
Zero,
One,
}
let map = treemap! {
Enum::Zero => 0,
Enum::One => 1
};
let expected = r#"{"Zero":0,"One":1}"#;
assert_eq!(to_string(&map).unwrap(), expected);
assert_eq!(map, from_str(expected).unwrap());
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Serialize, Deserialize)]
struct Wrapper(String);
let map = treemap! {
Wrapper("zero".to_owned()) => 0,
Wrapper("one".to_owned()) => 1
};
let expected = r#"{"one":1,"zero":0}"#;
assert_eq!(to_string(&map).unwrap(), expected);
assert_eq!(map, from_str(expected).unwrap());
}
#[test]
fn test_bytes_ser() {
let buf = vec![];
@ -1675,15 +1622,66 @@ fn test_stack_overflow() {
}
#[test]
fn test_allow_ser_integers_as_map_keys() {
fn test_integer_key() {
// map with integer keys
let map = treemap!(
1 => 2,
2 => 4,
-1 => 6,
-2 => 8
-1 => 6
);
let j = r#"{"-1":6,"1":2}"#;
test_encode_ok(&[(&map, j)]);
test_parse_ok(vec![(j, map)]);
assert_eq!(to_string(&map).unwrap(), r#"{"-2":8,"-1":6,"1":2,"2":4}"#);
let j = r#"{"x":null}"#;
test_parse_err::<BTreeMap<i32, ()>>(
&[
(j, "invalid digit found in string at line 1 column 4"),
],
);
}
#[test]
fn test_deny_float_key() {
#[derive(Eq, PartialEq, Ord, PartialOrd)]
struct Float;
impl Serialize for Float {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_f32(1.0)
}
}
// map with float key
let map = treemap!(Float => "x");
assert!(serde_json::to_value(&map).is_err());
}
#[test]
fn test_effectively_string_keys() {
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Serialize, Deserialize)]
enum Enum {
One,
Two,
}
let map = treemap! {
Enum::One => 1,
Enum::Two => 2
};
let expected = r#"{"One":1,"Two":2}"#;
test_encode_ok(&[(&map, expected)]);
test_parse_ok(vec![(expected, map)]);
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Serialize, Deserialize)]
struct Wrapper(String);
let map = treemap! {
Wrapper("zero".to_owned()) => 0,
Wrapper("one".to_owned()) => 1
};
let expected = r#"{"one":1,"zero":0}"#;
test_encode_ok(&[(&map, expected)]);
test_parse_ok(vec![(expected, map)]);
}
#[test]