Commit a4c90474 authored by John Fremlin VII's avatar John Fremlin VII Committed by Owen Yamauchi

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
parent a896ed39
...@@ -251,8 +251,9 @@ struct ParseError : std::runtime_error { ...@@ -251,8 +251,9 @@ struct ParseError : std::runtime_error {
// Wraps our input buffer with some helper functions. // Wraps our input buffer with some helper functions.
struct Input { struct Input {
explicit Input(StringPiece range) explicit Input(StringPiece range, json::serialization_opts const* opts)
: range_(range) : range_(range)
, opts_(*opts)
, lineNum_(0) , lineNum_(0)
{ {
storeCurrent(); storeCurrent();
...@@ -351,6 +352,10 @@ struct Input { ...@@ -351,6 +352,10 @@ struct Input {
throw ParseError(lineNum_, context(), what); throw ParseError(lineNum_, context(), what);
} }
json::serialization_opts const& getOpts() {
return opts_;
}
private: private:
void storeCurrent() { void storeCurrent() {
current_ = range_.empty() ? EOF : range_.front(); current_ = range_.empty() ? EOF : range_.front();
...@@ -358,12 +363,14 @@ private: ...@@ -358,12 +363,14 @@ private:
private: private:
StringPiece range_; StringPiece range_;
json::serialization_opts const& opts_;
unsigned lineNum_; unsigned lineNum_;
int current_; int current_;
}; };
dynamic parseValue(Input& in); dynamic parseValue(Input& in);
fbstring parseString(Input& in); fbstring parseString(Input& in);
dynamic parseNumber(Input& in);
dynamic parseObject(Input& in) { dynamic parseObject(Input& in) {
assert(*in == '{'); assert(*in == '{');
...@@ -378,14 +385,22 @@ dynamic parseObject(Input& in) { ...@@ -378,14 +385,22 @@ dynamic parseObject(Input& in) {
} }
for (;;) { for (;;) {
if (*in != '\"') { if (*in == '\"') { // string
in.error("expected string for object key name");
}
auto key = parseString(in); auto key = parseString(in);
in.skipWhitespace(); in.skipWhitespace();
in.expect(':'); in.expect(':');
in.skipWhitespace(); in.skipWhitespace();
ret.insert(std::move(key), parseValue(in)); 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));
}
in.skipWhitespace(); in.skipWhitespace();
if (*in != ',') { if (*in != ',') {
break; break;
...@@ -665,11 +680,18 @@ void escapeString(StringPiece input, ...@@ -665,11 +680,18 @@ void escapeString(StringPiece input,
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
dynamic parseJson(StringPiece range) { 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); auto ret = parseValue(in);
in.skipWhitespace(); in.skipWhitespace();
if (*in != '\0' && in.size()) { if (in.size() && *in != '\0') {
in.error("parsing didn't consume all input"); in.error("parsing didn't consume all input");
} }
return ret; return ret;
......
...@@ -106,6 +106,7 @@ namespace json { ...@@ -106,6 +106,7 @@ namespace json {
* Parse a json blob out of a range and produce a dynamic representing * Parse a json blob out of a range and produce a dynamic representing
* it. * it.
*/ */
dynamic parseJson(StringPiece, json::serialization_opts const&);
dynamic parseJson(StringPiece); dynamic parseJson(StringPiece);
/* /*
......
...@@ -314,6 +314,33 @@ TEST(Json, UTF8Validation) { ...@@ -314,6 +314,33 @@ TEST(Json, UTF8Validation) {
EXPECT_ANY_THROW(folly::json::serialize("a\xe0\xa0\x80z\xe0\x80\x80", opts)); 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) { BENCHMARK(jsonSerialize, iters) {
folly::json::serialization_opts opts; folly::json::serialization_opts opts;
for (int i = 0; i < iters; ++i) { for (int i = 0; i < iters; ++i) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment