Rust Json serialization overlapping responsibilities
I'm learning Json serialization in Rust, in particular, how to serialize Rust objects to Json.
Currently I see 3 methods of converting an instance of a struct to Json:
Deriving Encodable trait
Manual implementation of ToJson trait
Manual implementation of Encodable trait
Below code illustrates all 3 approaches:
extern crate serialize;
use serialize::{Encoder, Encodable, json};
use serialize::json::{Json, ToJson};
use std::collections::TreeMap;
fn main() {
let document = Document::new();
let word_document = WordDocument::new();
println!("1. Deriving `Encodable`: {}", json::encode(&document));
println!("2. Manually implementing `ToJson` trait: {}", document.to_json());
println!("3. Manually implementing `Encodable` trait: {}", json::encode(&word_document));
}
#[deriving(Encodable)]
struct Document<'a> {
metadata: Vec<(&'a str, &'a str)>
}
impl<'a> Document<'a> {
fn new() -> Document<'a> {
let metadata = vec!(("Title", "Untitled Document 1"));
Document {metadata: metadata}
}
}
impl<'a> ToJson for Document<'a> {
fn to_json(&self) -> Json {
let mut tm = TreeMap::new();
for &(ref mk, ref mv) in self.metadata.iter() {
tm.insert(mk.to_string(), mv.to_string().to_json());
}
json::Object(tm)
}
}
struct WordDocument<'a> {
metadata: Vec<(&'a str, &'a str)>
}
impl<'a> WordDocument<'a> {
fn new() -> WordDocument<'a> {
let metadata = vec!(("Title", "Untitled Word Document 1"));
WordDocument {metadata: metadata}
}
}
impl<'a, E, S: Encoder<E>> Encodable<S, E> for WordDocument<'a> {
fn encode(&self, s: &mut S) -> Result<(), E> {
s.emit_map(self.metadata.len(), |e| {
let mut i = 0;
for &(ref key, ref val) in self.metadata.iter() {
try!(e.emit_map_elt_key(i, |e| key.encode(e)));
try!(e.emit_map_elt_val(i, |e| val.encode(e)));
i += 1;
}
Ok(())
})
}
}
Rust playpen: http://is.gd/r7cYmE
Results:
1. Deriving `Encodable`: {"metadata":[["Title","Untitled Document 1"]]}
2. Manually implementing `ToJson` trait: {"Title":"Untitled Document 1"}
3. Manually implementing `Encodable` trait: {"Title":"Untitled Word Document 1"}
First method is fully automatic, but does not provide sufficient flexibility. Second and third achieve same level of flexibility by specifying the serialization process manually. In my case I want document metadata to be serialized as an object, not as an array (which is what deriving implementation gives me).
Questions :
If I want manual serialization, which method should I choose and why?
Am I right in assuming that method 2 will build a Json
enum in memory (besides the struct itself) and is a worse fit for huge documents (multi megabytes), while method 3 is streaming and safer for huge documents?
Why does rust stdlib use method 3 even for primitives, while not using method 2 internally?
Why do methods 2 and 3 exist at all? I don't understand the reason for the overlap between them. I would expect there to exist only one automatic (deriving) method of serialization and one manual.
Method 2 (the ToJson
trait) is specific to encoding JSON. It returns JSON objects, instead of writing to a stream. One example of use is for mapping to custom representations - see this example in the documentation.
Method 3 (implementing Encodable
) has to exist for method 1 to work.
Am I right in assuming that method 2 will build a Json enum in memory (besides the struct itself) and is a worse fit for huge documents (multi megabytes), while method 3 is streaming and safer for huge documents?
Yes. ToJson
creates a nested Json
enum of the whole object, while Encodable
streams to a Writer
.
If I want manual serialization, which method should I choose and why?
Why does rust stdlib use method 3 even for primitives, while not using method 2 internally?
You should use Encodable
. It is not specific to the JSON format, does not use an intermediate representation (so can be streamed instead of stored in memory) and should continue to work with new formats added to the library.
上一篇: summernote在img中添加类
下一篇: Rust Json序列化重叠责任