diff --git a/src/converter.js b/src/converter.js index 364c57c0a..f1ed9c0ac 100644 --- a/src/converter.js +++ b/src/converter.js @@ -9,6 +9,8 @@ var Enum = require("./enum"), types = require("./types"), util = require("./util"); +var wrapperValueRe = /^\.google\.protobuf\.(?:DoubleValue|FloatValue|Int64Value|UInt64Value|Int32Value|UInt32Value|BoolValue|StringValue|BytesValue)$/; + /** * Generates a partial value fromObject conveter. * @param {Codegen} gen Codegen instance @@ -41,10 +43,13 @@ function genValuePartial_fromObject(gen, field, fieldIndex, prop) { ("break"); } gen ("}"); - } else gen + } else { + if (!wrapperValueRe.test(field.resolvedType.fullName)) gen ("if(typeof d%s!==\"object\")", prop) - ("throw TypeError(%j)", field.fullName + ": object expected") + ("throw TypeError(%j)", field.fullName + ": object expected"); + gen ("m%s=types[%i].fromObject(d%s,q+1)", prop, fieldIndex, prop); + } } else { var isUnsigned = false; switch (field.type) { @@ -112,6 +117,9 @@ converter.fromObject = function fromObject(mtype) { ("if(q===undefined)q=0") ("if(q>util.recursionLimit)") ("throw Error(\"max depth exceeded\")"); + if (wrapperValueRe.test(mtype.fullName)) gen + ("if(d!=null&&typeof d!==\"object\")") + ("d={value:d}"); if (!fields.length) return gen ("return new this.ctor"); gen @@ -313,6 +321,9 @@ converter.toObject = function toObject(mtype) { gen ("}"); } + if (wrapperValueRe.test(mtype.fullName)) gen + ("if(o.json)") + ("return d.value"); return gen ("return d"); /* eslint-enable no-unexpected-multiline, block-scoped-var, no-redeclare */ diff --git a/tests/cli.js b/tests/cli.js index 5af4e5e17..ac51ffe12 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -112,6 +112,60 @@ tape.test("pbjs generates correct ES6 static-module imports", function(test) { }); }); +tape.test("pbjs generates static wrapper conversions", function(test) { + cliTest(test, function() { + var root = new protobuf.Root().addJSON(protobuf.common["google/protobuf/wrappers.proto"].nested).addJSON({ + WrapperContainer: { + fields: { + name: { + id: 1, + type: "google.protobuf.StringValue" + }, + count: { + id: 2, + type: "google.protobuf.Int64Value" + }, + flag: { + id: 3, + type: "google.protobuf.BoolValue" + }, + data: { + id: 4, + type: "google.protobuf.BytesValue" + } + } + } + }).resolveAll(); + + var staticTarget = require("../cli/targets/static"); + + staticTarget(root, { + create: true, + encode: true, + decode: true, + convert: true + }, function(err, jsCode) { + test.error(err, "static code generation worked"); + + var $protobuf = protobuf; + eval(jsCode); + + var WrapperContainer = protobuf.roots.default.WrapperContainer; + var object = { + name: "abc", + count: "123", + flag: false, + data: "AQI=" + }; + var message = WrapperContainer.fromObject(object); + + test.same(WrapperContainer.toObject(message, { json: true, bytes: String, longs: String }), object, "should use wrapper scalar representation for json conversion"); + test.same(WrapperContainer.toObject(message, { bytes: String, longs: String }).name, { value: "abc" }, "should preserve wrapper object representation without json conversion"); + test.end(); + }); + }); +}); + tape.test("pbjs escapes static target names", function(test) { cliTest(test, function() { var root = protobuf.Root.fromJSON({ diff --git a/tests/comp_google_protobuf_wrappers.js b/tests/comp_google_protobuf_wrappers.js new file mode 100644 index 000000000..b483972f5 --- /dev/null +++ b/tests/comp_google_protobuf_wrappers.js @@ -0,0 +1,69 @@ +var tape = require("tape"); + +var protobuf = require(".."); + +var root = new protobuf.Root().addJSON(protobuf.common["google/protobuf/wrappers.proto"].nested).addJSON({ + Foo: { + fields: { + stringValue: { + id: 1, + type: "google.protobuf.StringValue" + }, + int64Value: { + id: 2, + type: "google.protobuf.Int64Value" + }, + boolValue: { + id: 3, + type: "google.protobuf.BoolValue" + }, + bytesValue: { + id: 4, + type: "google.protobuf.BytesValue" + } + } + } +}).resolveAll(); + +var Foo = root.lookupType("Foo"), + StringValue = root.lookupType("google.protobuf.StringValue"); + +tape.test("google.protobuf wrapper types", function(test) { + + var foo = Foo.fromObject({ + stringValue: "abc", + int64Value: "123", + boolValue: false, + bytesValue: "AQI=" + }); + + test.same(Foo.toObject(foo, { json: true, bytes: String, longs: String }), { + stringValue: "abc", + int64Value: "123", + boolValue: false, + bytesValue: "AQI=" + }, "should convert wrapper fields using their JSON scalar representation"); + + foo = Foo.fromObject({ + stringValue: { + value: "abc" + } + }); + + test.same(Foo.toObject(foo), { + stringValue: { + value: "abc" + } + }, "should preserve the existing object representation without json conversion"); + + var stringValue = StringValue.fromObject("abc"); + + test.same(StringValue.toObject(stringValue), { + value: "abc" + }, "should preserve direct wrapper objects without json conversion"); + test.equal(StringValue.toObject(stringValue, { json: true }), "abc", "should unwrap direct wrapper objects with json conversion"); + + test.same(StringValue.toObject(StringValue.fromObject({})), {}, "should preserve empty wrapper objects"); + + test.end(); +});