mozilla/rust — pull request #12780 by zslayton (2014-03-09 07:16:44+00:00)
Title: Added convenience methods to the Json class

This is my first non-docs contribution to Rust, so please let me know what I can fix. I probably should've submitted this to the mailing list first for comments, but it didn't take too long to implement so I figured I'd just give it a shot.

These changes are modeled loosely on the [JsonNode API](http://jackson.codehaus.org/1.7.9/javadoc/org/codehaus/jackson/JsonNode.html) provided by the [Jackson JSON processor](http://jackson.codehaus.org/).

Many common use cases for parsing JSON involve pulling one or more fields out of an object, however deeply nested. At present, this requires writing a pyramid of match statements. The added methods in this PR aim to make this a more painless process.

Example JSON:
```json
{
"successful" : true,
"status" : 200,
"error" : null,
"content" : {
"vehicles" : [
{"make" : "Toyota", "model" : "Camry", "year" : 1997},
{"make" : "Honda", "model" : "Accord", "year" : 2003}
]
}
}
```

Accessing "successful":
```rust
let example_json : Json = from_str("...above json...").unwrap();
let was_successful: Option = example_json.get_boolean(&~"successful");
```

Accessing "status":
```rust
let example_json : Json = from_str("...above json...").unwrap();
let status_code : Option = example_json.get_number(&~"status");
```

Accessing "vehicles":
```rust
let example_json : Json = from_str("...above json...").unwrap();
let vehicle_list: Option<&[Json]> = example_json.find_list(&~"vehicles");
```

Accessing "vehicles" with an explicit path:
```rust
let example_json : Json = from_str("...above json...").unwrap();
let vehicle_list: Option<&[Json]> = example_json.get_path_list(&[&~"content", &~"vehicles"]);
```

Accessing "error", which might be null or a string:
```rust
let example_json : Json = from_str("...above json...").unwrap();
let error: Option = example_json.get(&~"error");
if error.is_null() { // This would be nicer as a match, I'm just illustrating the boolean test methods
println!("Error is null, everything's fine.");
} else if error.is_str(){
println!("Something went wrong: {}", error.as_string().unwrap());
}
```

Some notes:
* Macros would help to eliminate some of the repetitiveness of the implementation, but I couldn't use them due to #4621.
* Would it be better to name methods after the Json enum type (e.g. `get_string`) or the associated Rust built-in? (e.g. `get_str`)
* TreeMap requires its keys to be &~str. Because of this, all of the new methods required &~str for their parameters. I'm uncertain what the best approach to fixing this is: neither demanding an owned pointer nor allocating within the methods to appease TreeMap's find() seems desirable. If I were able to take &str, people could put together paths easily with `"foo.bar.baz".split('.').collect();`
* At the moment, the `find_` methods all find the first match for the provided key and attempt to return that value if it's of the specified type. This makes sense to me, but it's possible that users would interpret a call to `find_boolean("successful")` as looking for the first "successful" item that was a boolean rather than looking for the first "successful" and returning None if it isn't boolean.

I hope this is helpful. Any feedback is appreciated!