在java中调用通用protobuffer类的parseFrom()方法

问题描述 投票:0回答:5

我正在调用 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)

java generics protocol-buffers
5个回答
12
投票

每个生成的 protobuf 类型都包含一个名为

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

Foo foo = getObject(Foo.PARSER);

9
投票

如果你想对

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
    }
}

1
投票

扩展 Kenton Varda 的答案:

首先,我将您的方法重构为单独的方法来获取输入流并解析它。只有后者才有理由通用。

public InputStream getInputStream() {
  // get it
}

现在您打算解析输入流并从 protobuf 构建 POJO。在我看来,此时您的代码必须知道您将获得什么类型的对象是合理的,因为否则接下来您将如何用它做一些智能的事情呢?例如

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);
}

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


0
投票

不,没有;如果不知道原型的类型,就无法反序列化原型。

如果您确实知道它的类型,那么您可以传入其类型的 Builder。

(此外,您不能在类型变量上调用静态方法,如

T
。)


0
投票

两步:

  1. 创建一个抛出 IOException 的函数接口,就像 MyProtobufClass.Entity::parseFrom 那样。
  2. 在通用加载方法中使用此接口。
    @FunctionalInterface
    public interface ParserFunction<T, R> {
        R apply(T t) throws IOException;
    }

    public class Loader {
    
        public static <T> T loadPbFile(
            String path,
            ParserFunction<FileInputStream, T> parser) throws IOException {
            FileInputStream inputStream = getFileInputStream(path);
            return parser.apply(inputStream);
        }
    }

现在只需使用 MyProtobufClass.Entity::parseFrom 作为泛型函数的第二个参数。

© www.soinside.com 2019 - 2024. All rights reserved.