1

I have a message with a field of the "Any" well known type which can hold a serialized protobuf message of any type.

I want to convert this field to its json representation.

I know the field names are required, and typically you would need the generated classes loaded in the app for this to work, but I am looking for a way to do it with the descriptors.

First, I parse the descriptors:

 FileInputStream descriptorFile = new FileInputStream("/descriptor");
DescriptorProtos.FileDescriptorSet fdp = DescriptorProtos.FileDescriptorSet.parseFrom(descriptorFile);

Then, loop through the contained messages and find the correct one (using the "Any" type's URL, which contains the package and message name. I add this to a TypeRegistry which is used to format the JSON.

JsonFormat.TypeRegistry.Builder typeRegistryBuilder = JsonFormat.TypeRegistry.newBuilder();

String messageNameFromUrl = member.getAny().getTypeUrl().split("/")[1];

for (DescriptorProtos.FileDescriptorProto file : fdp.getFileList()) {
    for (DescriptorProtos.DescriptorProto dp : file.getMessageTypeList()) {
        if (messageNameFromUrl.equals(String.format("%s.%s", file.getPackage(), dp.getName()))) {

            typeRegistryBuilder.add(dp.getDescriptorForType()); //Doesn't work.
            typeRegistryBuilder.add(MyConcreteGeneratedClass.getDescriptor()); //Works

            System.out.println(JsonFormat.printer().usingTypeRegistry(typeRegistryBuilder.build()).preservingProtoFieldNames().print(member.getAny()));
            return;
        }

    }
}

The problem seems to be that parsing the descriptor gives me access to Descriptors.DescriptorProto objects, but I see no way to get the Descriptors.Descriptor object needed for the type registry. I can access the concrete class's descriptor with getDescriptor() and that works, but I am trying to format the JSON at runtime by accessing a pre-generated descriptor file from outside the app, and so I do not have that concrete class available to call getDescriptor()

What would be even better is if I could use the "Any" field's type URL to resolve the Type object and use that to generate the JSON, since it also appears to have the field numbers and names as required for this process.

Any help is appreciated, thanks!

1
  • I also tried using DynamicMessage to parse the Any, but that also requires passing in a Descriptors.Descriptor which I can't seem to get from the Descriptors.DescriptorProto Commented Jan 2, 2019 at 22:18

1 Answer 1

2

If you convert a DescriptorProtos.FileDescriptorProto to Descriptors.FileDescriptor, the latter has a getMessageTypes() method that returns List<Descriptor>.

Following is a snippet of Kotlin code taken from an open-source library I'm developing called okgrpc. It's the first of its kind attempt to create a dynamic gRPC client/CLI in Java.

private fun DescriptorProtos.FileDescriptorProto.resolve(
        index: Map<String, DescriptorProtos.FileDescriptorProto>,
        cache: MutableMap<String, Descriptors.FileDescriptor>
): Descriptors.FileDescriptor {
    if (cache.containsKey(this.name)) return cache[this.name]!!

    return this.dependencyList
        .map { (index[it] ?: error("Unknown dependency: $it")).resolve(index, cache) }
        .let {
            val fd = Descriptors.FileDescriptor.buildFrom(this, *it.toTypedArray())
            cache[fd.name] = fd
            fd
        }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.