Skip to content

Commit bef02ba

Browse files
committed
Fix unknown enum handling
Fixes google#849.
1 parent e76bd74 commit bef02ba

File tree

4 files changed

+80
-12
lines changed

4 files changed

+80
-12
lines changed

protobuf/lib/src/protobuf/proto3_json.dart

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ void _mergeFromProto3Json(
164164
ignoreUnknownFields, supportNamesWithUnderscores, permissiveEnums);
165165

166166
void recursionHelper(Object? json, _FieldSet fieldSet) {
167+
// Convert a JSON object to proto object. Returns `null` on unknown enum
168+
// values when [ignoreUnknownFields] is [true].
167169
Object? convertProto3JsonValue(Object value, FieldInfo fieldInfo) {
168170
final fieldType = fieldInfo.type;
169171
switch (PbFieldType._baseType(fieldType)) {
@@ -174,14 +176,12 @@ void _mergeFromProto3Json(
174176
throw context.parseException('Expected bool value', json);
175177
case PbFieldType._BYTES_BIT:
176178
if (value is String) {
177-
Uint8List result;
178179
try {
179-
result = base64Decode(value);
180+
return base64Decode(value);
180181
} on FormatException {
181182
throw context.parseException(
182183
'Expected bytes encoded as base64 String', json);
183184
}
184-
return result;
185185
}
186186
throw context.parseException(
187187
'Expected bytes encoded as base64 String', value);
@@ -264,14 +264,12 @@ void _mergeFromProto3Json(
264264
case PbFieldType._SFIXED64_BIT:
265265
if (value is int) return Int64(value);
266266
if (value is String) {
267-
Int64 result;
268267
try {
269-
result = Int64.parseInt(value);
268+
return Int64.parseInt(value);
270269
} on FormatException {
271270
throw context.parseException(
272271
'Expected int or stringified int', value);
273272
}
274-
return result;
275273
}
276274
throw context.parseException(
277275
'Expected int or stringified int', value);
@@ -368,9 +366,12 @@ void _mergeFromProto3Json(
368366
throw context.parseException('Expected a String key', subKey);
369367
}
370368
context.addMapIndex(subKey);
371-
fieldValues[decodeMapKey(subKey, mapFieldInfo.keyFieldType)] =
372-
convertProto3JsonValue(
373-
subValue, mapFieldInfo.valueFieldInfo);
369+
final key = decodeMapKey(subKey, mapFieldInfo.keyFieldType);
370+
final value = convertProto3JsonValue(
371+
subValue, mapFieldInfo.valueFieldInfo);
372+
if (value != null) {
373+
fieldValues[key] = value;
374+
}
374375
context.popIndex();
375376
});
376377
} else {
@@ -382,7 +383,10 @@ void _mergeFromProto3Json(
382383
for (var i = 0; i < value.length; i++) {
383384
final entry = value[i];
384385
context.addListIndex(i);
385-
values.add(convertProto3JsonValue(entry, fieldInfo));
386+
final parsedValue = convertProto3JsonValue(entry, fieldInfo);
387+
if (parsedValue != null) {
388+
values.add(parsedValue);
389+
}
386390
context.popIndex();
387391
}
388392
} else {
@@ -402,8 +406,15 @@ void _mergeFromProto3Json(
402406
original.mergeFromMessage(parsedSubMessage);
403407
}
404408
} else {
405-
fieldSet._setFieldUnchecked(
406-
meta, fieldInfo, convertProto3JsonValue(value, fieldInfo));
409+
final parsedValue = convertProto3JsonValue(value, fieldInfo);
410+
if (parsedValue == null) {
411+
// Unknown enum
412+
if (!ignoreUnknownFields) {
413+
throw context.parseException('Unknown enum value', value);
414+
}
415+
} else {
416+
fieldSet._setFieldUnchecked(meta, fieldInfo, parsedValue);
417+
}
407418
}
408419
context.popIndex();
409420
});

protoc_plugin/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ TEST_PROTO_LIST = \
3131
entity \
3232
enum_extension \
3333
enum_name \
34+
enum_test \
3435
extend_unittest \
3536
ExtensionEnumNameConflict \
3637
ExtensionNameConflict \
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
syntax = "proto3";
6+
7+
enum A {
8+
X = 0;
9+
Y = 1;
10+
}
11+
12+
message Message {
13+
A enum_field = 1;
14+
map<int32, A> map_value_field = 2;
15+
repeated A repeated_enum_field = 3;
16+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:test/test.dart';
6+
7+
import '../out/protos/enum_test.pb.dart';
8+
9+
void main() {
10+
group('Enum parsing in maps, lists, messages', () {
11+
test('Parse known fields', () {
12+
final json = {
13+
'enumField': 'Y',
14+
'mapValueField': {'1': 'Y'},
15+
'repeatedEnumField': ['Y'],
16+
};
17+
18+
final msg = Message();
19+
msg.mergeFromProto3Json(json);
20+
expect(msg.enumField, A.Y);
21+
expect(msg.mapValueField.values.toList(), [A.Y]);
22+
expect(msg.repeatedEnumField, [A.Y]);
23+
});
24+
25+
test('Skip unknown fields', () {
26+
final json = {
27+
'enumField': 'Z',
28+
'mapValueField': {'1': 'X', '2': 'Z', '3': 'Y'},
29+
'repeatedEnumField': ['X', 'Z', 'Y'],
30+
};
31+
32+
final msg = Message();
33+
msg.enumField = A.Y;
34+
msg.mergeFromProto3Json(json, ignoreUnknownFields: true);
35+
expect(msg.enumField, A.Y);
36+
expect(msg.mapValueField.values.toList(), [A.X, A.Y]);
37+
expect(msg.repeatedEnumField, [A.X, A.Y]);
38+
});
39+
});
40+
}

0 commit comments

Comments
 (0)