Incorrect JSON serialization for otel protos
Hello! big fan of the library, however, I'm running into an issue with json serialization.
const resource_attributes = [_]KeyValue{
.{ .key = "service.name", .value = .{ .value = .{ .string_value = self.service.name } } },
.{ .key = "service.instance.id", .value = .{ .value = .{ .string_value = self.service.instance_id } } },
.{ .key = "service.version", .value = .{ .value = .{ .string_value = self.service.version } } },
.{ .key = "service.namespace", .value = .{ .value = .{ .string_value = self.service.namespace } } },
};
Is correct and matches the other proto syntax one may see in something like golang. I did find, however, that when encoding to JSON, the extra value field is included which causes an error when reading a proto message in other languages:
[
{
"key": "service.name",
"value": {
"value": {
"stringValue": "example"
}
}
},
{
"key": "service.instance.id",
"value": {
"value": {
"stringValue": "example-1764783202368-14467194"
}
}
},
{
"key": "service.version",
"value": {
"value": {
"stringValue": "latest"
}
}
},
{
"key": "service.namespace",
"value": {
"value": {
"stringValue": "example"
}
}
}
]
as opposed to the expected like this JSON example elsewhere in otel.
I am encoding like so:
const request_body = try request.jsonEncode(.{}, self.allocator);
defer self.allocator.free(request_body);
Let me know if there's something I'm doing incorrect here, but I believe this is a bug in the json serialization.
Hi @jaronoff97, it seems that the generated json is somehow valid, assuming we are looking at the same .proto
// Represents any type of attribute value. AnyValue may contain a
// primitive value such as a string or integer or it may contain an arbitrary nested
// object containing arrays, key-value lists and primitives.
message AnyValue {
// The value is one of the listed fields. It is valid for all values to be unspecified
// in which case this AnyValue is considered to be "empty".
oneof value {
string string_value = 1;
bool bool_value = 2;
int64 int_value = 3;
double double_value = 4;
ArrayValue array_value = 5;
KeyValueList kvlist_value = 6;
bytes bytes_value = 7;
}
}
I'm not against shadowing one level of a message serialization just to skip the oneof value. But we all need agreement and prior art references to prevent local minimums, is this behavior documented in Go and other languages? Is it opt-in?
@menduz it seems most implementation infer it from this issue. The behavior isn't well documented (per that comment), but going through other issues it's clear this is the expected behavior
I took a look at the default behavior of protobuf cpp and it seems consistent with this proposal. They do have one caveat for serialization, it may become handy now that you are looking at the serialization.
message TestOneof {
// In JSON format oneof fields behave mostly the same as optional
// fields except that:
// 1. Oneof fields have field presence information and will be
// printed if it's set no matter whether it's the default value.
// 2. Multiple oneof fields in the same oneof cannot appear at the
// same time in the input.
oneof oneof_value {
int32 oneof_int32_value = 1;
string oneof_string_value = 2;
bytes oneof_bytes_value = 3;
EnumType oneof_enum_value = 4;
MessageType oneof_message_value = 5;
google.protobuf.NullValue oneof_null_value = 6;
}
}
https://github.com/protocolbuffers/protobuf/blob/f66ad439e1ddab24f588da3df00daa7bbba3142c/src/google/protobuf/util/json_format_proto3.proto#L75-L90
Hello @jaronoff97 , thanks a lot for this issue and the PR you started. As said in the readme, json encoding/decoding is still a beta feature in this library, so i'm not surprised we fall in some problems. Thanks a lot for helping us improving the library.