From: John Fremlin VII Date: Tue, 21 May 2013 23:04:40 +0000 (-0700) Subject: allow reading maps from number -> value X-Git-Tag: v0.22.0~960 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=a4c90474ff595398d94043f386634befa3ef4728;p=folly.git allow reading maps from number -> value Summary: The serialization of PHP values often uses integer -> value maps in JSON arrays. These are emitted by the standard stream << operator on dynamics but cannot be read. This diff fixes that. Test Plan: - read in serialized value with array "bucketing":{"days_stale":{0:{2:null,1:14.01,0:"more_than_two_weeks_stale"}}} Reviewed By: delong.j@fb.com FB internal diff: D805218 --- diff --git a/folly/json.cpp b/folly/json.cpp index a4992643..445b4b0a 100644 --- a/folly/json.cpp +++ b/folly/json.cpp @@ -251,9 +251,10 @@ struct ParseError : std::runtime_error { // Wraps our input buffer with some helper functions. struct Input { - explicit Input(StringPiece range) - : range_(range) - , lineNum_(0) + explicit Input(StringPiece range, json::serialization_opts const* opts) + : range_(range) + , opts_(*opts) + , lineNum_(0) { storeCurrent(); } @@ -351,6 +352,10 @@ struct Input { throw ParseError(lineNum_, context(), what); } + json::serialization_opts const& getOpts() { + return opts_; + } + private: void storeCurrent() { current_ = range_.empty() ? EOF : range_.front(); @@ -358,12 +363,14 @@ private: private: StringPiece range_; + json::serialization_opts const& opts_; unsigned lineNum_; int current_; }; dynamic parseValue(Input& in); fbstring parseString(Input& in); +dynamic parseNumber(Input& in); dynamic parseObject(Input& in) { assert(*in == '{'); @@ -378,14 +385,22 @@ dynamic parseObject(Input& in) { } for (;;) { - if (*in != '\"') { + if (*in == '\"') { // string + auto key = parseString(in); + in.skipWhitespace(); + in.expect(':'); + in.skipWhitespace(); + ret.insert(std::move(key), parseValue(in)); + } else if (!in.getOpts().allow_non_string_keys) { in.error("expected string for object key name"); + } else { + auto key = parseValue(in); + in.skipWhitespace(); + in.expect(':'); + in.skipWhitespace(); + ret.insert(std::move(key), parseValue(in)); } - auto key = parseString(in); - in.skipWhitespace(); - in.expect(':'); - in.skipWhitespace(); - ret.insert(std::move(key), parseValue(in)); + in.skipWhitespace(); if (*in != ',') { break; @@ -665,11 +680,18 @@ void escapeString(StringPiece input, ////////////////////////////////////////////////////////////////////// dynamic parseJson(StringPiece range) { - json::Input in(range); + return parseJson(range, json::serialization_opts()); +} + +dynamic parseJson( + StringPiece range, + json::serialization_opts const& opts) { + + json::Input in(range, &opts); auto ret = parseValue(in); in.skipWhitespace(); - if (*in != '\0' && in.size()) { + if (in.size() && *in != '\0') { in.error("parsing didn't consume all input"); } return ret; diff --git a/folly/json.h b/folly/json.h index 11024281..84a70661 100644 --- a/folly/json.h +++ b/folly/json.h @@ -106,6 +106,7 @@ namespace json { * Parse a json blob out of a range and produce a dynamic representing * it. */ +dynamic parseJson(StringPiece, json::serialization_opts const&); dynamic parseJson(StringPiece); /* diff --git a/folly/test/JsonTest.cpp b/folly/test/JsonTest.cpp index ff1c9008..b3165308 100644 --- a/folly/test/JsonTest.cpp +++ b/folly/test/JsonTest.cpp @@ -314,6 +314,33 @@ TEST(Json, UTF8Validation) { EXPECT_ANY_THROW(folly::json::serialize("a\xe0\xa0\x80z\xe0\x80\x80", opts)); } + +TEST(Json, ParseNonStringKeys) { + // test string keys + EXPECT_EQ("a", parseJson("{\"a\":[]}").items().begin().first.asString()); + + // check that we don't allow non-string keys as this violates the + // strict JSON spec (though it is emitted by the output of + // folly::dynamic with operator <<). + EXPECT_THROW(parseJson("{1:[]}"), std::runtime_error); + + // check that we can parse colloquial JSON if the option is set + folly::json::serialization_opts opts; + opts.allow_non_string_keys = true; + + auto val = parseJson("{1:[]}", opts); + EXPECT_EQ(1, val.items().begin().first.asInteger()); + + + // test we can still read in strings + auto sval = parseJson("{\"a\":[]}", opts); + EXPECT_EQ("a", sval.items().begin().first.asString()); + + // test we can read in doubles + auto dval = parseJson("{1.5:[]}", opts); + EXPECT_EQ(1.5, dval.items().begin().first.asDouble()); +} + BENCHMARK(jsonSerialize, iters) { folly::json::serialization_opts opts; for (int i = 0; i < iters; ++i) {