protobuf-ts icon indicating copy to clipboard operation
protobuf-ts copied to clipboard

Dynamic types based on proto definition

Open Joel-PeakMetrics opened this issue 10 months ago • 1 comments

Hello,

First off, thank you for contributing this work to the open-source community using protobuf on the unofficially supported NodeJS / Typescript language.

I'm integrating this library with a schema registry that contains proto definitions. Is there an example of how to create a model class to use for serialization or deserialization on the fly?

Here's the code I've got working with protobufjs doing deserialization, but I don't like how this library handles bigints, etc.

Note: this is running with Deno. Your library works fine with Deno besides the extensions issue in the imports (which is easily fixed with the search/replace script).

import { default as protobuf } from "npm:protobufjs";

const protoDefinition = "syntax = \"proto3\";\n message ExampleMessage { \n    string id = 1; \n}";
const parsedMessage = protobuf.parse(protoDefinition);
const root = parsedMessage.root;
const protobufSchema = root.lookupType(
    getTypeName(
        parsedMessage, {
            messageName: props.schemaName,
        },
    ));

protobufSchema.decode(serializedMessage);

// helper functions
function getNestedTypeName(
    parent: { [k: string]: protobuf.ReflectionObject } | undefined,
): string {
    if (!parent) {
        throw new Error("Invalid parent");
    }
    const keys = Object.keys(parent);
    const reflection = parent[keys[0]];
    // Traverse down the nested Namespaces until we find a message Type instance (which extends Namespace)
    if (
        reflection instanceof protobuf.Namespace &&
        !(reflection instanceof protobuf.Type) && reflection.nested
    ) {
        return getNestedTypeName(reflection.nested);
    }
    return keys[0];
}
function getTypeName(
    parsedMessage: protobuf.IParserResult,
    opts: { messageName?: string } = {},
) {
    const root = parsedMessage.root;
    const pkg = parsedMessage.package;
    const name = opts && opts.messageName ? opts.messageName : getNestedTypeName(root.nested);
    return `${pkg ? pkg + "." : ""}.${name}`;
}

TYIA for your help, appreciate it!

Joel-PeakMetrics avatar Aug 24 '23 01:08 Joel-PeakMetrics