如何在java.util.Map<String,Object>中转换google.protobuf.Struct字段?



我有一个quotation_item.proto文件定义的消息,该文件具有google.protobuf.Struct字段来解析元数据JSON。

message QuotationItemEntry {
// Other fields
google.protobuf.Struct metadata = 15;
}

元数据字段也在 Java 实体类中定义

public class QuotationItem {
// Other entity fields
private Map<String, Object> metadata;
}

我想使用 spring 启动应用程序存储 gRPC 请求中传递的 QuoteItemEntry 对象,但无法转换java.util.Map<String, Object>中的google.protobuf.Struct字段。

在 Java 中,google.protobuf.Struct的字段值在其本机表示中不受支持。相反,它们被com.google.protobuf.Value包裹。所以使用google.protobuf.Struct#getFieldsMap()方法,结果是Map<String, Value>.您需要手动填充条目值。

我认为您可以流式传输元数据键并将它们收集为地图。这可以解决您的问题。

public Map<String, String> getMetadataMap(Metadata metadata) {
Map<String, String> metadataMap = metadata.keys().stream().collect(
Collectors.toMap(
key -> key,
key -> metadata.get(Metadata.Key.of(key,
Metadata.ASCII_STRING_MARSHALLER))));
return metadataMap;
}

Kotlin 的解决方案:

fun Struct.toMap(): Map<String, Any?> =
this.fieldsMap.mapValues { it.value.toAny() }
fun Value.toAny(): Any? =
when (kindCase) {
NULL_VALUE -> null
NUMBER_VALUE -> numberValue
STRING_VALUE -> stringValue
BOOL_VALUE -> boolValue
STRUCT_VALUE -> structValue.toMap()
LIST_VALUE -> listValue.valuesList.map { it.toAny() }
else -> error("Incorrect protobuf value $this")
}

用法:

val map: Map<String, Any?> = youProtoStruct.toMap()

voidzcy的回答正确地指出了在Java中处理Google Protobuf结构的复杂性,特别是使用Value来包装字段。我想为那些刚接触Protobuf的人提供一些更深入的理解,以阐明为什么做出这种设计选择以及它如何影响您作为开发人员。

Protobuf 的 Struct 类型被设计为一个灵活的容器,可以容纳各种数据类型。这种设计选择对于处理半结构化或动态数据特别有利。灵活性允许结构捕获不符合固定架构的数据。虽然这对于处理各种数据形状非常有用,但它需要权衡:您将失去强大的类型保证。

为了更深入地了解核心库中Struct众所周知的消息的定义Protobuf我们可以查看Github上.proto描述。

下面的代码取自 google protobuf 的开源存储库:

message Struct {
// Unordered map of dynamically typed values.
map<string, Value> fields = 1;
}

因此,从上面的代码片段中,我们可以看出结构本质上是使用我们上面讨论的map<>,并将Value存储到映射字符串中。

Value是在同一.proto文件上定义的另一条消息,如下所示Value

message Value {
oneof kind {
NullValue null_value = 1;
double number_value = 2;
string string_value = 3;
bool bool_value = 4;
Struct struct_value = 5;
ListValue list_value = 6;
}
}

我们还可以查看struct.proto文件中的最后一个实体,以了解该值可以作为"Null"或Value的"列表":

enum NullValue {
NULL_VALUE = 0;
}
message ListValue {
repeated Value values = 1;
}

一些语言和框架可能会使用内部的protobuf结构,而不给开发人员和"JSON Like"接口来与Struct进行交互。

例如,如果我们有以下 JSON 数据:

{
"id": 1,
"name": "foo",
"domain": "bar",
"isAuthenticated": true
}

我们将拥有内部Protobuf伪代码结构,如下所示:

Struct {
fields {
key: "name"
value {
string_value: "foo"
}
}
fields {
key: "isAuthenticated"
value {
bool_value: true
}
}
fields {
key: "id"
value {
number_value: 1
}
}
fields {
key: "domain"
value {
string_value: "bar"
}
}
}

如果您想阅读更多关于它的信息,我已经发布了一个关于这个主题的博客

最新更新