Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass key in custom converter function -- updated #16

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 38 additions & 38 deletions JSONParser.class.nut
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* @author Mikhail Yurasov <[email protected]>
* @package JSONParser
* @version 1.0.2
* @version 2.0.0
*/

/**
Expand All @@ -13,7 +13,7 @@
class JSONParser {

// should be the same for all components within JSONParser package
static version = "1.0.2";
static version = "2.0.0";

/**
* Parse JSON string into data structure
Expand Down Expand Up @@ -44,15 +44,15 @@ class JSONParser {
state = "colon";
},
ovalue = function () {
value = this._convert(value, "string", converter);
value = this._convert(value, "string", converter, key);
state = "ocomma";
}.bindenv(this),
firstavalue = function () {
value = this._convert(value, "string", converter);
value = this._convert(value, "string", converter, key);
state = "acomma";
}.bindenv(this),
avalue = function () {
value = this._convert(value, "string", converter);
value = this._convert(value, "string", converter, key);
state = "acomma";
}.bindenv(this)
};
Expand All @@ -63,15 +63,15 @@ class JSONParser {
state = "ok";
},
ovalue = function () {
value = this._convert(value, "number", converter);
value = this._convert(value, "number", converter, key);
state = "ocomma";
}.bindenv(this),
firstavalue = function () {
value = this._convert(value, "number", converter);
value = this._convert(value, "number", converter, key);
state = "acomma";
}.bindenv(this),
avalue = function () {
value = this._convert(value, "number", converter);
value = this._convert(value, "number", converter, key);
state = "acomma";
}.bindenv(this)
};
Expand Down Expand Up @@ -254,7 +254,7 @@ class JSONParser {
// current tokenizeing position
local start = 0;
local lastToken = null;

try {

local
Expand All @@ -264,7 +264,7 @@ class JSONParser {

while (token = tokenizer.nextToken(str, start)) {
lastToken = token;

if ("ptfn" == token.type) {
// punctuation/true/false/null
action[token.value][state]();
Expand Down Expand Up @@ -295,9 +295,9 @@ class JSONParser {

// if this is a standalone string or number, convert it
if (lastToken.type == "string" || lastToken.type == "number") {
return this._convert(lastToken.value, lastToken.type, converter)
return this._convert(lastToken.value, lastToken.type, converter, null)
}

return value;
}

Expand All @@ -309,33 +309,33 @@ class JSONParser {
* @param {string} type
* @param {function|null} converter
*/
function _convert(value, type, converter) {
if ("function" == typeof converter) {

// # of params for converter function

local parametercCount = 2;

// .getinfos() is missing on ei platform
if ("getinfos" in converter) {
parametercCount = converter.getinfos().parameters.len()
- 1 /* "this" is also included */;
}

if (parametercCount == 1) {
return converter(value);
} else if (parametercCount == 2) {
return converter(value, type);
} else {
throw "Error: converter function must take 1 or 2 parameters"
}

} else if ("number" == type) {
return (value.find(".") == null && value.find("e") == null && value.find("E") == null) ? value.tointeger() : value.tofloat();
} else {
return value;
function _convert(value, type, converter, key) {
if ("function" == typeof converter) {
// # of params for converter function
local parametercCount = 3;

// .getinfos() is missing on ei platform
if ("getinfos" in converter) {
parametercCount = converter.getinfos().parameters.len()
- 1 /* "this" is also included */;
}

if (parametercCount == 1) {
return converter(value);
} else if (parametercCount == 2) {
return converter(value, type);
} else if(parametercCount == 3) {
return converter(value, type, key);
} else {
throw "Error: converter function must take 1, 2, or 3 parameters"
}

} else if ("number" == type) {
return (value.find(".") == null && value.find("e") == null && value.find("E") == null) ? value.tointeger() : value.tofloat();
} else {
return value;
}
}
}
}

/**
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Squirrel JSON Parser 1.0.2 #
# Squirrel JSON Parser 2.0.0 #

This library parses JSON into Squirrel data types.

**To include this library in your project, add** `#require "JSONParser.class.nut:1.0.2"` **at the top of your code.**
**To include this library in your project, add** `#require "JSONParser.class.nut:2.0.0"` **at the top of your code.**

![Build Status](https://cse-ci.electricimp.com/app/rest/builds/buildType:(id:JSONParser_BuildAndTest)/statusIcon)

## Usage ##

JSONParser has no constructor and one public function, *parse()*.

### parse(*jsonString[, converter]*)
### parse(*jsonString[, converter]*) ###

This method converts the supplied JSON to a table.

Expand All @@ -27,6 +27,7 @@ An optional converter function can be passed into *parse()* to de-serialize cust

- *value* &mdash; String representation of a value.
- *type* &mdash; String indicating conversion type: `"string"` or `"number"`.
- *key* &mdash; If the value is in a table, this is the value's key.

For example, the following code converts all numbers to floats and makes strings uppercase:

Expand Down Expand Up @@ -78,7 +79,7 @@ s <- JSONEncoder.encode(o);
server.log(s);
// Displays '{"a":1,"c":"@mycustomtype:100500","b":"Something"}'

result <- JSONParser.parse(s, function (val, type) {
result <- JSONParser.parse(s, function (val, type, key) {
if ("number" == type) {
return val.tofloat();
} else if ("string" == type) {
Expand Down
89 changes: 89 additions & 0 deletions tests/badData.agent.test.nut
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Check for failure when passed bad JSON encoded string
*/
class badData extends ImpTestCase {

function testBadBasic() {
// local data = {
// "a" : 1,
// "b" : 2,
// "c" : 3,
// };
// this.info(JSONEncoder.encode(data))

// delete a comma
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":1\"c\":3,\"b\":2}"]);

// delete a curly bracket
this.assertThrowsError(JSONParser.parse, JSONParser, ["\"a\":1,\"c\":3,\"b\":2}"]);

// delete the value
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":1,\"c\":,\"b\":2}"]);

// delete a colon
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\"1,\"c\":3,\"b\":2}"]);

// delete the other bracket
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":1,\"c\":3,\"b\":2"]);

// delete all the quotes
this.assertThrowsError(JSONParser.parse, JSONParser, ["{a:1,c:3,b:2}"]);
}

function testBadArray() {
// local data1 = {
// a = [1,2,3],
// b = ["A", "B", "C"],
// c = [1.1, 2.2, 3.3]
// };
// this.info(JSONEncoder.encode(data1))

// delete a bracket
this.assertThrowsError(JSONParser.parse, JSONParser, ["\"a\":[1,2,3],\"c\":[1.1,2.2,3.3],\"b\":[\"A\",\"B\",\"C\"]}"]);
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":[1,2,3],\"c\":[1.1,2.2,3.3],\"b\":[\"A\",\"B\",\"C\"]"]);

// delete a square bracket
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":[1,2,3],\"c\":[1.1,2.2,3.3],\"b\":[\"A\",\"B\",\"C\"}"]);
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":[1,2,3],\"c\":1.1,2.2,3.3],\"b\":[\"A\",\"B\",\"C\"]}"]);
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":[1,2,3,\"c\":[1.1,2.2,3.3],\"b\":[\"A\",\"B\",\"C\"]}"]);

// destroy colons
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\"[1,2,3],\"c\"[1.1,2.2,3.3],\"b\"[\"A\",\"B\",\"C\"]}"]);


// local data2 = [1, 2, "red", "blue", {"a": "table"}];
// this.info(JSONEncoder.encode(data2))
// "[1,2,\"red\",\"blue\",{\"a\":\"table\"}]"

this.assertThrowsError(JSONParser.parse, JSONParser, ["1,2,\"red\",\"blue\",{\"a\":\"table\"}]"]);
this.assertThrowsError(JSONParser.parse, JSONParser, ["[1,2,\"red\",\"blue\",{\"a\":\"table\"}"]);
this.assertThrowsError(JSONParser.parse, JSONParser, ["[1,2,\"red\",\"blue\",{\"a\":\"table\"]"]);
this.assertThrowsError(JSONParser.parse, JSONParser, ["[1,2,\"red\",\"blue\",\"a\":\"table\"}]"]);
this.assertThrowsError(JSONParser.parse, JSONParser, ["[1,2,\"red\",\"blue\",{\"a\"\"table\"}]"]);
this.assertThrowsError(JSONParser.parse, JSONParser, ["[1,2\"red\"\"blue\"{\"a\":\"table\"}]"]);
}

function testBadTable() {
// local data = {
// "a" : [1,2,3],
// "b" : "Hello world!"
// "c" : 1598.677
// };
// this.info(JSONEncoder.encode(data))

// remove commas
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":[1,2,3],\"c\":1598.68\"b\":\"Hello world!\"}"])
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":[1,2,3]\"c\":1598.68,\"b\":\"Hello world!\"}"])

// remove brackets
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":[1,2,3],\"c\":1598.68,\"b\":\"Hello world!\""])
this.assertThrowsError(JSONParser.parse, JSONParser, ["\"a\":[1,2,3],\"c\":1598.68,\"b\":\"Hello world!\"}"])

// and quotation marks
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":[1,2,3],\"c:1598.68,\"b\":\"Hello world!\"}"])
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":[1,2,3],\"c\":1598.68,\"b\":\"Hello world!}"])

// whole chunk of data missing
this.assertThrowsError(JSONParser.parse, JSONParser, ["{\"a\":,\"c\":1598.68,\"b\":\"Hello world!\"}"])
}
}
89 changes: 88 additions & 1 deletion tests/converter.agent.test.nut
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Custom_Converter_TestCase extends ImpTestCase {
local s = "{\"a\":1,\"c\":\"@mycustomtype:abc\",\"b\":2}";

// use custom conveter to revreate MyCustomType
local d = JSONParser.parse(s, function (val, type) {
local d = JSONParser.parse(s, function (val, type, key) {
if ("number" == type) {
return val.tofloat();
} else if ("string" == type) {
Expand All @@ -42,4 +42,91 @@ class Custom_Converter_TestCase extends ImpTestCase {
this.assertTrue(d.c instanceof MyCustomType);
this.assertTrue(d.c.getValue() == "abc");
}

function test_2() {
local s = "\"Hello world!\"";
local d = JSONParser.parse(s, function(value, type, key){
if (type == "string") {
return value;
} else if (type == "number") {
throw "JSONParse passed wrong type!"
} else {
throw "JSONParse passed invalid type!"
}
});

this.assertDeepEqual("Hello world!", d)
}

function test_3() {
// Arrays with custom parsing
local s = "[1,2,3]";
local d = JSONParser.parse(s, function(value, type, key){
if (type == "string") {
throw "JSONParse passed wrong type!"
} else if (type == "number") {
return value.tointeger();
} else {
throw "JSONParse passed invalid type!"
}
});

this.assertDeepEqual([1,2,3], d)
}

function test_4() {
// Integers with custom parsing
local s = "77";
local d = JSONParser.parse(s, function(value, type, key){
if (type == "string") {
throw "JSONParse passed wrong type!"
} else if (type == "number") {
return value.tointeger();
} else {
throw "JSONParse passed invalid type!"
}
});

this.assertDeepEqual(77, d)
}

function test_5() {
local s = "77.8";
local d = JSONParser.parse(s, function(value, type, key){
if (type == "string") {
throw "JSONParse passed wrong type!"
} else if (type == "number") {
return value.tofloat();
} else {
throw "JSONParse passed invalid type!"
}
});

this.assertDeepEqual(77.8, d)
}

function test_6() {
local s = "{\"a\":\"Hello world!\",\"c\":1.667,\"b\":1024}"
local d = JSONParser.parse(s, function(value, type, key){
if (type == "string" && key == "a") {
return value;
} else if (type == "number") {
if (key == "c") {
return value.tofloat();
} else if (key == "b") {
return value.tointeger();
} else {
throw "JSONParse passed bad key!"
}
} else {
throw "JSONParse key testing failed!"
}
});

this.assertDeepEqual({
a = "Hello world!",
b = 1024,
c = 1.667
}, d)
}
}
20 changes: 19 additions & 1 deletion tests/parse.agent.test.nut
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,24 @@ class Parsing_TestCase extends ImpTestCase {
function test_5() {
local s = "77";
local d = JSONParser.parse(s);
this.assertDeepEqual(d, 77);
this.assertDeepEqual(77, d);
}

function test_6() {
local s = "\"Hello world!\"";
local d = JSONParser.parse(s)
this.assertDeepEqual("Hello world!", d)
}

function test_7() {
local s = "77.8";
local d = JSONParser.parse(s);
this.assertDeepEqual(77.8, d);
}

function test_8() {
local s = "[1,2,3]";
local d = JSONParser.parse(s);
this.assertDeepEqual([1,2,3], d);
}
}