使用反射实现嵌套的协议缓冲区。

6

假设我有一个包含以下内容的.proto文件中的消息

Message Foo {
    Message Bar {
        optional int32 a = 1;
        optional int32 b = 2;
    }
    optional Bar bar = 1;
}

在Java中,是否有办法仅使用字符串"bar.a"来设置字段a?理想情况下,我想编写以下方法:
public Foo.Builder apply(Foo.Builder builder, String fieldPath, Object value) {
    // fieldPath == "bar.a"
    // This doesn't work
    FieldDescriptor fd = builder.getDefaultInstanceForType().findFieldByName(fieldPath);
    builder = builder.setField(fd, value);
}

但是当我这样做时,我会得到一个非法参数异常。

有人知道如何以通用的方式解决这个问题吗?

我也需要另一种方式。

public Object getValue(Foo message, String fieldPath) {
    // This doesn't work
    FieldDescriptor fd = message.getDefaultInstanceForType().findFieldByName(fieldPath);
    return message.getField(fieldPath);
}

作为一条注意事项,如果fieldPath不包含分隔符(“.”)并且引用的是基础Message而不是嵌套Message,则此方法可以正常工作。

1
foo.a 是正确的吗?我认为应该是 foo.bar.a(根据 Foo 的结构)? - Muel
你说得对,问题已经更新。 - Jon
1个回答

8

您需要将字段路径按 '.' 分割并执行一系列查找,例如:

Message subMessage =
    (Message)message.getField(
        message.getDescriptorForType().findFieldByName("bar"));
return subMessage.getField(
    subMessage.getDescriptorForType().findFieldByName("a"));

或者写成:
FieldDescriptor desc = message.getDescriptorForType().findFieldByName("bar");
Message.Builder subBuilder = (Message.Builder)builder.getFieldBuilder(desc);
subBuilder.setField(
    subMessage.getDescriptorForType().findFieldByName("a"), value);
builder.setField(desc, subBuilder.build());

当然,您可以编写一个库,一次性拆分字符串并进行所有查找(并执行适当的错误检查)。


是否有一种更新更好的方法来设置protobuf中嵌套字段?因为一些旧方法已不再可用。 - Amit Kumar
@AmitKumar 我已经很多年没有参与这个项目了,所以如果这些方法已经被淘汰了,我不知道替代方案是什么。抱歉。 - Kenton Varda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接