当前位置: 首页 > 知识库问答 >
问题:

如何在Go中的运行时创建“鸭子类型”grpc原型消息?

莫誉
2023-03-14

我正在使用grpc protobuf消息定义,并在Go中实现它们。

我的最终目标是让我的rpc在用户上检索一些json并返回带有可选嵌套消息的ProfileMessage,用于解组json的子集。

使用此rpc:

  rpc GetUser (GetUserRequest) returns (Profile) {
    option (google.api.http) = {
      get: "/user/{id=*}"
    };
  }

假设如下:

{
  "profile": {
     "foo": {
        "name": "tim"
        "age": 22
      },
     "bar": {
       "level": 5
     }
  }
}

我想返回一个Profile消息,该消息仅包含“foo”、“bar”或两者,作为基于grpc请求的传入运行时scope参数的嵌套消息(目前,scope将是包含用于将json子设置为相应消息的消息名称的字符串列表,例如["Foo","Bar"])。

给定这些Message定义:

message Profile {
  //both Foo & Bar are optional by default
  Foo foo = 1 [json_name="foo"];
  Bar bar = 2 [json_name="bar"];
}

message Foo {
  string name = 1 [json_name="name"];
  int32 age = 2 [json_name="age"];
}

message Bar {
  string level = 1 [json_name="level"];
}

然后,如果范围是,我希望rpc返回:

Profile{
  Foo: // Foo Message unmarshalled from json
}

或者如果"scope"["Foo","Bar"]那么:

Profile{
  Foo: 
  Bar:
}

问题似乎可以归结为“duck-typing”消息类型。

我几乎用protoreflect

import(
    "google.golang.org/protobuf/reflect/protoregistry"
    "google.golang.org/protobuf/reflect/protoreflect"
)

var scope protoreflect.FullName = "Foo"
var types = new(protoregistry.Types)
var message, errs = types.FindMessageByName(scope)
var almost_foo = message.New()

// using `myJson` object without top level "profile" key to make testing more simple at the moment
var myJson = `{ "foo": { .. }, "bar", { ... } }`
err = json.Unmarshal([]byte(myJson), almost_foo)

但当我尝试使用几乎_foo创建配置文件消息时:

var profile = &pb.Profile{almost_foo}

我得到错误:不能使用几乎_foo(protoreflect.Message类型)作为类型*package_名称。字段值中的Foo

使用

import(
  "github.com/jhump/protoreflect/desc"
  "github.com/jhump/protoreflect/dynamic"
)

我尝试再次动态创建消息:

var fd, errs = desc.LoadFileDescriptor("github.com/package/path/..")
var message_desc = fd.FindMessage("Foo")
var almost_foo = dynamic.NewMessage(message_desc)

出现了一个类似的错误:不能使用几乎_foo(type*dynamic.Message)作为type*package_名称。字段值中的Foo

两次尝试几乎都会创建一条消息,但类型系统仍然不允许实际使用这两种消息。

感谢您的帮助。

共有1个答案

吕征
2023-03-14

我设法解决了这个问题,首先将json缩减到我想要的大小,然后将其解组。

我创建了一条顶级消息,将Profile作为一个字段:

message GetUserResponse {
  Profile profile = 1;
}

并重新定义了反应:

rpc GetUser (GetUserRequest) returns (GetUserResponse)

要构建允许/限定范围的json,我使用:

"github.com/tidwall/gjson"
"github.com/tidwall/sjson"

我将作用域路径(以点分隔,例如[“profile.foo”])指定的json复制到一个最初为空的输出响应_json字符串中,并只构建允许返回的json。

var response_json string

var scopes = []string{"profile.foo"}

for _, scope := range scopes {
    // copy allowed json subset in raw form
    value := gjson.Get(myJson, scope + "|@ugly")

    // if scope didn't copy a value, skip
    if value.String() == "" {
        continue
    } else {
        // write value to output json using SetRaw since value can be an object
        response_json, _ = sjson.SetRaw(response_json, scope, value.Raw)
    }
}

响应如下所示:

{
  "profile": {
     "foo": {
        "name": "tim"
        "age": 22
      }
  }
}

然后解开玛莎:

response = &pb.VerifyUserResponse{}
err = json.Unmarshal([]byte(response_json), response)
 类似资料:
  • “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” 我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。 比如在python中,有很多file-like的东西,比如StringIO,GzipFile,socket。它们有很多相同的方法,我们把它们当作文件使用。 又比如list.extend()方法中,我们并不关心它的参数是不是list,只要它是可迭代的,

  • 问题内容: 我试图在GO中创建错误的子类型,现在,我面临多种类型的问题。以下代码显示了错误类型定义: 当我尝试运行以下代码时: 该函数打印以下内容: 我需要将的类型标识为,而不是。我怎样才能做到这一点?我的方法有什么问题吗? 问题答案: 您使用了该方法,但是没有将其添加到“定义”接口中,因此请执行以下操作: 而你想成为一个。对于成为一个,它必须实现所有的方法:和。因此,您必须同时添加这两种方法:

  • 问题内容: 在Go中,如何在运行时根据对象的类型创建对象的实例?我想您还需要先获取对象的实际值吗? 我正在尝试执行惰性实例化以节省内存。 问题答案: 为了做到这一点,你需要。 您可以使用结构类型(而不是int)执行相同的操作。还是其他的,真的。只需确保了解map和slice类型时new和make之间的区别即可。

  • 我目前正在构建一个python gRPC服务器,它将大量不同的proto消息序列化为json,以将它们存储到无sql数据库中。我希望简化此服务器的扩展,这样我们就可以添加新类型,而无需重写gRPC服务器和重新部署。理想情况下,我们希望定义一条新消息,将其放入一个proto文件中,并仅更新客户端。服务器首先应该期望任何类型,但知道。进行序列化/反序列化时要查找的原型文件或文件夹。 我读过关于“任何类

  • 我试图用web3j编写一个Java应用程序,它可以读取任意的abi文件,向用户显示AbiDefinitions列表,并让用户调用自己选择的常量函数。我如何计算下面的输出类型? TypeReference 类对泛型类型使用技巧,当泛型类型在源代码中硬编码时,这些技巧可以正常工作,如下所示: 这就是生成的合约包装器要做的。 对于简单类型,我可以这样做: 对于“int256[2]”这样的数组类型,该怎么

  • 问题内容: 我几乎没有这样声明的C函数 我想将这些作为一个Go函数公开 所以我需要能够在运行时检查 参数 类型。我该怎么做,这是个好主意吗(如果不是,在这种情况下,什么是好的做法)? 问题答案: 在此处查看类型断言: http://golang.org/ref/spec#Type_assertions 我只断言一个明智的类型(字符串,uint64)等,并使其尽可能宽松,最后执行到本机类型的转换。