Docs, json::Value::U64, and bring many of the rust-serialize fns to Value

This commit is contained in:
Erick Tryzelaar 2015-03-18 20:51:48 -07:00
parent eb4af09456
commit 8821421357
6 changed files with 434 additions and 60 deletions

View File

@ -130,17 +130,16 @@ impl<Iter> Deserializer<Iter>
fn parse_number<V>(&mut self, mut visitor: V) -> Result<V::Value, Error>
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<Iter> Deserializer<Iter>
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<i64, Error> {
fn parse_integer(&mut self) -> Result<u64, Error> {
let mut res = 0;
match self.ch_or_null() {
@ -177,7 +191,7 @@ impl<Iter> Deserializer<Iter>
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,

View File

@ -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<T>`, but also allowing objects of different types in the
//! same array
//! * `Object`: equivalent to rust's `BTreeMap<String, serde::json::Value>`
//! * `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<u8>,
//! }
//!
//! 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::{

View File

@ -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<Value>),
Object(BTreeMap<String, Value>),
}
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<String, Value>> {
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<String, Value>> {
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<Value>> {
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<Value>> {
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<i64> {
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<u64> {
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<f64> {
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<bool> {
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<S>(&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<E>(&mut self, value: i64) -> Result<Value, E> {
Ok(Value::I64(value))
if value < 0 {
Ok(Value::I64(value))
} else {
Ok(Value::U64(value as u64))
}
}
#[inline]
fn visit_u64<E>(&mut self, value: u64) -> Result<Value, E> {
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) => {

View File

@ -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]),

View File

@ -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));
}

View File

@ -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 {