Skip to content

Add an option to ignore unknown enum cases when decoding from JSON (instead of failing)#896

Closed
Cozmonat wants to merge 1 commit intoapple:masterfrom
Cozmonat:master
Closed

Add an option to ignore unknown enum cases when decoding from JSON (instead of failing)#896
Cozmonat wants to merge 1 commit intoapple:masterfrom
Cozmonat:master

Conversation

@Cozmonat
Copy link

The issue was already described in the past but looks like no PR was uploaded: https://github.com/apple/swift-protobuf/issues/481#issuecomment-295800326

I know it is unwanted behaviour but in real life scenarios backend teams sometimes coming to the necessity of extending existing enums by adding new cases. This leads to backward incompatibility in the mobile clients using swift-protobuf generated models and when backend encodes enum cases with case names.

To avoid the issue, an option added to pass unknown enum cases and use first enum case as default case following the best practice in proto programming. Backend teams usually define first enum case as "unknown" or "initial" or any other with the intention that it will be used as a default case, or to indicate the problem.

@thomasvl
Copy link
Collaborator

This still feels sorta wrong to add a specific feature for this just to the Swift implementation. Normally to get better support for a proto definition to expand with time, I believe the binary form is suggested because of these sorta issues with JSON.

If you do feel strongly about this, It seems like it might be worth bring up the discussion on the main proto repo (or maybe better on their mailing list) as if the functionality is useful, it shouldn't be limited to just one language.

@tbkka
Copy link
Collaborator

tbkka commented Jul 29, 2019

If you can point to other protobuf implementations that support this, that would certainly help. As you've noticed, we're very conservative about adding features.

@Cozmonat
Copy link
Author

I haven't found any other networking libraries which are "wrapping" SwiftProtobuf. I think the developers should be using Protobufs in conjunction with their own networking layer implementation.

I am agree with you and I hope I understand why it doesn't look right ideologically. But my reasons is practical. I have already the system working and clients are using REST via gRPC bridging to speak with the backend. Some clients can use Swagger, some other handle requests by manually parsing and building JSONs. In that case I can't force other teams to change their already well-working implementation so my decoding will not fail in my iOS client. I don't see real alternative but fix my implementation.

This is an option for those who can't force their backend to change existing encoding for REST clients.

@thomasvl
Copy link
Collaborator

Sorry, not following the wrapping comment. I believe we were both asking from the POV of other Protobuf implementations that support something like this. Hopefully the ones in Google's official distro.

@Cozmonat
Copy link
Author

Cozmonat commented Jul 30, 2019

One of google/protobuf implementations contains already an option called 'ignore_unknown_enum_values' inside 'json_util.cc'.

Actual implementation is inside "src/google/protobuf/util/internal/datapiece.cc", function DataPiece::ToEnum:

 // If ignore_unknown_enum_values is true an unknown enum value is ignored.
      
if (ignore_unknown_enum_values) {
  *is_unknown_enum_value = true;
  return enum_type->enumvalue(0).number();
}

@thomasvl
Copy link
Collaborator

One of google/protobuf implementations contains already an option called 'ignore_unknown_enum_values' inside 'json_util.cc'.

That appears to be an internal detail for some of the rewriting support between json/binary formats, and isn't directly exposed, if you look at the exposed options, there doesn't seem to be anything: https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/util/json_util.h

@Cozmonat
Copy link
Author

@thomasvl, I am not sure I understand your point.

There is an issue with current SwiftProtobuf implementation so it can't be used in production if your backend produces JSON with enum values encoded as names and not numbers.

@thomasvl
Copy link
Collaborator

My point is no other proto implementation seems to provide the option you are suggesting and we prefer to stay inline with the other implementation on abilities.

The Swift impl is to spec and in line with the other libraries. This is one of the downsides of using JSON (vs. binary) and it is usually handled by doing something like early in your communications having clients identify a "version" they are using and then the server ensure it only sends (and expects) JSON with the constructs known that that time.

The generally problem with the "ignore" options is they are lossy and there is no way for for clients to really know when things aren't known. The binary format has unknown fields support (and unknown enum value handing with proto3 syntax), so it is more robust and thus better handle these things.

@thomasvl
Copy link
Collaborator

ps - if you think there is general need/interest, please bring it up on https://github.com/protocolbuffers/protobuf or their discussion group (https://groups.google.com/forum/#!forum/protobuf) to see about getting something like this generally added to multiple languages. Having the broader feedback is the best way to help figure out if there are likely to be issues/downsides when adding support for something like this and then using it.

@tbkka tbkka changed the title Add an option to use initial enum case for unknown enum cases when decoding from JSON Add an option to ignore unknown enum cases when decoding from JSON (instead of failing) Jan 2, 2020
Copy link
Collaborator

@tbkka tbkka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for how long this took. Apparently, the C++ implementation has supported an equivalent feature for a while, but we missed it. If you could take a few minutes to clean this up a bit, I think we can go forward with it.

…h ignoreUnknownFields flag

When decoding from JSON with enum values encoded as names instead of numbers, the exception will be thrown and decoding will be stopped. The usage of SwiftProtobuf for decoding in such server configuration is impractical. Any server side update to existing enum will break the clients.
@Cozmonat
Copy link
Author

Good news! This should help us a lot. @tbkka, I've updated the commit as you requested.

return
}
value = try scanner.nextEnumValue() as E
value = try scanner.nextEnumValue()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think map decoding also comes thru here, so the value will be nil, and the pair won't end up in the map. We'll need to confirm that is correct vs. using the default value. (we can correct this on the map handling side since single field might want to say unset in this case).

m.repeatedNestedEnum == [.foo]
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll need to check map<*, Enum>, and when the single field is within a oneof.

@tbkka
Copy link
Collaborator

tbkka commented Sep 15, 2020

@thomasvl has asked for some additional test cases. Will you be able to implement those?

@tbkka tbkka closed this Dec 15, 2020
@tbkka tbkka deleted the branch apple:master December 15, 2020 17:36
@tbkka
Copy link
Collaborator

tbkka commented Dec 15, 2020

If you'd like to continue pursuing this, please rebase against the new main branch and re-submit. Thank you!

antongrbin added a commit to noom/swift-protobuf that referenced this pull request May 27, 2022
# Motivation


https://docs.google.com/document/d/1p5LUSTWrVBcT9F2wXBgHBDT3OJyOm5BpXAyTpwwDVts/edit#

# Changes

Merge apple#896

# Tested

```
swift test
```

Also tested as part of noom-contracts conformance tests: https://github.com/noom/noom-contracts/pull/294 

# Release process

Explained in Installation section here #1
thomasvl added a commit to thomasvl/swift-protobuf that referenced this pull request Nov 15, 2022
A new conformance test came in protocolbuffers/protobuf@09f4901 that says
ignoreUnknownFields should also result in unknown enums being ignored.

These changes are based on apple#896.

Fixes apple#972.
thomasvl added a commit to thomasvl/swift-protobuf that referenced this pull request Nov 15, 2022
A new conformance test came in protocolbuffers/protobuf@09f4901 that says
ignoreUnknownFields should also result in unknown enums being ignored.

These changes are based on apple#896.

Fixes apple#972.
thomasvl added a commit to thomasvl/swift-protobuf that referenced this pull request Nov 15, 2022
A new conformance test came in protocolbuffers/protobuf@09f4901 that says
ignoreUnknownFields should also result in unknown enums being ignored.

These changes are based on apple#896.

Fixes apple#972.
thomasvl added a commit that referenced this pull request Nov 15, 2022
A new conformance test came in protocolbuffers/protobuf@09f4901 that says
ignoreUnknownFields should also result in unknown enums being ignored.

These changes are based on #896.

Fixes #972.
thomasvl added a commit to thomasvl/swift-protobuf that referenced this pull request Nov 15, 2022
A new conformance test came in protocolbuffers/protobuf@09f4901 that says
ignoreUnknownFields should also result in unknown enums being ignored.

These changes are based on apple#896.

Fixes apple#972.
thomasvl added a commit that referenced this pull request Nov 15, 2022
A new conformance test came in protocolbuffers/protobuf@09f4901 that says
ignoreUnknownFields should also result in unknown enums being ignored.

These changes are based on #896.

Fixes #972.
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.

3 participants