Skip to content

Commit

Permalink
Merge pull request protocolbuffers#4195 from alexey-malov/IgnoreUnkno…
Browse files Browse the repository at this point in the history
…wnEnumsInJson

Adds an option in C++ JSON parser to ignore unrecognized enum values
  • Loading branch information
xfxyjwf committed Feb 26, 2018
2 parents bb40c0c + 02452db commit 3aaed96
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 6 deletions.
7 changes: 6 additions & 1 deletion src/google/protobuf/util/internal/datapiece.cc
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ StatusOr<string> DataPiece::ToBytes() const {
}

StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type,
bool use_lower_camel_for_enums) const {
bool use_lower_camel_for_enums,
bool ignore_unknown_enum_values) const {
if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE;

if (type_ == TYPE_STRING) {
Expand Down Expand Up @@ -305,6 +306,10 @@ StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type,
value = FindEnumValueByNameWithoutUnderscoreOrNull(enum_type, enum_name);
if (value != NULL) return value->number();
}

// If ignore_unknown_enum_values is true an unknown enum value is treated
// as the default
if (ignore_unknown_enum_values) return enum_type->enumvalue(0).number();
} else {
// We don't need to check whether the value is actually declared in the
// enum because we preserve unknown enum values as well.
Expand Down
3 changes: 2 additions & 1 deletion src/google/protobuf/util/internal/datapiece.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ class LIBPROTOBUF_EXPORT DataPiece {
// If the value is not a string, attempts to convert to a 32-bit integer.
// If none of these succeeds, returns a conversion error status.
util::StatusOr<int> ToEnum(const google::protobuf::Enum* enum_type,
bool use_lower_camel_for_enums) const;
bool use_lower_camel_for_enums,
bool ignore_unknown_enum_values) const;

private:
// Disallow implicit constructor.
Expand Down
8 changes: 5 additions & 3 deletions src/google/protobuf/util/internal/proto_writer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,9 @@ inline Status WriteString(int field_number, const DataPiece& data,
inline Status WriteEnum(int field_number, const DataPiece& data,
const google::protobuf::Enum* enum_type,
CodedOutputStream* stream,
bool use_lower_camel_for_enums) {
StatusOr<int> e = data.ToEnum(enum_type, use_lower_camel_for_enums);
bool use_lower_camel_for_enums,
bool ignore_unknown_values) {
StatusOr<int> e = data.ToEnum(enum_type, use_lower_camel_for_enums, ignore_unknown_values);
if (e.ok()) {
WireFormatLite::WriteEnum(field_number, e.ValueOrDie(), stream);
}
Expand Down Expand Up @@ -665,7 +666,8 @@ ProtoWriter* ProtoWriter::RenderPrimitiveField(
case google::protobuf::Field_Kind_TYPE_ENUM: {
status = WriteEnum(field.number(), data,
typeinfo_->GetEnumByTypeUrl(field.type_url()),
stream_.get(), use_lower_camel_for_enums_);
stream_.get(), use_lower_camel_for_enums_,
ignore_unknown_fields_);
break;
}
default: // TYPE_GROUP or TYPE_MESSAGE
Expand Down
2 changes: 1 addition & 1 deletion src/google/protobuf/util/internal/proto_writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ class LIBPROTOBUF_EXPORT ProtoWriter : public StructuredObjectWriter {
// Indicates whether we finished writing root message completely.
bool done_;

// If true, don't report unknown field names to the listener.
// If true, don't report unknown field names and enum values to the listener.
bool ignore_unknown_fields_;

// If true, check if enum name in camel case or without underscore matches the
Expand Down
58 changes: 58 additions & 0 deletions src/google/protobuf/util/json_util_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,64 @@ TEST_F(JsonUtilTest, TestDynamicMessage) {
EXPECT_EQ(ToJson(generated, options), ToJson(*message, options));
}

TEST_F(JsonUtilTest, TestParsingUnknownEnumsAs0) {
TestMessage m;
{
JsonParseOptions options;
ASSERT_FALSE(options.ignore_unknown_fields);
string input =
"{\n"
" \"enum_value\":\"UNKNOWN_VALUE\"\n"
"}";
m.set_enum_value(proto3::BAR);
EXPECT_FALSE(FromJson(input, &m, options));
ASSERT_EQ(proto3::BAR, m.enum_value()); // Keep previous value

options.ignore_unknown_fields = true;
EXPECT_TRUE(FromJson(input, &m, options));
EXPECT_EQ(0, m.enum_value()); // Unknown enum value must be decoded as 0
}
// Integer values are read as usual
{
JsonParseOptions options;
string input =
"{\n"
" \"enum_value\":12345\n"
"}";
m.set_enum_value(proto3::BAR);
EXPECT_TRUE(FromJson(input, &m, options));
ASSERT_EQ(12345, m.enum_value());

options.ignore_unknown_fields = true;
EXPECT_TRUE(FromJson(input, &m, options));
EXPECT_EQ(12345, m.enum_value());
}

// Trying to pass an object as an enum field value is always treated as an error
{
JsonParseOptions options;
string input =
"{\n"
" \"enum_value\":{}\n"
"}";
options.ignore_unknown_fields = true;
EXPECT_FALSE(FromJson(input, &m, options));
options.ignore_unknown_fields = false;
EXPECT_FALSE(FromJson(input, &m, options));
}
// Trying to pass an array as an enum field value is always treated as an error
{
JsonParseOptions options;
string input =
"{\n"
" \"enum_value\":[]\n"
"}";
EXPECT_FALSE(FromJson(input, &m, options));
options.ignore_unknown_fields = true;
EXPECT_FALSE(FromJson(input, &m, options));
}
}

typedef std::pair<char*, int> Segment;
// A ZeroCopyOutputStream that writes to multiple buffers.
class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream {
Expand Down

0 comments on commit 3aaed96

Please sign in to comment.