From b750b3ef30785a35331d7f6f795150e8f9e3ebf1 Mon Sep 17 00:00:00 2001 From: Kevin Cantu Date: Thu, 30 Aug 2012 16:39:56 -0700 Subject: [PATCH 1/4] Pretty print JSON: indentation and newlines --- src/libstd/json.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/libstd/json.rs b/src/libstd/json.rs index b8fe2ec267aa0..3bcfa0022af46 100644 --- a/src/libstd/json.rs +++ b/src/libstd/json.rs @@ -85,6 +85,69 @@ 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 ss = ~""; + 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(v) => { + // [ + wr.write_str(spaces(indent)); + wr.write_str("[ "); + + // [ elem, + // elem, + // elem ] + let inner_indent = indent + 2; + let mut first = true; + for (*v).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(~" ]"); + } + Dict(d) => { + // { + wr.write_str(spaces(indent)); + wr.write_str(~"{ "); + + // { k: v, + // k: v, + // k: v } + let inner_indent = indent + 2; + let mut first = true; + for d.each |key, value| { + if !first { + wr.write_str(~",\n"); + wr.write_str(spaces(inner_indent)); + } + first = false; + let key = str::append(escape_str(key), ~": "); + let key_indent = str::len(key); + wr.write_str(key); + to_writer_pretty(wr, value, key_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 +173,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, From d87fca2698eb9841a63d44a89d43bab3561e4e56 Mon Sep 17 00:00:00 2001 From: Kevin Cantu Date: Thu, 30 Aug 2012 17:38:07 -0700 Subject: [PATCH 2/4] Pretty print JSON: sorting object keys --- src/libstd/json.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/libstd/json.rs b/src/libstd/json.rs index 3bcfa0022af46..b1dbcaa4a0f20 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; @@ -88,8 +91,8 @@ 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 ss = ~""; - n.times { str::push_str(ss, " "); } + let mut ss = ~""; + for n.times { str::push_str(ss, " "); } return ss; } @@ -97,7 +100,7 @@ fn to_writer_pretty(wr: io::Writer, j: Json, indent: uint) { 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(v) => { + List(vv) => { // [ wr.write_str(spaces(indent)); wr.write_str("[ "); @@ -107,7 +110,7 @@ fn to_writer_pretty(wr: io::Writer, j: Json, indent: uint) { // elem ] let inner_indent = indent + 2; let mut first = true; - for (*v).each |item| { + for (*vv).each |item| { if !first { wr.write_str(~",\n"); wr.write_str(spaces(inner_indent)); @@ -119,7 +122,16 @@ fn to_writer_pretty(wr: io::Writer, j: Json, indent: uint) { // ] wr.write_str(~" ]"); } - Dict(d) => { + Dict(dd) => { + // 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(spaces(indent)); wr.write_str(~"{ "); @@ -129,7 +141,8 @@ fn to_writer_pretty(wr: io::Writer, j: Json, indent: uint) { // k: v } let inner_indent = indent + 2; let mut first = true; - for d.each |key, value| { + for sorted_pairs.each |kv| { + let (key, value) = kv; if !first { wr.write_str(~",\n"); wr.write_str(spaces(inner_indent)); From 36a04bde18c6c3412ad95f88849fbfe58e81ea8d Mon Sep 17 00:00:00 2001 From: Kevin Cantu Date: Thu, 30 Aug 2012 20:12:10 -0700 Subject: [PATCH 3/4] Fix JSON parsing of unicode escapes --- src/libstd/json.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/libstd/json.rs b/src/libstd/json.rs index b1dbcaa4a0f20..e7c52cdc7966f 100644 --- a/src/libstd/json.rs +++ b/src/libstd/json.rs @@ -427,17 +427,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); From 75f9e6962d8328822c02e925547f965b45ac3a17 Mon Sep 17 00:00:00 2001 From: Kevin Cantu Date: Thu, 30 Aug 2012 21:03:19 -0700 Subject: [PATCH 4/4] Various JSON pretty printing tweaks --- src/libstd/json.rs | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/libstd/json.rs b/src/libstd/json.rs index e7c52cdc7966f..d60c35fa2f50f 100644 --- a/src/libstd/json.rs +++ b/src/libstd/json.rs @@ -101,14 +101,20 @@ fn to_writer_pretty(wr: io::Writer, j: Json, indent: uint) { 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(spaces(indent)); - wr.write_str("[ "); + wr.write_str("[\n"); + wr.write_str(spaces(inner_indent)); // [ elem, // elem, // elem ] - let inner_indent = indent + 2; let mut first = true; for (*vv).each |item| { if !first { @@ -120,9 +126,18 @@ fn to_writer_pretty(wr: io::Writer, j: Json, indent: uint) { }; // ] - wr.write_str(~" ]"); + 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| { @@ -133,13 +148,12 @@ fn to_writer_pretty(wr: io::Writer, j: Json, indent: uint) { let sorted_pairs = sort::merge_sort(|a,b| *a <= *b, pairs); // { - wr.write_str(spaces(indent)); - wr.write_str(~"{ "); + wr.write_str(~"{\n"); + wr.write_str(spaces(inner_indent)); // { k: v, // k: v, // k: v } - let inner_indent = indent + 2; let mut first = true; for sorted_pairs.each |kv| { let (key, value) = kv; @@ -149,13 +163,15 @@ fn to_writer_pretty(wr: io::Writer, j: Json, indent: uint) { } first = false; let key = str::append(escape_str(key), ~": "); - let key_indent = str::len(key); + let key_indent = inner_indent + str::len(key); wr.write_str(key); to_writer_pretty(wr, value, key_indent); }; // } - wr.write_str(~" }"); + wr.write_str(~"\n"); + wr.write_str(spaces(indent)); + wr.write_str(~"}"); } Null => wr.write_str(~"null") } @@ -879,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(~"[") ==