Skip to content

Best way to clone a protobuf object #1006

Open
@cghawthorne

Description

@cghawthorne

protobuf.js version: 6.8.6

I have several places in my code where I'd like to clone a protobuf object and make changes to the clone that don't affect the original. I've tried several methods but haven't been able to find one that works and is efficient.

Here are some examples:

test.proto

package test;
syntax = "proto3";

message Inner {
  string id = 1;
}

message Test {
  string id = 1;
  repeated Inner inner = 2;
}
$ pbjs -t static-module -w commonjs -o compiled.js test.proto

test.js:

var clone = require('clone');
var proto = require('./compiled');

var a = proto.test.Test.create();

// Clone method goes here.

b.inner.push(proto.test.Inner.create());
if (a.inner.length != 0) {
  throw new Error();
}

First, I tried using the clone package:

var b = clone(a);

This causes some kind of problem with the prototype of the object.

test.js:11
b.inner.push(proto.test.Inner.create());
        ^

TypeError: Cannot add property 0, object is not extensible
    at Array.push (<anonymous>)
    at Object.<anonymous> (/usr/local/google/home/fjord/m/protoissue/test.js:11:9)
    at Module._compile (module.js:569:30)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:503:32)
    at tryModuleLoad (module.js:466:12)
    at Function.Module._load (module.js:458:3)
    at Function.Module.runMain (module.js:605:10)
    at startup (bootstrap_node.js:158:16)
    at bootstrap_node.js:575:3

Then, I tried just passing the first object to create:

var b = proto.test.Test.create(a);

This doesn't produce a deep clone:

test.js:13
  throw new Error();
  ^

Then, I tired to/from Object:

var b = proto.test.Test.fromObject(proto.test.Test.toObject(a));

This works, but toObject only supports an actual Test proto object, and we want our API to support users supplying any object that matches ITest.

This is the approach we ended up using:

var b = proto.test.Test.decode(proto.test.Test.encode(a).finish());

This produces an actual deep clone (guaranteed because object references clearly aren't preserved across serialization) and it works with either Test or ITest. However, this seems really heavyweight for just cloning an object.

Do you have suggestions for the best way to do this? It would be great if protobuf.js had a clone() method that was the officially supported way of cloning an object.

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions