diff --git a/nodes/core/locales/en-US/messages.json b/nodes/core/locales/en-US/messages.json index b9c414c5f..6665387ae 100644 --- a/nodes/core/locales/en-US/messages.json +++ b/nodes/core/locales/en-US/messages.json @@ -674,7 +674,14 @@ }, "label": { "o2j": "Object to JSON options", - "pretty": "Format JSON string" + "pretty": "Format JSON string", + "action": "Action", + "property": "Property", + "actions": { + "toggle": "Convert between JSON String & Object", + "str":"Always convert to JSON String", + "obj":"Always convert to JavaScript Object" + } } }, "yaml": { diff --git a/nodes/core/parsers/70-JSON.html b/nodes/core/parsers/70-JSON.html index 9b6584378..0dfbfe21e 100644 --- a/nodes/core/parsers/70-JSON.html +++ b/nodes/core/parsers/70-JSON.html @@ -4,11 +4,25 @@ -
+ + +
+
+ + +
+ +
+ +
-
+
@@ -30,6 +44,14 @@ +

Details

+

By default, the node operates on msg.payload, but can be configured + to convert any message property.

+

The node can also be configured to ensure a particular encoding instead of toggling + between the two. This can be used, for example, with the HTTP In + node to ensure the payload is a parsed object even if an incoming request + did not set its content-type correctly for the HTTP In node to do the conversion.

+ diff --git a/nodes/core/parsers/70-JSON.js b/nodes/core/parsers/70-JSON.js index 5709e8f57..aed328574 100644 --- a/nodes/core/parsers/70-JSON.js +++ b/nodes/core/parsers/70-JSON.js @@ -20,29 +20,40 @@ module.exports = function(RED) { function JSONNode(n) { RED.nodes.createNode(this,n); this.indent = n.pretty ? 4 : 0; + this.action = n.action||""; + this.property = n.property||"payload"; var node = this; this.on("input", function(msg) { - if (msg.hasOwnProperty("payload")) { - if (typeof msg.payload === "string") { - try { - msg.payload = JSON.parse(msg.payload); - node.send(msg); - } - catch(e) { node.error(e.message,msg); } - } - else if (typeof msg.payload === "object") { - if (!Buffer.isBuffer(msg.payload)) { + var value = RED.util.getMessageProperty(msg,node.property); + if (value !== undefined) { + if (typeof value === "string") { + if (node.action === "" || node.action === "obj") { try { - msg.payload = JSON.stringify(msg.payload,null,node.indent); + RED.util.setMessageProperty(msg,node.property,JSON.parse(value)); node.send(msg); } - catch(e) { node.error(RED._("json.errors.dropped-error")); } + catch(e) { node.error(e.message,msg); } + } else { + node.send(msg); + } + } + else if (typeof value === "object") { + if (node.action === "" || node.action === "str") { + if (!Buffer.isBuffer(value)) { + try { + RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent)); + node.send(msg); + } + catch(e) { node.error(RED._("json.errors.dropped-error")); } + } + else { node.warn(RED._("json.errors.dropped-object")); } + } else { + node.send(msg); } - else { node.warn(RED._("json.errors.dropped-object")); } } else { node.warn(RED._("json.errors.dropped")); } } - else { node.send(msg); } // If no payload - just pass it on. + else { node.send(msg); } // If no property - just pass it on. }); } RED.nodes.registerType("json",JSONNode); diff --git a/test/nodes/core/parsers/70-JSON_spec.js b/test/nodes/core/parsers/70-JSON_spec.js index 6d3d857f3..833d118e1 100644 --- a/test/nodes/core/parsers/70-JSON_spec.js +++ b/test/nodes/core/parsers/70-JSON_spec.js @@ -28,17 +28,8 @@ describe('JSON node', function() { helper.unload(); }); - it('should be loaded', function(done) { - var flow = [{id:"jsonNode1", type:"json", name: "jsonNode" }]; - helper.load(jsonNode, flow, function() { - var jsonNode1 = helper.getNode("jsonNode1"); - jsonNode1.should.have.property('name', 'jsonNode'); - done(); - }); - }); - it('should convert a valid json string to a javascript object', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"}, + var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, {id:"jn2", type:"helper"}]; helper.load(jsonNode, flow, function() { var jn1 = helper.getNode("jn1"); @@ -56,7 +47,7 @@ describe('JSON node', function() { }); it('should convert a javascript object to a json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"}, + var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, {id:"jn2", type:"helper"}]; helper.load(jsonNode, flow, function() { var jn1 = helper.getNode("jn1"); @@ -71,7 +62,7 @@ describe('JSON node', function() { }); it('should convert a array to a json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"}, + var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, {id:"jn2", type:"helper"}]; helper.load(jsonNode, flow, function() { var jn1 = helper.getNode("jn1"); @@ -86,7 +77,7 @@ describe('JSON node', function() { }); it('should log an error if asked to parse an invalid json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"}, + var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, {id:"jn2", type:"helper"}]; helper.load(jsonNode, flow, function() { try { @@ -108,7 +99,7 @@ describe('JSON node', function() { }); it('should log an error if asked to parse something thats not json or js', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"}, + var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, {id:"jn2", type:"helper"}]; helper.load(jsonNode, flow, function() { var jn1 = helper.getNode("jn1"); @@ -137,7 +128,7 @@ describe('JSON node', function() { }); it('should pass straight through if no payload set', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"}, + var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, {id:"jn2", type:"helper"}]; helper.load(jsonNode, flow, function() { var jn1 = helper.getNode("jn1"); @@ -151,4 +142,105 @@ describe('JSON node', function() { }); }); + it('should ensure the result is a json string', function(done) { + var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]}, + {id:"jn2", type:"helper"}]; + helper.load(jsonNode, flow, function() { + var jn1 = helper.getNode("jn1"); + var jn2 = helper.getNode("jn2"); + var count = 0; + jn2.on("input", function(msg) { + try { + should.equal(msg.payload, '{"employees":[{"firstName":"John","lastName":"Smith"}]}'); + count++; + if (count === 2) { + done(); + } + } catch(err) { + done(err); + } + }); + var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; + jn1.receive({payload:obj,topic: "bar"}); + jn1.receive({payload:JSON.stringify(obj),topic: "bar"}); + }); + }); + + it('should ensure the result is a JS Object', function(done) { + var flow = [{id:"jn1",type:"json",action:"obj",wires:[["jn2"]]}, + {id:"jn2", type:"helper"}]; + helper.load(jsonNode, flow, function() { + var jn1 = helper.getNode("jn1"); + var jn2 = helper.getNode("jn2"); + var count = 0; + jn2.on("input", function(msg) { + try { + msg.should.have.property('topic', 'bar'); + msg.payload.should.have.property('employees'); + msg.payload.employees[0].should.have.property('firstName', 'John'); + msg.payload.employees[0].should.have.property('lastName', 'Smith'); + count++; + if (count === 2) { + done(); + } + } catch(err) { + done(err); + } + }); + var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; + jn1.receive({payload:obj,topic: "bar"}); + jn1.receive({payload:JSON.stringify(obj),topic: "bar"}); + }); + }); + + it('should handle any msg property - receive existing string', function(done) { + var flow = [{id:"jn1",type:"json",property:"one.two",wires:[["jn2"]]}, + {id:"jn2", type:"helper"}]; + helper.load(jsonNode, flow, function() { + var jn1 = helper.getNode("jn1"); + var jn2 = helper.getNode("jn2"); + jn2.on("input", function(msg) { + try { + msg.should.have.property('topic', 'bar'); + msg.should.have.property('one'); + msg.one.should.have.property('two'); + msg.one.two.should.have.property('employees'); + msg.one.two.employees[0].should.have.property('firstName', 'John'); + msg.one.two.employees[0].should.have.property('lastName', 'Smith'); + done(); + } catch(err) { + done(err); + } + }); + var jsonString = '{"employees":[{"firstName":"John", "lastName":"Smith"}]}'; + jn1.receive({payload:"",one:{two:jsonString},topic: "bar"}); + + var logEvents = helper.log().args.filter(function(evt) { + return evt[0].type == "json"; + }); + }); + }); + + it('should handle any msg property - receive existing obj', function(done) { + var flow = [{id:"jn1",type:"json",property:"one.two",wires:[["jn2"]]}, + {id:"jn2", type:"helper"}]; + helper.load(jsonNode, flow, function() { + var jn1 = helper.getNode("jn1"); + var jn2 = helper.getNode("jn2"); + jn2.on("input", function(msg) { + try { + should.equal(msg.one.two, '{"employees":[{"firstName":"John","lastName":"Smith"}]}'); + done(); + } catch(err) { + done(err); + } + }); + var jsonString = '{"employees":[{"firstName":"John", "lastName":"Smith"}]}'; + jn1.receive({payload:"",one:{two:JSON.parse(jsonString)},topic: "bar"}); + + var logEvents = helper.log().args.filter(function(evt) { + return evt[0].type == "json"; + }); + }); + }); });