// 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();
}
throw ParseError(lineNum_, context(), what);
}
+ json::serialization_opts const& getOpts() {
+ return opts_;
+ }
+
private:
void storeCurrent() {
current_ = range_.empty() ? EOF : range_.front();
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 == '{');
}
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;
//////////////////////////////////////////////////////////////////////
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;
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) {