diff --git a/src/json/de.rs b/src/json/de.rs index 038a0f26..4d485ba9 100644 --- a/src/json/de.rs +++ b/src/json/de.rs @@ -130,17 +130,16 @@ impl Deserializer fn parse_number(&mut self, mut visitor: V) -> Result where V: de::Visitor, { - let mut neg = 1; + let mut neg = false; if self.ch_is(b'-') { self.bump(); - neg = -1; + neg = true; } let res = try!(self.parse_integer()); if self.ch_is(b'.') || self.ch_is(b'e') || self.ch_is(b'E') { - let neg = neg as f64; let mut res = res as f64; if self.ch_is(b'.') { @@ -151,13 +150,28 @@ impl Deserializer res = try!(self.parse_exponent(res)); } - visitor.visit_f64(neg * res) + if neg { + visitor.visit_f64(-res) + } else { + visitor.visit_f64(res) + } } else { - visitor.visit_i64(neg * res) + if neg { + let res = -(res as i64); + + // Make sure we didn't underflow. + if res > 0 { + Err(self.error(ErrorCode::InvalidNumber)) + } else { + visitor.visit_i64(res) + } + } else { + visitor.visit_u64(res) + } } } - fn parse_integer(&mut self) -> Result { + fn parse_integer(&mut self) -> Result { let mut res = 0; match self.ch_or_null() { @@ -177,7 +191,7 @@ impl Deserializer match self.ch_or_null() { c @ b'0' ... b'9' => { res *= 10; - res += (c as i64) - (b'0' as i64); + res += (c as u64) - (b'0' as u64); self.bump(); } _ => break, diff --git a/src/json/mod.rs b/src/json/mod.rs index 4c1c6716..90220135 100644 --- a/src/json/mod.rs +++ b/src/json/mod.rs @@ -1,3 +1,135 @@ +//! JSON and serialization +//! +//! # What is JSON? +//! +//! JSON (JavaScript Object Notation) is a way to write data in JavaScript. Like XML, it allows to +//! encode structured data in a text format that can be easily read by humans. Its simple syntax +//! and native compatibility with JavaScript have made it a widely used format. +//! +//! Data types that can be encoded are JavaScript types (see the `serde::json:Value` enum for more +//! details): +//! +//! * `Boolean`: equivalent to rust's `bool` +//! * `I64`: equivalent to rust's `i64` +//! * `U64`: equivalent to rust's `u64` +//! * `F64`: equivalent to rust's `i64` +//! * `String`: equivalent to rust's `String` +//! * `Array`: equivalent to rust's `Vec`, but also allowing objects of different types in the +//! same array +//! * `Object`: equivalent to rust's `BTreeMap` +//! * `Null` +//! +//! An object is a series of string keys mapping to values, in `"key": value` format. Arrays are +//! enclosed in square brackets ([ ... ]) and objects in curly brackets ({ ... }). A simple JSON +//! document encoding a person, his/her age, address and phone numbers could look like +//! +//! ```ignore +//! { +//! "FirstName": "John", +//! "LastName": "Doe", +//! "Age": 43, +//! "Address": { +//! "Street": "Downing Street 10", +//! "City": "London", +//! "Country": "Great Britain" +//! }, +//! "PhoneNumbers": [ +//! "+44 1234567", +//! "+44 2345678" +//! ] +//! } +//! ``` +//! +//! # Type-based Serialization and Deserialization +//! +//! Serde provides a mechanism for low boilerplate serialization & deserialization of values to and +//! from JSON via the serialization API. To be able to serialize a piece of data, it must implement +//! the `serde::Serialize` trait. To be able to deserialize a piece of data, it must implement the +//! `serde::Deserialize` trait. Serde provides provides an annotation to automatically generate +//! the code for these traits: `#[derive_serialize]` and `#[derive_deserialize]`. +//! +//! The JSON API also provides an enum `serde::json::Value` and a method `to_value` to serialize +//! objects. A `serde::json::Value` value can be serialized as a string or buffer using the +//! functions described above. You can also use the `json::Encoder` object, which implements the +//! `Encoder` trait. +//! +//! # Examples of use +//! +//! ## Using Autoserialization +//! +//! Create a struct called `TestStruct` and serialize and deserialize it to and from JSON using the +//! serialization API, using the derived serialization code. +//! +//! ```rust +//! // Required to use the annotations. +//! #![feature(custom_derive, plugin)] +//! #![plugin(serde_macros)] +//! +//! extern crate serde; +//! +//! use serde::json; +//! +//! // Automatically generate `Serialize` and `Deserialize` trait implementations +//! #[derive_serialize] +//! #[derive_deserialize] +//! pub struct TestStruct { +//! data_int: u8, +//! data_str: String, +//! data_vector: Vec, +//! } +//! +//! fn main() { +//! let object = TestStruct { +//! data_int: 1, +//! data_str: "homura".to_string(), +//! data_vector: vec![2,3,4,5], +//! }; +//! +//! // Serialize using `json::to_string` +//! let serialized = json::to_string(&object).unwrap(); +//! +//! // Deserialize using `json::from_str` +//! let deserialized: TestStruct = json::from_str(&serialized).unwrap(); +//! } +//! ``` +//! +//! ## Parsing a `str` to `Value` and reading the result +//! +//! ```rust +//! #![feature(custom_derive, plugin)] +//! #![plugin(serde_macros)] +//! +//! extern crate serde; +//! +//! use serde::json::{self, Value}; +//! +//! fn main() { +//! let data: Value = json::from_str("{\"foo\": 13, \"bar\": \"baz\"}").unwrap(); +//! println!("data: {:?}", data); +//! // data: {"bar":"baz","foo":13} +//! println!("object? {}", data.is_object()); +//! // object? true +//! +//! let obj = data.as_object().unwrap(); +//! let foo = obj.get("foo").unwrap(); +//! +//! println!("array? {:?}", foo.as_array()); +//! // array? None +//! println!("u64? {:?}", foo.as_u64()); +//! // u64? Some(13u64) +//! +//! for (key, value) in obj.iter() { +//! println!("{}: {}", key, match *value { +//! Value::U64(v) => format!("{} (u64)", v), +//! Value::String(ref v) => format!("{} (string)", v), +//! _ => format!("other") +//! }); +//! } +//! // bar: baz (string) +//! // foo: 13 (u64) +//! } +//! ``` + pub use self::de::{Deserializer, from_str}; pub use self::error::{Error, ErrorCode}; pub use self::ser::{ diff --git a/src/json/value.rs b/src/json/value.rs index 386225fc..0f1b103b 100644 --- a/src/json/value.rs +++ b/src/json/value.rs @@ -1,6 +1,7 @@ use std::collections::{BTreeMap, btree_map}; use std::fmt; use std::io; +use std::num; use std::str; use std::vec; @@ -13,12 +14,212 @@ pub enum Value { Null, Bool(bool), I64(i64), + U64(u64), F64(f64), String(String), Array(Vec), Object(BTreeMap), } +impl Value { + /// If the `Value` is an Object, returns the value associated with the provided key. + /// Otherwise, returns None. + pub fn find<'a>(&'a self, key: &str) -> Option<&'a Value>{ + match self { + &Value::Object(ref map) => map.get(key), + _ => None + } + } + + /// Attempts to get a nested Value Object for each key in `keys`. + /// If any key is found not to exist, find_path will return None. + /// Otherwise, it will return the `Value` associated with the final key. + pub fn find_path<'a>(&'a self, keys: &[&str]) -> Option<&'a Value>{ + let mut target = self; + for key in keys.iter() { + match target.find(*key) { + Some(t) => { target = t; }, + None => return None + } + } + Some(target) + } + + /// If the `Value` is an Object, performs a depth-first search until + /// a value associated with the provided key is found. If no value is found + /// or the `Value` is not an Object, returns None. + pub fn search<'a>(&'a self, key: &str) -> Option<&'a Value> { + match self { + &Value::Object(ref map) => { + match map.get(key) { + Some(json_value) => Some(json_value), + None => { + for (_, v) in map.iter() { + match v.search(key) { + x if x.is_some() => return x, + _ => () + } + } + None + } + } + }, + _ => None + } + } + + /// Returns true if the `Value` is an Object. Returns false otherwise. + pub fn is_object<'a>(&'a self) -> bool { + self.as_object().is_some() + } + + /// If the `Value` is an Object, returns the associated BTreeMap. + /// Returns None otherwise. + pub fn as_object<'a>(&'a self) -> Option<&'a BTreeMap> { + match self { + &Value::Object(ref map) => Some(map), + _ => None + } + } + + /// If the `Value` is an Object, returns the associated mutable BTreeMap. + /// Returns None otherwise. + pub fn as_object_mut<'a>(&'a mut self) -> Option<&'a mut BTreeMap> { + match self { + &mut Value::Object(ref mut map) => Some(map), + _ => None + } + } + + /// Returns true if the `Value` is an Array. Returns false otherwise. + pub fn is_array<'a>(&'a self) -> bool { + self.as_array().is_some() + } + + /// If the `Value` is an Array, returns the associated vector. + /// Returns None otherwise. + pub fn as_array<'a>(&'a self) -> Option<&'a Vec> { + match self { + &Value::Array(ref array) => Some(&*array), + _ => None + } + } + + /// If the `Value` is an Array, returns the associated mutable vector. + /// Returns None otherwise. + pub fn as_array_mut<'a>(&'a mut self) -> Option<&'a mut Vec> { + match self { + &mut Value::Array(ref mut list) => Some(list), + _ => None + } + } + + /// Returns true if the `Value` is a String. Returns false otherwise. + pub fn is_string<'a>(&'a self) -> bool { + self.as_string().is_some() + } + + /// If the `Value` is a String, returns the associated str. + /// Returns None otherwise. + pub fn as_string<'a>(&'a self) -> Option<&'a str> { + match *self { + Value::String(ref s) => Some(&s), + _ => None + } + } + + /// Returns true if the `Value` is a Number. Returns false otherwise. + pub fn is_number(&self) -> bool { + match *self { + Value::I64(_) | Value::U64(_) | Value::F64(_) => true, + _ => false, + } + } + + /// Returns true if the `Value` is a i64. Returns false otherwise. + pub fn is_i64(&self) -> bool { + match *self { + Value::I64(_) => true, + _ => false, + } + } + + /// Returns true if the `Value` is a u64. Returns false otherwise. + pub fn is_u64(&self) -> bool { + match *self { + Value::U64(_) => true, + _ => false, + } + } + + /// Returns true if the `Value` is a f64. Returns false otherwise. + pub fn is_f64(&self) -> bool { + match *self { + Value::F64(_) => true, + _ => false, + } + } + + /// If the `Value` is a number, return or cast it to a i64. + /// Returns None otherwise. + pub fn as_i64(&self) -> Option { + match *self { + Value::I64(n) => Some(n), + Value::U64(n) => num::cast(n), + _ => None + } + } + + /// If the `Value` is a number, return or cast it to a u64. + /// Returns None otherwise. + pub fn as_u64(&self) -> Option { + match *self { + Value::I64(n) => num::cast(n), + Value::U64(n) => Some(n), + _ => None + } + } + + /// If the `Value` is a number, return or cast it to a f64. + /// Returns None otherwise. + pub fn as_f64(&self) -> Option { + match *self { + Value::I64(n) => num::cast(n), + Value::U64(n) => num::cast(n), + Value::F64(n) => Some(n), + _ => None + } + } + + /// Returns true if the `Value` is a Boolean. Returns false otherwise. + pub fn is_boolean(&self) -> bool { + self.as_boolean().is_some() + } + + /// If the `Value` is a Boolean, returns the associated bool. + /// Returns None otherwise. + pub fn as_boolean(&self) -> Option { + match self { + &Value::Bool(b) => Some(b), + _ => None + } + } + + /// Returns true if the `Value` is a Null. Returns false otherwise. + pub fn is_null(&self) -> bool { + self.as_null().is_some() + } + + /// If the `Value` is a Null, returns (). + /// Returns None otherwise. + pub fn as_null(&self) -> Option<()> { + match self { + &Value::Null => Some(()), + _ => None + } + } +} + impl ser::Serialize for Value { #[inline] fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> @@ -28,6 +229,7 @@ impl ser::Serialize for Value { Value::Null => serializer.visit_unit(), Value::Bool(v) => serializer.visit_bool(v), Value::I64(v) => serializer.visit_i64(v), + Value::U64(v) => serializer.visit_u64(v), Value::F64(v) => serializer.visit_f64(v), Value::String(ref v) => serializer.visit_str(&v), Value::Array(ref v) => v.serialize(serializer), @@ -53,7 +255,16 @@ impl de::Deserialize for Value { #[inline] fn visit_i64(&mut self, value: i64) -> Result { - Ok(Value::I64(value)) + if value < 0 { + Ok(Value::I64(value)) + } else { + Ok(Value::U64(value as u64)) + } + } + + #[inline] + fn visit_u64(&mut self, value: u64) -> Result { + Ok(Value::U64(value)) } #[inline] @@ -173,13 +384,17 @@ impl ser::Serializer for Serializer { #[inline] fn visit_i64(&mut self, value: i64) -> Result<(), ()> { - self.state.push(State::Value(Value::I64(value))); + if value < 0 { + self.state.push(State::Value(Value::I64(value))); + } else { + self.state.push(State::Value(Value::U64(value as u64))); + } Ok(()) } #[inline] fn visit_u64(&mut self, value: u64) -> Result<(), ()> { - self.state.push(State::Value(Value::I64(value as i64))); + self.state.push(State::Value(Value::U64(value))); Ok(()) } @@ -386,6 +601,7 @@ impl de::Deserializer for Deserializer { Value::Null => visitor.visit_unit(), Value::Bool(v) => visitor.visit_bool(v), Value::I64(v) => visitor.visit_i64(v), + Value::U64(v) => visitor.visit_u64(v), Value::F64(v) => visitor.visit_f64(v), Value::String(v) => visitor.visit_string(v), Value::Array(v) => { diff --git a/tests/test_json.rs b/tests/test_json.rs index 02d87478..02381bf1 100644 --- a/tests/test_json.rs +++ b/tests/test_json.rs @@ -711,13 +711,20 @@ fn test_parse_number_errors() { #[test] fn test_parse_i64() { test_parse_ok(vec![ - ("3", 3), ("-2", -2), ("-1234", -1234), (" -1234 ", -1234), ]); } +#[test] +fn test_parse_u64() { + test_parse_ok(vec![ + ("3", 3u64), + ("1234", 1234), + ]); +} + #[test] fn test_parse_f64() { test_parse_ok(vec![ @@ -778,28 +785,28 @@ fn test_parse_list() { ]); test_parse_ok(vec![ - ("[3,1]", vec![3, 1]), + ("[3,1]", vec![3u64, 1]), (" [ 3 , 1 ] ", vec![3, 1]), ]); test_parse_ok(vec![ - ("[[3], [1, 2]]", vec![vec![3], vec![1, 2]]), + ("[[3], [1, 2]]", vec![vec![3u64], vec![1, 2]]), ]); test_parse_ok(vec![ - ("[1]", (1,)), + ("[1]", (1u64,)), ]); test_parse_ok(vec![ - ("[1, 2]", (1, 2)), + ("[1, 2]", (1u64, 2u64)), ]); test_parse_ok(vec![ - ("[1, 2, 3]", (1, 2, 3)), + ("[1, 2, 3]", (1u64, 2u64, 3u64)), ]); test_parse_ok(vec![ - ("[1, [2, 3]]", (1, (2, 3))), + ("[1, [2, 3]]", (1u64, (2u64, 3u64))), ]); let v: () = from_str("[]").unwrap(); @@ -828,7 +835,7 @@ fn test_parse_object() { ("{ }", treemap!()), ( "{\"a\":3}", - treemap!("a".to_string() => 3) + treemap!("a".to_string() => 3u64) ), ( "{ \"a\" : 3 }", @@ -847,7 +854,12 @@ fn test_parse_object() { test_parse_ok(vec![ ( "{\"a\": {\"b\": 3, \"c\": 4}}", - treemap!("a".to_string() => treemap!("b".to_string() => 3, "c".to_string() => 4)), + treemap!( + "a".to_string() => treemap!( + "b".to_string() => 3u64, + "c".to_string() => 4 + ) + ), ), ]); } @@ -962,7 +974,7 @@ fn test_parse_enum() { #[test] fn test_parse_trailing_whitespace() { test_parse_ok(vec![ - ("[1, 2] ", vec![1, 2]), + ("[1, 2] ", vec![1u64, 2]), ("[1, 2]\n", vec![1, 2]), ("[1, 2]\t", vec![1, 2]), ("[1, 2]\t \n", vec![1, 2]), diff --git a/tests/test_json_builder.rs b/tests/test_json_builder.rs index 18587b9e..66547fb8 100644 --- a/tests/test_json_builder.rs +++ b/tests/test_json_builder.rs @@ -15,12 +15,12 @@ fn test_array_builder() { .push(2) .push(3) .unwrap(); - assert_eq!(value, Value::Array(vec!(Value::I64(1), Value::I64(2), Value::I64(3)))); + assert_eq!(value, Value::Array(vec!(Value::U64(1), Value::U64(2), Value::U64(3)))); let value = ArrayBuilder::new() .push_array(|bld| bld.push(1).push(2).push(3)) .unwrap(); - assert_eq!(value, Value::Array(vec!(Value::Array(vec!(Value::I64(1), Value::I64(2), Value::I64(3)))))); + assert_eq!(value, Value::Array(vec!(Value::Array(vec!(Value::U64(1), Value::U64(2), Value::U64(3)))))); let value = ArrayBuilder::new() .push_object(|bld| @@ -30,8 +30,8 @@ fn test_array_builder() { .unwrap(); let mut map = BTreeMap::new(); - map.insert("a".to_string(), Value::I64(1)); - map.insert("b".to_string(), Value::I64(2)); + map.insert("a".to_string(), Value::U64(1)); + map.insert("b".to_string(), Value::U64(2)); assert_eq!(value, Value::Array(vec!(Value::Object(map)))); } @@ -46,7 +46,7 @@ fn test_object_builder() { .unwrap(); let mut map = BTreeMap::new(); - map.insert("a".to_string(), Value::I64(1)); - map.insert("b".to_string(), Value::I64(2)); + map.insert("a".to_string(), Value::U64(1)); + map.insert("b".to_string(), Value::U64(2)); assert_eq!(value, Value::Object(map)); } diff --git a/tests/test_macros.rs b/tests/test_macros.rs index 8e5a3d1a..6ee0f780 100644 --- a/tests/test_macros.rs +++ b/tests/test_macros.rs @@ -171,7 +171,7 @@ fn test_ser_named_tuple() { assert_eq!( json::to_value(&named_tuple), - Value::Array(vec![Value::I64(5), Value::I64(6), Value::I64(7)]) + Value::Array(vec![Value::U64(5), Value::U64(6), Value::U64(7)]) ); } @@ -185,9 +185,9 @@ fn test_de_named_tuple() { assert_eq!( json::from_str("[1,2,3]").unwrap(), Value::Array(vec![ - Value::I64(1), - Value::I64(2), - Value::I64(3), + Value::U64(1), + Value::U64(2), + Value::U64(3), ]) ); } @@ -211,9 +211,9 @@ fn test_ser_named_map() { assert_eq!( json::to_value(&named_map), Value::Object(btreemap![ - "a".to_string() => Value::I64(5), - "b".to_string() => Value::I64(6), - "c".to_string() => Value::I64(7) + "a".to_string() => Value::U64(5), + "b".to_string() => Value::U64(6), + "c".to_string() => Value::U64(7) ]) ); } @@ -233,9 +233,9 @@ fn test_de_named_map() { assert_eq!( json::from_value(Value::Object(btreemap![ - "a".to_string() => Value::I64(5), - "b".to_string() => Value::I64(6), - "c".to_string() => Value::I64(7) + "a".to_string() => Value::U64(5), + "b".to_string() => Value::U64(6), + "c".to_string() => Value::U64(7) ])).unwrap(), v ); @@ -288,12 +288,12 @@ fn test_ser_enum_seq() { )), Value::Object(btreemap!( "Seq".to_string() => Value::Array(vec![ - Value::I64(1), - Value::I64(2), - Value::I64(3), - //Value::I64(4), - Value::I64(5), - //Value::I64(6), + Value::U64(1), + Value::U64(2), + Value::U64(3), + //Value::U64(4), + Value::U64(5), + //Value::U64(6), ]) )) ); @@ -331,12 +331,12 @@ fn test_ser_enum_map() { }), Value::Object(btreemap!( "Map".to_string() => Value::Object(btreemap![ - "a".to_string() => Value::I64(1), - "b".to_string() => Value::I64(2), - "c".to_string() => Value::I64(3), - //"d".to_string() => Value::I64(4) - "e".to_string() => Value::I64(5) - //"f".to_string() => Value::I64(6) + "a".to_string() => Value::U64(1), + "b".to_string() => Value::U64(2), + "c".to_string() => Value::U64(3), + //"d".to_string() => Value::U64(4) + "e".to_string() => Value::U64(5) + //"f".to_string() => Value::U64(6) ]) )) ); @@ -381,12 +381,12 @@ fn test_de_enum_seq() { assert_eq!( json::from_value(Value::Object(btreemap!( "Seq".to_string() => Value::Array(vec![ - Value::I64(1), - Value::I64(2), - Value::I64(3), - //Value::I64(4), - Value::I64(5), - //Value::I64(6), + Value::U64(1), + Value::U64(2), + Value::U64(3), + //Value::U64(4), + Value::U64(5), + //Value::U64(6), ]) ))).unwrap(), DeEnum::Seq( @@ -424,12 +424,12 @@ fn test_de_enum_map() { assert_eq!( json::from_value(Value::Object(btreemap!( "Map".to_string() => Value::Object(btreemap![ - "a".to_string() => Value::I64(1), - "b".to_string() => Value::I64(2), - "c".to_string() => Value::I64(3), - //"d".to_string() => Value::I64(4) - "e".to_string() => Value::I64(5) - //"f".to_string() => Value::I64(6) + "a".to_string() => Value::U64(1), + "b".to_string() => Value::U64(2), + "c".to_string() => Value::U64(3), + //"d".to_string() => Value::U64(4) + "e".to_string() => Value::U64(5) + //"f".to_string() => Value::U64(6) ]) ))).unwrap(), DeEnum::Map {