ブログを更新していない間、一方では学校よりも忙しいインターンシップに行っていたため、本当に学ぶべきことがたくさんありました。怖いですね、もう一方ではただ怠けていただけです
protobuf は本当に素晴らしいものですが、具体的な構造がわからない場合に特定のフィールドの値を取得するのは少し手間がかかります。やっとやり方を見つけましたので、ブログに記録しておきます。
func FindByName(name string, msg protoreflect.Message) (has bool, value protoreflect.Value, isList bool) {
if name == "" {
return false, *new(protoreflect.Value), false
}
msgDesc := msg.Descriptor()
for i := 0; i < msgDesc.Fields().Len(); i++ {
if msgDesc.Fields().Get(i).Kind() == protoreflect.MessageKind {
sonMsg := msgDesc.Fields().Get(i)
has, value, isList = FindByName(name, msg.Get(sonMsg).Message()) // type mismatch: cannot convert list to message
if has {
return has, value, isList
}
}
if msgDesc.Fields().Get(i).Name() == protoreflect.Name(name) {
return true, msg.Get(msgDesc.Fields().Get(i)), msgDesc.Fields().Get(i).IsList()
}
}
return false, value, false
}
この関数は、proto メッセージ内にメッセージがネストされている場合も考慮しています。参考までに。
update:
ちょっとした落とし穴があります。フィールドがスライスの場合、ソースコードを見ると、Interface () 関数を使用して取得したインターフェースは、そのスライスのポインタです。リフレクションで取得した型は Ptr 型になり、後続の処理が少し面倒になります。そのため、関数内で直接判断を追加し、List 型かどうかを判断して、対応する処理を行うようにしました。
func (v Value) Interface() interface{} {
switch v.typ {
case nilType:
return nil
case boolType:
return v.Bool()
case int32Type:
return int32(v.Int())
case int64Type:
return int64(v.Int())
case uint32Type:
return uint32(v.Uint())
case uint64Type:
return uint64(v.Uint())
case float32Type:
return float32(v.Float())
case float64Type:
return float64(v.Float())
case stringType:
return v.String()
case bytesType:
return v.Bytes()
case enumType:
return v.Enum()
default:
return v.getIface()
}
}
func (v Value) getIface() (x interface{}) {
*(*ifaceHeader)(unsafe.Pointer(&x)) = ifaceHeader{Type: v.typ, Data: v.ptr}
return x
}