google / google/protobuf.dart

Assigning primitive values to Any fields?

Open
#570 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Dart
Stars
572
Forks
196
Avg merge
1h 59m
Merged PRs (30d)
2

Description

Imagine the following `.proto` file:
```proto:
message Property {
string name = 1;
google.protobuf.Any value = 2;
}

message Value {
string val = 1;
}
```

Now what I would like to do, is to set the value of an instance of the generated `Property` class to either an actual "generated message" instance, or any kind of "primitive value". In pure dart, this would map to:

```dart:
class Property {
String? name;
dynamic value;
}
```

I know that the actual generated `Property` class will "look like" this:

```
class Property extends GeneratedMessage {
String? name;
Any? value;
}
```

So, to work with this `Any` field, I'll have to either `pack` the `GeneratedMessage` value into an actual `Any` instance, or create a new `Any` instance with a `List`, which should be a byte representation, right?

A `GeneratedMessage` class example:
```dart:
Property()
..name= 'ValueObject'
..value = Any.pack(Value()..val = 'test')
);
```

A `String` value example (basically any object that doesn't inherit `GeneratedMessage`):
```dart:
Property()
..name= 'StringValue'
..value = Any(
value: utf8.encode('This is the property value'),
typeUrl: 'core/String')
);
```

When unpacking such a message, I can check for the `typeUrl`, like so:

```dart:
Parameter param = Parameter.fromJson(jsonString);
switch (param.typeUrl) {
case 'type.googleapis.com/Value':
return param.unpackInto(Value());
case 'core/String':
return utf8.decode(param.value);
...
}
```

So far so good, I can pack and unpack messages, serialize and deserialize them, but what I'm currently still stuck on, is using `toProto3Json()`, as this uses the `TypeRegistry`, taking only `GeneratedMessage` classes. If I could somehow manage to register an `unpack` method to the `core/String` typeUrl in the `TypeRegistry`, I think I would be all set. But for now I guess I hit a dead end, because doing:

```dart:
Property()
..name = 'String'
..value = Any(
value: utf8.encode('Value string'),
typeUrl: 'core/String'
)
).toProto3Json(typeRegistry: [...]);
```
will eventually result in
```
Invalid argument(s): The type of the Any message (core/String) is not in the given typeRegistry.
```

I feel like I'm kind of swimming against the current here, and maybe I'm missing something obvious?

One alternative that I can think of, would be using a `oneof`, like this:
```proto:
message Property {
string name = 1;
DynamicPropertyValue value = 2;
}

message DynamicPropertyValue {
oneof value {
google.protobuf.Any any = 1;
string string = 2;
int32 int32 = 3;
int64 int64 = 4;
double double = 5;
float float = 6;
...
}
}
```
But, there are a few issues I have with this solution:
1. Things get quite verbose, as I have to list all possible primitive values as well.
2. For enumeration types, I'd either have to list all possible message types in the `DynamicPropertyValue`, or employ the same kind of manual packing/unpacking strategy as before.
3. Semantically I'll still be saying "this value can be anything". The only difference is that primitive types (and enums) will get their own field index.

So, I guess my questions are:
1. Is the kind of thing that I'm doing sensible? A simple use case would be the dynamic "name" => "value" pairs.
2. Am I going to have to go for the `oneof` solution, or is it somehow possible to expand on the "custom typeUrl mapper" idea in the `TypeRegistry`?
3. Am I completely missing something here?

I'll gladly welcome any feedback on this!

Contributor guide

Open the contributing guide

Research direction

Start by reading the Any, TypeRegistry, and toProto3Json behavior described in the issue, along with the proposed oneof schema. Determine whether custom type URLs for primitive values can be represented and decoded by the existing registry; done should be a documented supported approach or a clearly stated limitation.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart
Domain
api
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.