解析GRPC响应到通用类型时出现问题

huangapple go评论69阅读模式
英文:

Issue with Unmarshalling GRPC Response To Generic Type

问题

我正在尝试使用泛型将一个JSON对象转换为具有枚举类型的GRPC响应,代码如下:

type GRPCResponse struct {
    str string
    enu EnumType
}

type EnumType int32
const (
    Type1 EnumType = 0
    Type2 EnumType = 1
)

反序列化的函数如下:

func assertHTTPResponseOK[T any](t *testing.T, endpoint string) T {
    body, err := GetResponse(endpoint)

    var v T
    err := json.Unmarshal(body, &v)
    require.Nil(t, err)
    return v
}

调用该函数的代码如下:

assertHTTPResponseOK[*GRPCResponse](t, "some-endpoint")

所涉及的JSON对象如下:

{"str":"hello", "enu": "Type2"}

我遇到了类似以下错误:

json: cannot unmarshal string into Go struct field GRPCResponse.enu of type EnumType

从类似的问题中,我看到通常的建议是使用jsonpb.Unmarshalprotojson.Unmarshal而不是典型的json.Unmarshal

在更改Unmarshal函数时,我还必须将T更改为protoreflect.ProtoMessage。然而,这样做会阻止我将指针传递给Unmarshal,因为它是指向接口的指针,而不是接口本身。当然,我也不能传递空指针(不取v的地址)。

所以我的问题是:

  1. 有没有办法使这个泛型对象的指针满足protoreflect.ProtoMessage接口?
  2. 有没有更好的反序列化函数可以更好地解决我的问题?
英文:

I am trying to use generics to convert a JSON object into a GRPC response that has an enum, i.e.:

type GRPCResponse {
    str string
    enu EnumType
}

type EnumType int32
const (
    Type1 EnumType = 0
    Type2 EnumType = 1
)

The function for unmarshalling looks like this:

func assertHTTPResponseOK[T any](t *testing.T, endpoint string) T {
    body, err := GetResponse(endpoint)

    var v T
    err := json.Unmarshal(body, &v)
    require.Nil(t, err)
    return v
}

And the code calling it looks like this:

assertHTTPResponseOK[*GRPCResponse](t, "some-endpoint")

The JSON object in question looks like this:

{"str":"hello", "enu": "Type2"}

and I am getting an error along the lines of:

json: cannot unmarshal string into Go struct field GRPCResponse.enu of type EnumType

From similar questions, I have seen that the usual advice is to use jsonpb.Unmarshal or protojson.Unmarshal instead of the typical json.Unmarshal.

In changing the Unmarshal function, I also had to change T to be protoreflect.ProtoMessage. However, this prevents me from passing a pointer to v to Unmarshal, because it is a pointer to the interface, not the interface. Of course, I also cannot pass in a nil pointer (not take the address of v).

So my questions are:

  1. Is there a way to have the pointer of this generic object satisfy the interface protoreflect.ProtoMessage?
  2. Is there a better Unmarshalling function that would better fit my problem?

答案1

得分: 2

这是一个支持泛型的 proto 反序列化器,它避免了传递第二个类型,但代价是使用反射调用来查看指针内部的类型并调用其构造函数。

var msg T // 限制为 proto.Message

// 查看 T 内部的类型(T = *SomeProtoMsgType)
msgType := reflect.TypeOf(msg).Elem()

// 创建一个新的实例,并将其赋值给 T
msg = reflect.New(msgType).Interface().(T)

errUnmarshal := proto.Unmarshal(body, msg)

请注意,这只是代码的翻译,我不会回答关于翻译内容的问题。

英文:

Here's a generics-friendly proto unmarshaller that avoids passing the second type, at the cost of a reflective invoke to peek the type inside the pointer and call it's constructor.

		var msg T // Constrained to proto.Message

        // Peek the type inside T (as T= *SomeProtoMsgType)
		msgType := reflect.TypeOf(msg).Elem()

        // Make a new one, and throw it back into T
		msg = reflect.New(msgType).Interface().(T)

		errUnmarshal := proto.Unmarshal(body, msg)

答案2

得分: 1

我最终将要解组的对象传递进去。

obj := new(GRPCResponse)
assertHTTPResponseOK[*GRPCResponse](t, ctx, "some-endpoint", obj)
func assertHTTPResponseOK[T protoreflect.ProtoMessage](t *testing.T, ctx context.Context, endpoint string, object T) {
	body, err := GetResponse(endpoint)
	require.Nil(t, err)

	err = protojson.Unmarshal(body, object)
	require.Nil(t, err)
}
英文:

I ended up passing in the object I am unmarshalling into.

obj := new(GRPCResponse)
assertHTTPResponseOK[*GRPCResponse](t, ctx, "some-endpoint", obj)
func assertHTTPResponseOK[T protoreflect.ProtoMessage](t *testing.T, ctx context.Context, endpoint string, object T) {
	body, err := GetResponse(endpoint)
	require.Nil(t, err)

	err = protojson.Unmarshal(body, object)
	require.Nil(t, err)
}

huangapple
  • 本文由 发表于 2022年4月20日 01:07:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/71928719.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定