java protobuf extend_protobuf中extension的使用

零、更新

proto3中用法参考这个。

一、extension的用法

由于extension是protobuf2中一个比较高级,但是在proto3中禁用的功能,所以在这里还是看下这个内容的实现,完整的实现参考来自下面文章。为了避免跳转或者连接失效,这里把原文章内容拷贝一份:

proto文件

package communication;

message BaseMessage {

required uint64 server_id = 1;

required string uuid = 2;

required uint64 message_id = 3;

extensions 100 to max;

}

message GetIdentify {

extend BaseMessage {

optional GetIdentify message = 100;

}

required string hostname = 1;

}

使用代码

communication::BaseMessage base_message;

base_message.set_message_id(123456);

base_message.set_server_id(112313123);

base_message.set_uuid("asdaskdjasd213123123asd");

base_message.MutableExtension(communication::GetIdentify::message)->set_hostname("http://test123123123ing");

二、MutableExtension的定义在哪里

从这个地方的注释也可以看到,如果一个message有"extension"声明,则有一个GOOGLE_PROTOBUF_EXTENSION_ACCESSORS宏在这个类的定义中,这个宏主要访问的是类的_extensions_成员。例如,使用前面生成的例子,可以看到有一个这种宏的定义。其中使用的_extensions_是一个PROTOBUF_NAMESPACE_ID::internal::ExtensionSet类型的定义。

class BaseMessage final :

public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:communication.BaseMessage) */ {

……

GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(BaseMessage)

……

private:

class HasBitSetters;

// helper for ByteSizeLong()

size_t RequiredFieldsByteSizeFallback() const;

::PROTOBUF_NAMESPACE_ID::internal::ExtensionSet _extensions_;

……

}

protobuf-master\src\google\protobuf\extension_set.h

// Generated accessors

// This macro should be expanded in the context of a generated type which

// has extensions.

//

// We use "_proto_TypeTraits" as a type name below because "TypeTraits"

// causes problems if the class has a nested message or enum type with that

// name and "_TypeTraits" is technically reserved for the C++ library since

// it starts with an underscore followed by a capital letter.

//

// For similar reason, we use "_field_type" and "_is_packed" as parameter names

// below, so that "field_type" and "is_packed" can be used as field names.

#define GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(CLASSNAME) \

/* Has, Size, Clear */ \

template

::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type, \

bool _is_packed> \

inline bool HasExtension( \

const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< \

CLASSNAME, _proto_TypeTraits, _field_type, _is_packed>& id) const { \

return _extensions_.Has(id.number()); \

} \

\

……

template

::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type, \

bool _is_packed> \

inline typename _proto_TypeTraits::Singular::MutableType MutableExtension( \

const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< \

CLASSNAME, _proto_TypeTraits, _field_type, _is_packed>& id) { \

return _proto_TypeTraits::Mutable(id.number(), _field_type, \

&_extensions_); \

}

……

template

::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type, \

bool _is_packed> \

inline typename _proto_TypeTraits::Repeated::RepeatedFieldType* \

MutableRepeatedExtension( \

const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< \

CLASSNAME, _proto_TypeTraits, _field_type, _is_packed>& id) { \

return _proto_TypeTraits::MutableRepeated(id.number(), _field_type, \

_is_packed, &_extensions_); \

}

三、派生类中使用的message是什么

可以看到,这里定义的是一个比较复杂的类型生成的静态变量,配置前面的GOOGLE_PROTOBUF_EXTENSION_ACCESSORS定义的模版函数,可以将这里面包含的所有信息提取出来,其中比较关键的就是消息的类型,

class GetIdentify final :

public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:communication.GetIdentify) */ {

public:

……

static ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< ::communication::BaseMessage,

::PROTOBUF_NAMESPACE_ID::internal::MessageTypeTraits< ::communication::GetIdentify >, 11, false >

message;

……

};

其中MessageTypeTraits类的定义:由于知道了扩展字段对应的数据类型,所以可以动态的创建这种类型的变量,并把它添加到集合中

protobuf-master\src\google\protobuf\extension_set.h

// ExtensionSet guarantees that when manipulating extensions with message

// types, the implementation used will be the compiled-in class representing

// that type. So, we can static_cast down to the exact type we expect.

template

class MessageTypeTraits {

public:

typedef const Type& ConstType;

typedef Type* MutableType;

……

static inline MutableType Mutable(int number, FieldType field_type,

ExtensionSet* set) {

return static_cast(set->MutableMessage(

number, field_type, Type::default_instance(), NULL));

}

四、field的具体创建

其中的prototype.New(arena_)创建新的实例

protobuf-master\src\google\protobuf\extension_set.cc

MessageLite* ExtensionSet::MutableMessage(int number, FieldType type,

const MessageLite& prototype,

const FieldDescriptor* descriptor) {

Extension* extension;

if (MaybeNewExtension(number, descriptor, &extension)) {

extension->type = type;

GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE);

extension->is_repeated = false;

extension->is_lazy = false;

extension->message_value = prototype.New(arena_);

extension->is_cleared = false;

return extension->message_value;

} else {

GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, MESSAGE);

extension->is_cleared = false;

if (extension->is_lazy) {

return extension->lazymessage_value->MutableMessage(prototype);

} else {

return extension->message_value;

}

}

}

内部维护map结构,根据字段的ID作为键值来查找某个类型的字段是否已经创建

std::pair<:extension bool> ExtensionSet::Insert(int key) {

if (PROTOBUF_PREDICT_FALSE(is_large())) {

auto maybe = map_.large->insert({key, Extension()});

return {&maybe.first->second, maybe.second};

}

KeyValue* end = flat_end();

KeyValue* it =

std::lower_bound(flat_begin(), end, key, KeyValue::FirstComparator());

if (it != end && it->first == key) {

return {&it->second, false};

}

if (flat_size_ < flat_capacity_) {

std::copy_backward(it, end, end + 1);

++flat_size_;

it->first = key;

it->second = Extension();

return {&it->second, true};

}

GrowCapacity(flat_size_ + 1);

return Insert(key);

}

五、不同类型的field如何放在一个数组结构中

由于所有非基础类型都是派生自MessageLite,所以其中的message_value就是一个MessageLite指针

protobuf-master\src\google\protobuf\extension_set.h

struct Extension {

// The order of these fields packs Extension into 24 bytes when using 8

// byte alignment. Consider this when adding or removing fields here.

union {

int32 int32_value;

int64 int64_value;

uint32 uint32_value;

uint64 uint64_value;

float float_value;

double double_value;

bool bool_value;

int enum_value;

std::string* string_value;

MessageLite* message_value;

LazyMessageExtension* lazymessage_value;

RepeatedField* repeated_int32_value;

RepeatedField* repeated_int64_value;

RepeatedField* repeated_uint32_value;

RepeatedField* repeated_uint64_value;

RepeatedField* repeated_float_value;

RepeatedField* repeated_double_value;

RepeatedField* repeated_bool_value;

RepeatedField* repeated_enum_value;

RepeatedPtrField<:string>* repeated_string_value;

RepeatedPtrField* repeated_message_value;

};

六、自定义option

在proto3中依然可以使用这个添加通用结构中的属性,但是由于扩展的基础结构使用的是proto2语法,所以没什么特殊之处:

protobuf-master\src\google\protobuf\descriptor.proto

// The messages in this file describe the definitions found in .proto files.

// A valid .proto file can be translated directly to a FileDescriptorProto

// without any other information (e.g. without reading its imports).

syntax = "proto2";

package google.protobuf;

option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor";

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于Protobuf动态解析在Java的示例程序: 1. 下载protobuf-java.jar和protobuf-java-util.jar包并添加到classpath。 2. 定义一个proto文件,例如: ``` syntax = "proto3"; message Person { string name = 1; int32 age = 2; repeated string hobbies = 3; } ``` 3. 使用protoc编译proto文件,生成Java类: ``` protoc --java_out=. person.proto ``` 4. 在Java动态解析protobuf消息: ``` import com.google.protobuf.DynamicMessage; import com.google.protobuf.Descriptors; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.ByteString; public class DynamicProtoExample { public static void main(String[] args) throws Exception { Descriptors.Descriptor descriptor = Person.getDescriptor(); // Person是生成的Java类 DynamicMessage.Builder builder = DynamicMessage.newBuilder(descriptor); builder.setField(descriptor.findFieldByName("name"), "Tom"); builder.setField(descriptor.findFieldByName("age"), 18); builder.addRepeatedField(descriptor.findFieldByName("hobbies"), "reading"); builder.addRepeatedField(descriptor.findFieldByName("hobbies"), "swimming"); DynamicMessage message = builder.build(); ByteString data = message.toByteString(); DynamicMessage parsedMessage = DynamicMessage.parseFrom(descriptor, data); String name = parsedMessage.getField(descriptor.findFieldByName("name")).toString(); int age = (int) parsedMessage.getField(descriptor.findFieldByName("age")); List<String> hobbiesList = parsedMessage.getRepeatedField(descriptor.findFieldByName("hobbies")); System.out.println("Name: " + name + ", Age: " + age + ", Hobbies: " + hobbiesList); } } ``` 这个程序动态创建了一个Person消息,将它转化为字节数组,然后再将其解析回Person消息,并从获取字段值。 这是一个简单的示例,但它演示了如何使用Protobuf动态解析在Java创建和解析消息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值