Skip to content

Conversation

osa1
Copy link
Member

@osa1 osa1 commented Jun 22, 2023

This fixes handling of unknown enum values in repeated and map fields and in
top-level fields.

Current behavior:

  • Unknown enums in map and repeated fields throw an exception.
  • Unknown enums in top-level fields reset the field value to the default one.

With this PR we just "ignore" the values:

  • Unknown enums in map and repeated fields are skipped.
  • Unknown enums in top-level fields do not reset the field's value to the
    default.

Fixes #849.


cl/796328163

@osa1 osa1 requested a review from sigurdm June 22, 2023 08:12
@sigurdm
Copy link
Collaborator

sigurdm commented Jun 22, 2023

I think we have to add a new value for "unknown" to each enum, then we can also (perhaps?) migrate to using real dart enums. That would give us exhaustiveness checking!

See https://b.corp.google.com/issues/287930910 for more context.

We can still do this PR first - but we should consider it.

@sigurdm
Copy link
Collaborator

sigurdm commented Jun 22, 2023

...Of course it is not trivial to use actual dart enums, because we want to store the integer value behind the "unknown" for re-serialization. I can only see how this could work by having a getter that translates the enum to a dart enum, but that is syntactically breaking.

We could also try to think in sealed classes - not sure how well that would work.

@osa1
Copy link
Member Author

osa1 commented Jun 22, 2023

It looks like resetting the top-level fields when the value is an unknown enum was deliberate as we have tests about it, but it doesn't make sense to me as resetting a field means we're not really ignoring the value (i.e. it has an effect on the message). As far as I can see proto3 spec doesn't mention this case, so I'll check other implementations and see what they do about this.

Java is skipping the field: https://github.com/protocolbuffers/protobuf/blob/f71a9263f9380836c4fc356b67710203e691bc95/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java#L1491-L1493

@sigurdm
Copy link
Collaborator

sigurdm commented Jun 22, 2023

Now I realize this PR is in terms of proto3 json decoding. Here we (usually) only have the name of the enum value, so we cannot translate it to the index...

Resetting vs skipping:

  • this only makes a difference when merging messages, right?
  • both behaviors seem questionable under certain circumstances, but so does stuffing the enum names into the encoding - it seems to break some fundamental properties that are upheld by the binary protocol. Not much we can do about that.
  • I guess following java is fine. I cannot remember why I implemented clearing the field.

@osa1
Copy link
Member Author

osa1 commented Jun 29, 2023

this only makes a difference when merging messages, right?

If you mean as in mergeFromMessage, then no, this happens when merging a JSON representing a message. E.g.

myMessage.mergeFromProto3Json(..., ignoreUnknownFields: true)

myMessage can be an empty message (in which case skip vs. reset does not matter, though I should check if the current behavior also resets the hazzer) but it may also be a non-empty message with the enum field already set. In that case skipping keeps the current value (i.e. actually ignores the unknown value), resetting resets the value to the default (does not really ignore the unknown value).

both behaviors seem questionable under certain circumstances, but so does stuffing the enum names into the encoding - it seems to break some fundamental properties that are upheld by the binary protocol. Not much we can do about that.

I think ignoring makes sense. Why do you say it's questionable? The flag says "ignore" so it should ignore.

I agree, proto3 JSON format is a bit strange..

@sigurdm
Copy link
Collaborator

sigurdm commented Jun 29, 2023

myMessage can be an empty message (in which case skip vs. reset does not matter, ...

That's what I meant :) .

I think ignoring makes sense. Why do you say it's questionable? The flag says "ignore" so it should ignore.

Well - one thing is what the flag says, another one is what is ergonomic for actual use. If I have one proto represented by

{
  "enumField": "A"
}

('A' is a known enum value).

And I merge it with another proto

{
  "enumField": "B"
}

If these were messages in the binary protocol, the unknown value would be preserved, and we would be able to see that from the merged proto.

I find it somehow surprising that now m.enumField still returns A. It seems that the fact that the merged field carries a value, although unknown should be reflected in the merge. I cannot think of a use-case where that is the semantics I want.

But again, if that is what other implementations do, I guess we should just follow along.

@osa1
Copy link
Member Author

osa1 commented Jun 29, 2023

I find it somehow surprising that now m.enumField still returns A

proto3 JSON (at least as we implement it in this library, AFAICS this is unspecified) does not support unknown fields, so the behavior you describe is not possible (you can't preserve the unknown value). You have these two options instead: (1) fail (the default) (2) ignore the unknown value (opt-in, with the flag). The default is (1). Binary format has (3) which is store the unknown field in unknown field set.

@sigurdm
Copy link
Collaborator

sigurdm commented Jun 29, 2023

Yeah - I argue that we get closer to option 3 by resetting.

But that is all splitting hairs. I don't hope anyone is actually using the proto3 json format for anything serious 🙈

@osa1
Copy link
Member Author

osa1 commented Aug 5, 2025

I think this may not be right. I'm consulting to the protobuf team to understand what "ignore unknown fields" really means in this context.

(We have existing tests that don't really ignore unknown fields, they reset the enum value to the default. I'm not sure if that's according to the spec as the ProtoJSON spec doesn't say much about ignoring unknown fields..)

Copy link

github-actions bot commented Aug 14, 2025

Package publishing

Package Version Status Publish tag (post-merge)
package:protobuf 5.0.0 (error) pubspec version (5.0.0) and changelog (5.0.0-wip) don't agree
package:protoc_plugin 23.0.0 ready to publish protoc_plugin-v23.0.0

Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.

@osa1 osa1 marked this pull request as ready for review August 19, 2025 11:34
@osa1 osa1 merged commit 6bce9d7 into google:master Aug 19, 2025
17 of 18 checks passed
@osa1 osa1 deleted the fix_unknown_enums branch August 19, 2025 12:21
Spiritus2424 pushed a commit to EXFO/protobuf.dart that referenced this pull request Aug 28, 2025
This fixes handling of unknown enum values in repeated and map fields and in
top-level fields.

Current behavior:

- Unknown enums in map and repeated fields throw an exception.
- Unknown enums in top-level fields reset the field value to the default one.

With this PR we just "ignore" the values:

- Unknown enums in map and repeated fields are skipped.
- Unknown enums in top-level fields do not reset the field's value to the
  default.

Fixes google#849.
Spiritus2424 pushed a commit to EXFO/protobuf.dart that referenced this pull request Aug 28, 2025
This fixes handling of unknown enum values in repeated and map fields and in
top-level fields.

Current behavior:

- Unknown enums in map and repeated fields throw an exception.
- Unknown enums in top-level fields reset the field value to the default one.

With this PR we just "ignore" the values:

- Unknown enums in map and repeated fields are skipped.
- Unknown enums in top-level fields do not reset the field's value to the
  default.

Fixes google#849.
copybara-service bot pushed a commit to dart-lang/sdk that referenced this pull request Aug 28, 2025
Revisions updated by `dart tools/rev_sdk_deps.dart`.

ecosystem (https://github.com/dart-lang/ecosystem/compare/68ff911..fd28be2):
  fd28be2  2025-08-27  Moritz  Dont show warnings by default (dart-lang/ecosystem#365)

http (https://github.com/dart-lang/http/compare/6656f15..ef05b37):
  ef05b37  2025-08-27  Brian Quinlan  [cupertino_http]: Switch to ffigen 19.1.0 (dart-lang/http#1811)

i18n (https://github.com/dart-lang/i18n/compare/6a8f9c7..9a211d1):
  9a211d14  2025-08-27  Moritz  [package:intl4x] `DateTime` formatting redesign (dart-lang/i18n#1003)

protobuf (https://github.com/dart-lang/protobuf/compare/6e9c9f4..765ba8a):
  765ba8a  2025-08-28  Ömer Sinan Ağacan  Fix non-proto3 JSON format double decoding (google/protobuf.dart#1043)
  703bf7c  2025-08-27  Ömer Sinan Ağacan  Compile dart2wasm benchmarks with --no-strip-wasm (google/protobuf.dart#1044)
  9939965  2025-08-26  Ömer Sinan Ağacan  Fix JS interop annotations (google/protobuf.dart#1042)
  da354a2  2025-08-22  Ömer Sinan Ağacan  Update benchmark builder to use -O2 with dart2wasm, add output dir to gitignore (google/protobuf.dart#1041)
  41eba19  2025-08-22  Ömer Sinan Ağacan  Add sinks to benchmarks to prevent smart tools eliminating benchmarked code (google/protobuf.dart#1040)
  3ccc8ab  2025-08-19  dependabot[bot]  Bump actions/checkout from 3.5.3 to 4.2.2 (google/protobuf.dart#1036)
  6bce9d7  2025-08-19  Ömer Sinan Ağacan  Fix unknown enum handling (google/protobuf.dart#853)
  19e5a25  2025-08-15  Ömer Sinan Ağacan  Implement deepCopy as copying, instead of merge (google/protobuf.dart#742)

web (https://github.com/dart-lang/web/compare/ee9733f..e7895bd):
  e7895bd  2025-08-27  Nikechukwu  [interop] Add Support for Declaration Merging and Overloading (dart-lang/web#449)

webdev (https://github.com/dart-lang/webdev/compare/9724fea..a7d3d2f):
  a7d3d2fc  2025-08-27  Ben Konyi  [ DWDS ] Fix issue where null was repeatedly sent to connected clients (dart-lang/webdev#2678)
  e3009dfb  2025-08-27  Nate Biggs  Remove stale calls to dartSdkIsAtLeast. (dart-lang/webdev#2677)
  e0d58082  2025-08-26  Nate Biggs  Fix expectation in test accessing private field. (dart-lang/webdev#2676)

Change-Id: Ia98a8f065830865f6922ca032939380683645d72
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/447640
Reviewed-by: Konstantin Shcheglov <[email protected]>
Commit-Queue: Konstantin Shcheglov <[email protected]>
Auto-Submit: Devon Carew <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Repeated enum does not honor ignoreUnknownFields
2 participants