diff --git a/src/libstd/json.rs b/src/libstd/json.rs index b8fe2ec267aa0..d60c35fa2f50f 100644 --- a/src/libstd/json.rs +++ b/src/libstd/json.rs @@ -11,11 +11,14 @@ import io::WriterUtil; import map; import map::hashmap; import map::map; +import sort; export Json; export Error; export to_writer; +export to_writer_pretty; export to_str; +export to_str_pretty; export from_reader; export from_str; export eq; @@ -85,6 +88,95 @@ fn to_writer(wr: io::Writer, j: Json) { } } +/// Serializes a json value into a io::writer +fn to_writer_pretty(wr: io::Writer, j: Json, indent: uint) { + fn spaces(n: uint) -> ~str { + let mut ss = ~""; + for n.times { str::push_str(ss, " "); } + return ss; + } + + match j { + Num(n) => wr.write_str(float::to_str(n, 6u)), + String(s) => wr.write_str(escape_str(*s)), + Boolean(b) => wr.write_str(if b { ~"true" } else { ~"false" }), + List(vv) => { + if vv.len() == 0u { + wr.write_str(~"[]"); + return; + } + + let inner_indent = indent + 2; + + // [ + wr.write_str("[\n"); + wr.write_str(spaces(inner_indent)); + + // [ elem, + // elem, + // elem ] + let mut first = true; + for (*vv).each |item| { + if !first { + wr.write_str(~",\n"); + wr.write_str(spaces(inner_indent)); + } + first = false; + to_writer_pretty(wr, item, inner_indent); + }; + + // ] + wr.write_str("\n"); + wr.write_str(spaces(indent)); + wr.write_str(~"]"); + } + Dict(dd) => { + if dd.size() == 0u { + wr.write_str(~"{}"); + return; + } + + let inner_indent = indent + 2; + + // convert from a dictionary + let mut pairs = ~[]; + for dd.each |key, value| { + vec::push(pairs, (key, value)); + } + + // sort by key strings + let sorted_pairs = sort::merge_sort(|a,b| *a <= *b, pairs); + + // { + wr.write_str(~"{\n"); + wr.write_str(spaces(inner_indent)); + + // { k: v, + // k: v, + // k: v } + let mut first = true; + for sorted_pairs.each |kv| { + let (key, value) = kv; + if !first { + wr.write_str(~",\n"); + wr.write_str(spaces(inner_indent)); + } + first = false; + let key = str::append(escape_str(key), ~": "); + let key_indent = inner_indent + str::len(key); + wr.write_str(key); + to_writer_pretty(wr, value, key_indent); + }; + + // } + wr.write_str(~"\n"); + wr.write_str(spaces(indent)); + wr.write_str(~"}"); + } + Null => wr.write_str(~"null") + } +} + fn escape_str(s: ~str) -> ~str { let mut escaped = ~"\""; do str::chars_iter(s) |c| { @@ -110,6 +202,11 @@ fn to_str(j: Json) -> ~str { io::with_str_writer(|wr| to_writer(wr, j)) } +/// Serializes a json value into a string, with whitespace and sorting +fn to_str_pretty(j: Json) -> ~str { + io::with_str_writer(|wr| to_writer_pretty(wr, j, 0)) +} + type Parser_ = { rdr: io::Reader, mut ch: char, @@ -346,17 +443,25 @@ impl Parser { while i < 4u { match self.next_char() { '0' to '9' => { - n = n * 10u + - (self.ch as uint) - ('0' as uint); - } - _ => return self.error(~"invalid \\u escape") + n = n * 16u + (self.ch as uint) + - ('0' as uint); + }, + 'a' | 'A' => n = n * 16u + 10u, + 'b' | 'B' => n = n * 16u + 11u, + 'c' | 'C' => n = n * 16u + 12u, + 'd' | 'D' => n = n * 16u + 13u, + 'e' | 'E' => n = n * 16u + 14u, + 'f' | 'F' => n = n * 16u + 15u, + _ => return self.error( + ~"invalid \\u escape (unrecognized hex)") } i += 1u; } // Error out if we didn't parse 4 digits. if i != 4u { - return self.error(~"invalid \\u escape"); + return self.error( + ~"invalid \\u escape (not four digits)"); } str::push_char(res, n as char); @@ -790,6 +895,12 @@ mod tests { assert from_str(~" \"foo\" ") == Ok(String(@~"foo")); } + #[test] + fn test_unicode_hex_escapes_in_str() { + assert from_str(~"\"\\u12ab\"") == Ok(String(@~"\u12ab")); + assert from_str(~"\"\\uAB12\"") == Ok(String(@~"\uAB12")); + } + #[test] fn test_read_list() { assert from_str(~"[") ==