【问题标题】:Calling parseFrom() method for generic protobuffer class in java在 java 中调用泛型 protobuffer 类的 parseFrom() 方法
【发布时间】:2014-12-24 20:46:35
【问题描述】:

我正在调用一个 api 来获取一个输入流,然后调用静态方法 parseFrom(inputstream) 将其转换为 protobuffclass。

如果我使用特定的课程,它会起作用:

public CustomerDTOOuterClass.CustomerDTO GetCustomer()
{
    CustomerDTOOuterClass.CustomerDTO customer = null;
    try
    {
        URL url = new URL("https://localhost:44302/Api/customer/1?");

        HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Content-Type", "application/x-protobuf");
        conn.connect();

        InputStream is = conn.getInputStream();

        CustomerDTOOuterClass.CustomerDTO customer =
                CustomerDTOOuterClass.CustomerDTO.parseFrom(is);

        conn.disconnect();
    }
    catch(Exception ex)
    {
        System.out.println("[ "+ex.getMessage()+" ]");
    }

    return customer;
}

但是如果我将它更改为泛型类型它会失败,因为 T 没有方法 parseFrom,我可以在 T 中实现任何接口以便我可以调用 parseFrom 方法吗?

public T GetObject()
{
    T object = null;
    try
    {
        URL url = new URL("https://localhost:44302/Api/customer/1?");

        HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Content-Type", "application/x-protobuf");
        conn.connect();

        InputStream is = conn.getInputStream();

        T object = T.parseFrom(is);

        conn.disconnect();
    }
    catch(Exception ex)
    {
        System.out.println("[ "+ex.getMessage()+" ]");
    }

    return object;
}

这是我得到的错误: 错误:(68, 27) 错误:找不到符号方法 parseFrom(InputStream)

【问题讨论】:

  • 通过快速搜索找到两件事情,也许一件有帮助:stackoverflow.com/questions/4035027/…transylvania-jug.org/archives/365
  • 你能告诉我们你想要达到的目标吗?如果您的处理非常通用,您可以查看 DynamicMessage。或者,您可以将 Builder 类传递给 getObject() 方法并使用 builder 类的 mergeFrom 方法
  • 你真的不能这样做吗?序列化的原型不知道自己的类型。

标签: java generics protocol-buffers


【解决方案1】:

每个生成的 protobuf 类型都包含一个名为 PARSER 的静态成员,它是 com.google.protobuf.Parser<T> 接口的实现。您的getObject 方法只需将Parser<T> 作为参数即可。那么你可以这样称呼它:

Foo foo = getObject(Foo.PARSER);

【讨论】:

  • 好答案,我对这将如何发挥很感兴趣,所以我稍微修改了一下,并在单独的答案中进行了扩展,希望你不介意!
【解决方案2】:

如果您想为T 执行此操作,将Class<T>(即Proto 类型的类)传递给您的类的构造函数,然后从中获取Parser 会更容易、更自然像这样:

public class Thing<T extends Message> {
    final Parser<T> parser;

    Thing(Class<T> cls) {
        parser = (Parser<T>) cls.getMethod("parser").invoke(null);
    }

    T deserialize(byte[] bytes) {
        parser.parseFrom(bytes);  // try/catch etc
    }
}

【讨论】:

    【解决方案3】:

    扩展 Kenton Varda 的回答:

    首先,我会将您的方法重构为单独的方法,以获取输入流并对其进行解析。只有后者有任何理由是通用的。

    public InputStream getInputStream() {
      // get it
    }
    

    现在您打算解析输入流并从 protobuf 构建一个 POJO。 IMO 有理由期望此时您的代码必须知道您将获得什么类型的对象,否则接下来您将如何使用它做一些智能的事情?例如

    InputStream is = getInputStream();
    Object o = parseGenericInputStream(is);
    doSomethingWithParsedObject(o); // how to do this if you don't know o's type?
    

    一旦你解析了o的类型,你就必须知道它的类型(因此在你解析它之前),否则你无法用它做任何我能想到的有意义的事情。

    所以...再次感谢 Kenton Varda:

    public void doStuff() {
      ...
      InputStream is = getInputStream();
      MyProtobufClass pojo = parseGenericInputStream(MyProtobufClass.PARSER, is);
      doSomethingWithParsedObject(pojo);
      ...
    }
    
    private <T> T parseGenericInputStream(Parser<T> parser, InputStream inputStream)
        throws InvalidProtocolBufferException {
      return parser.parseFrom(inputStream);
    }
    

    虽然此时你正在为一行代码编写一个通用方法,但如果你问我,这有点不值得。

    【讨论】:

      【解决方案4】:

      不,没有;你不能在不知道它的类型的情况下反序列化一个原型。

      如果你知道它的类型,那么你可以传入一个 Builder 来表示它的类型。

      (此外,您不能对T 之类的类型变量调用静态方法。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-21
        • 2012-01-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多