johnpoint

johnpoint

(。・∀・)ノ゙嗨
github

Get value by field name in proto

It's been a long time since I updated my blog. On the one hand, I have been busy with internships outside of school, and there are really many things waiting for me to learn. It's terrifying, on the other hand, I'm just lazy.

Protobuf is really a good thing. It's just a bit tricky to get the value of a specific field when you don't know the exact structure. After a lot of tinkering, I finally figured it out. I'll write a blog post to record it.

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
}

This also takes into account the case where a message is nested inside a proto message, for reference only.

update:

There's a small pitfall. If the field is a slice, according to the source code, the interface obtained using the Interface() function is a pointer to this slice, and the type obtained through reflection is Ptr, which makes the subsequent processing a bit tricky. So I added a check inside the function to directly determine whether it is of List type, and return the corresponding processing based on the boolean value.

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
}
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.