[JSON] Add ObjectMapper::mapOptional to validate optional data.
Currently the idiom for mapping optional fields is:
ObjectMapper O(Val, P);
if (!O.map("required1", Out.R1) || !O.map("required2", Out.R2))
return false;
O.map("optional1", Out.O1); // ignore result
return true;
If `optional1` is present but malformed, then we won't detect/report
that error. We may even leave `Out` in an incomplete state while returning true.
Instead, we'd often prefer to ignore `optional1` if it is absent, but otherwise
behave just like map().
Differential Revision: https://reviews.llvm.org/D89128
diff --git a/llvm/unittests/Support/JSONTest.cpp b/llvm/unittests/Support/JSONTest.cpp
index 9f17c98..ed9a72d 100644
--- a/llvm/unittests/Support/JSONTest.cpp
+++ b/llvm/unittests/Support/JSONTest.cpp
@@ -375,10 +375,8 @@
}
bool fromJSON(const Value &E, CustomStruct &R, Path P) {
ObjectMapper O(E, P);
- if (!O || !O.map("str", R.S) || !O.map("int", R.I))
- return false;
- O.map("bool", R.B);
- return true;
+ return O && O.map("str", R.S) && O.map("int", R.I) &&
+ O.mapOptional("bool", R.B);
}
static std::string errorContext(const Value &V, const Path::Root &R) {
@@ -392,24 +390,18 @@
std::map<std::string, std::vector<CustomStruct>> R;
CustomStruct ExpectedStruct = {"foo", 42, true};
std::map<std::string, std::vector<CustomStruct>> Expected;
- Value J = Object{
- {"foo",
- Array{
- Object{
- {"str", "foo"},
- {"int", 42},
- {"bool", true},
- {"unknown", "ignored"},
- },
- Object{{"str", "bar"}},
- Object{
- {"str", "baz"}, {"bool", "string"}, // OK, deserialize ignores.
- },
- }}};
+ Value J = Object{{"foo", Array{
+ Object{
+ {"str", "foo"},
+ {"int", 42},
+ {"bool", true},
+ {"unknown", "ignored"},
+ },
+ Object{{"str", "bar"}},
+ }}};
Expected["foo"] = {
CustomStruct("foo", 42, true),
CustomStruct("bar", llvm::None, false),
- CustomStruct("baz", llvm::None, false),
};
Path::Root Root("CustomStruct");
ASSERT_TRUE(fromJSON(J, R, Root));
@@ -423,7 +415,6 @@
"foo": [
/* error: expected object */
123,
- { ... },
{ ... }
]
})";
@@ -443,6 +434,10 @@
// Optional<T> must parse as the correct type if present.
EXPECT_FALSE(fromJSON(Object{{"str", "1"}, {"int", "string"}}, V, Root));
EXPECT_EQ("expected integer at CustomStruct.int", toString(Root.getError()));
+
+ // mapOptional must parse as the correct type if present.
+ EXPECT_FALSE(fromJSON(Object{{"str", "1"}, {"bool", "string"}}, V, Root));
+ EXPECT_EQ("expected boolean at CustomStruct.bool", toString(Root.getError()));
}
TEST(JSONTest, ParseDeserialize) {