From a61fc5b8366de1f0f3d4c6f4c6a38f13dafee564 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Sat, 29 Nov 2014 09:30:11 +0100 Subject: [PATCH 1/4] Minor corrections to the doc --- doc/Avoiding pitfalls.md | 37 +++++++++++++++++++------------------ doc/Contributing.md | 2 +- doc/Decoding JSON.md | 24 +++++++++++++++--------- doc/Encoding JSON.md | 23 +++++++++++------------ doc/Memory model.md | 2 +- 5 files changed, 47 insertions(+), 41 deletions(-) diff --git a/doc/Avoiding pitfalls.md b/doc/Avoiding pitfalls.md index 28dcfee2..834b9ee8 100644 --- a/doc/Avoiding pitfalls.md +++ b/doc/Avoiding pitfalls.md @@ -1,7 +1,7 @@ Avoiding common pitfalls in Arduino JSON ======================================== -As `StaticJsonBuffer` is the corner stone of this library, you'll see that every pitfall listed here is related to a wrong understanding of the memory model. +As `StaticJsonBuffer` is the corner stone of this library, you'll see that every pitfall listed here are related to a wrong understanding of the memory model. Make sure you read [Arduino JSON memory model](Memory model.md) before going further. @@ -14,29 +14,29 @@ There are basically two reasons why they may fail: 1. the JSON string is invalid, 2. the JSON string contains more values that the buffer can store. -So, if you are sure the JSON string is correct and you still can't parse it, you should slightly increase the number of token of the parser. +So, if you are sure the JSON string is correct and you still can't parse it, you should try to increase the size of the `StaticJsonBuffer`. ## 2. Make sure everything fits in memory You may go into unpredictable trouble if you allocate more memory than your processor really has. -It's a very common issue in embedded development. +It's a very common issue in embedded development. To diagnose this, look at every big objects in you code and sum their size to check that they fit in RAM. For example, don't do this: - char json[1024]; // 1 KB - JsonParser<512> parser; // 514 B + char json[1024]; // 1 KB + StaticJsonBuffer<512> buffer; // 514 B because it may be too big for a processor with only 2 KB: you need free memory to store other variables and the call stack. That is why an 8-bit processor is not able to parse long and complex JSON strings. -## 3. Keep the `StaticJsonBuffer` in memory long enough +## 3. Keep the `StaticJsonBuffer` in memory long enough -Remember that the function of `StaticJsonBuffer` return references. -References don't store data, they are just pointer to the actual. -This will only work if the data actual is still in memory. +Remember that `StaticJsonBuffer`'s function return references. +References don't contain data, they are just pointer to the actual. +So they can only work if the actual data is in memory. For example, don't do this: @@ -48,12 +48,13 @@ For example, don't do this: because the local variable `buffer` will be *removed* from memory when the function `parseArray()` returns, and the `JsonArray&` will point to an invalid location. -## 4. Don't make `StaticJsonBuffer` global +## 4. Don't reuse the same `StaticJsonBuffer` -If you read the previous point, you may come to the idea of using a global variable for your `StaticJsonBuffer`. -This is probably a bad idea because `StaticJsonBuffer` can be quite big (depending on your requirement) and would be eating a lot of memory, even when you don't use it. +During is lifetime a `StaticJsonBuffer` growth until it's discarded. If you try to reuse the same instance several time, it will rapidly get full. -There are some cases were a `StaticJsonBuffer` can be a global variable, but must of the time you should declare it in a local scope, in a function which unique role is to handle the JSON serialization. +For this reason, you should not use a global variable for your `StaticJsonBuffer`. I don't think there is any scenario in which a global `StaticJsonBuffer` would be a valid option. + +The best practice is to declare it in a local scope, so that it's discarded as soon as possible. My advice it to declare it in a function which unique role is to handle the JSON serialization. ## 5. Keep the JSON string in memory long enough @@ -69,21 +70,21 @@ For instance, let's imagine that you parse `["hello","world"]`, like this: const char* first = array[0]; const char* second = array[1]; -In that case, both `first` and `second` are pointer to the content of the original string `json`. +In that case, both `first` and `second` are pointers to the content of the original string `json`. So this will only work if `json` is still in memory. ## 6. JSON string is altered -If you read carefully the previous pitfall, you may I come to the conclusion that the JSON parser modifies the JSON string. +If you read carefully the previous section, you may have come to the conclusion that the JSON parser modifies the JSON string. Indeed, the parser modifies the string for two reasons: 1. it inserts `\0` to terminate substrings, 2. it translate escaped characters like `\n` or `\t`. -Most of the time this wont be an issue, but it there are some corner case that can be problematic. +Most of the time this wont be an issue, but there are some corner cases that can be problematic. -Let take the example above: +Let take the example bellow: char[] json = "[\"hello\",\"world\"]"; StaticJsonBuffer<32> buffer; @@ -97,5 +98,5 @@ If you replace it by: Depending on your platform, you may have an exception because the parser tries to write at a location that is read-only. In the first case `char json[]` declares an array of `char` initialized to the specified string. -In the second case `char* json` declares a pointer to a read only string, in fact it should be a `const char*` instead of a `char*`. +In the second case `char* json` declares a pointer to a read-only string, in fact it should be a `const char*` instead of a `char*`. diff --git a/doc/Contributing.md b/doc/Contributing.md index 95886302..acdd00c6 100644 --- a/doc/Contributing.md +++ b/doc/Contributing.md @@ -7,7 +7,7 @@ If you want to contribute to the project, please: 2. Follow the coding conventions 3. Write tests -About the coding conventions: I try to follow the [Google C++ Style Guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.html) which few variations to match the Arduino conventions. +About the coding conventions: I try to follow the [Google C++ Style Guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.html) with few variations to match the Arduino conventions. I use [ClangFormat](http://clang.llvm.org/docs/ClangFormat.html) to format the code for me. I use [CppLint](http://google-styleguide.googlecode.com/svn/trunk/cpplint/cpplint.py) to detect non-compliant stuff. diff --git a/doc/Decoding JSON.md b/doc/Decoding JSON.md index cbd560ed..57417479 100644 --- a/doc/Decoding JSON.md +++ b/doc/Decoding JSON.md @@ -1,6 +1,12 @@ Decoding JSON with Arduino JSON =============================== +Before writing any code, don't forget to include the header: + + #include + +For instructions on how to install the library, please read [Using the library with Arduino](Using the library with Arduino.md) or [Using the library without Arduino](Using the library without Arduino.md). + ## Example Here an example that parse the string `{"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}`: @@ -17,7 +23,7 @@ Here an example that parse the string `{"sensor":"gps","time":1351824120,"data": // JsonObject& root = jsonBuffer.parseObject(json); - if (!root.success()) + if (!root.success()) { Serial.println("parseObject() failed"); return; @@ -39,14 +45,14 @@ Before continuing please read the page [Arduino JSON memory model](Memory model. ## Step 2: Parse the JSON string -You call the JSON parser through the instance of `StaticJsonBuffer`. -It exposes two function for parsing JSON: +You invoke the JSON parser through the instance of `StaticJsonBuffer`. +It exposes two functions for parsing JSON: -1. parseArray() that returns a reference to a `JsonArray` -2. parseObject() that returns a reference to a `JsonObject` +1. `parseArray()` that returns a reference to a `JsonArray` +2. `parseObject()` that returns a reference to a `JsonObject` Let's see an example. -Say we want to parse `{"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}`, it's an object so we call `parseObject` as follows: +Say we want to parse `{"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}`, it's an object so we call `parseObject()` as follows: char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; @@ -90,7 +96,7 @@ The simplest way is to use the subscript operator of `JsonObject`: const char* sensor = root["sensor"]; long time = root["time"]; - + You can chain the subscript operator if you have nested arrays or objects: double latitude = root["data"][0]; @@ -121,14 +127,14 @@ If the actual value doesn't match the target type, a default value will be retur If you want to know if some value is present, call `containsKey()`: - if (root.contains("extra")) + if (root.contains("extra")) { // root["extra"] is valid } If you want to check the type value has a certain type, call `is()`: - if (root["extra"].is()) + if (root["extra"].is()) { // root["extra"] is an array } diff --git a/doc/Encoding JSON.md b/doc/Encoding JSON.md index e6cf9a6f..a45fcbe2 100644 --- a/doc/Encoding JSON.md +++ b/doc/Encoding JSON.md @@ -5,7 +5,7 @@ Before writing any code, don't forget to include the header: #include -If your not using the Arduino IDE, please read [Using the library without Arduino](Using the library without Arduino.md). +For instructions on how to install the library, please read [Using the library with Arduino](Using the library with Arduino.md) or [Using the library without Arduino](Using the library without Arduino.md). ## Example @@ -50,7 +50,7 @@ You create an array like this: Don't forget the `&` after `JsonArray`, it needs to be a reference to the array. -Then you can add strings, integer, booleans, etc: +Then you can add strings, integer, booleans, etc: array.add("bazinga!"); array.add(42); @@ -58,14 +58,13 @@ Then you can add strings, integer, booleans, etc: There are two syntaxes for floating point values: - array.add<4>(3.1415); // 4 digits: "3.1415" + array.add(3.1415, 4); // 4 digits: "3.1415" array.add(3.1415); // 2 digits: "3.14" > ##### About floating point precision -> The overload of `add()` with 2 parameters allows you to specify the number of decimals to save in the JSON string. -> When you use the overload with one parameter, you use the default number of decimals which is two. -> Note that this behavior is the exact same as Arduino's `Print::print(double,int);` which is implemented by `Serial`. -> So you may already be familiar with it. +> The overload of `add()` with 2 parameters allows you to specify the number of decimals to save in the JSON string. +> When you use the overload with one parameter, you use the default number of decimals which is 2. +> Note that this behavior is the exact same as Arduino's `Print::print(double,int);` which is implemented by `Serial`, so you may already be familiar with this behavior. You can add a nested array or object if you have a reference to it. Or simpler, you can create nested array or nested objects from the array: @@ -75,13 +74,13 @@ Or simpler, you can create nested array or nested objects from the array: #### Objects -You create an array like this: +You create an object like this: JsonObject& object = jsonBuffer.createObject(); Again, don't forget the `&` after `JsonObject`, it needs to be a reference to the object. -Then you can add strings, integer, booleans, etc: +Then you can add strings, integer, booleans, etc: object["key1"] = "bazinga!"; object["key2"] = 42; @@ -89,7 +88,7 @@ Then you can add strings, integer, booleans, etc: As for the arrays, there are two syntaxes for the floating point values: - object["key4"].set<4>(3.1415); // 4 digits "3.1415" + object["key4"].set(3.1415, 4); // 4 digits "3.1415" object["key5"] = 3.1415; // default: 2 digits "3.14" You can add a nested array or object if you have a reference to it. @@ -136,6 +135,6 @@ And, of course if you need an indented JSON string: array.prettyPrintTo(Serial); > ##### About the Print interface -> The library is designed to send the JSON string to an implementation of the `Print` interface that is part of Arduino. -> In the example above we used `Serial`, but they are many other implementation that would work as well, including: `HardwareSerial`, `SoftwareSerial`, `LiquidCrystal`, `EthernetClient`, `WiFiClient`, `Wire`... +> The library is designed to send the JSON string to an implementation of the `Print` interface that is part of Arduino. +> In the example above we used `Serial`, but they are many other implementations that would work as well, including: `HardwareSerial`, `SoftwareSerial`, `LiquidCrystal`, `EthernetClient`, `WiFiClient`, `Wire`... > When you use this library out of the Arduino environment, it will use it's own implementation of `Print` and everything will be the same. \ No newline at end of file diff --git a/doc/Memory model.md b/doc/Memory model.md index eefb85ee..8cfff483 100644 --- a/doc/Memory model.md +++ b/doc/Memory model.md @@ -17,7 +17,7 @@ The bigger the buffer is, the more complex the object tree can be, but also the ### How to determine the buffer size? -So the big question you should have in mind right now is *How can I determine the size?*. +So the big question you should have in mind right now is *How can I determine the size?*. There are basically two approaches here: From 02f6fab02561a639356b9cd1057ab6d2c49b391b Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Sat, 29 Nov 2014 10:36:15 +0100 Subject: [PATCH 2/4] Minor changes in the docs --- doc/Memory model.md | 16 +++++++--------- doc/Migrating code to new API.md | 27 +++++++++------------------ 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/doc/Memory model.md b/doc/Memory model.md index 8cfff483..45881744 100644 --- a/doc/Memory model.md +++ b/doc/Memory model.md @@ -5,11 +5,11 @@ Arduino JSON memory model ### Introducing `StaticJsonBuffer` -Arduino JSON uses a preallocated memory pool to store the object tree, this is done by the `StaticJsonBuffer`. +Arduino JSON uses a preallocated memory pool to store the object tree, this is done by the `StaticJsonBuffer` class. Before using any function of the library you need to create a `StaticJsonBuffer`. Then you can use this instance to create arrays and objects, or parse a JSON string. -`StaticJsonBuffer` has a template parameter that determines the number of bytes that it contains. For example, the following line create a `StaticJsonBuffer` containing 200 bytes on the stack: +`StaticJsonBuffer` has a template parameter that determines its capacity. For example, the following line create a `StaticJsonBuffer` with a capacity of 200 bytes: StaticJsonBuffer<200> jsonBuffer; @@ -28,27 +28,25 @@ In the first case, you know some constraints on the object tree. For instance, l {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} -To determine the memory usage of this object tree, you use the two macros `JSON_ARRAY_SIZE(n)` `JSON_OBJECT_SIZE(n)`, both take the number of elements as a parameter. +To determine the memory usage of this object tree, you use the two macros `JSON_ARRAY_SIZE(n)` and `JSON_OBJECT_SIZE(n)`, both take the number of elements as an argument. For the example above, it would be: const int BUFFER_SIZE = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2); StaticJsonBuffer jsonBuffer; -In the second case, let's say you dynamically generate a JSON object tree of a random complexity so you can't put a limit base on that. But on the other hand, you don't want your program to crash because the object tree doesn't fit in memory. -The solution here is to determine how much memory is available, or in other words how much memory you can afford for the JSON generation. +In the second case, let's say you dynamically generate a JSON object tree of a random complexity so you can't put a limit based on that. But on the other hand, you don't want your program to crash because the object tree doesn't fit in memory. +The solution here is to determine how much memory is available, or in other words how much memory you can afford for the JSON object tree. ### Why choosing fixed allocation? -This fixed allocation approach may seem a bit strange, especially if your a desktop app developer used to dynamic allocation, but it make a lot of sense in an embedded context: +This fixed allocation approach may seem a bit strange, especially if your a desktop application developer used to dynamic allocation, but it make a lot of sense in an embedded context: 1. the code is smaller 2. it uses less memory 3. it doesn't create memory fragmentation 4. it predictable -Don't forget that, the memory is "freed" as soon as the `StaticJsonBuffer` is out of scope, like any other variable. It only hold the memory for a short amount of time. - -For that reason, you should never use a `StaticJsonBuffer` as a **global variable** because it would hold a lot of memory for the whole life of the program. +Don't forget that the memory is "freed" as soon as the `StaticJsonBuffer` is out of scope, like any other variable. It only hold the memory for a short amount of time. ## Memory usage diff --git a/doc/Migrating code to new API.md b/doc/Migrating code to new API.md index 71a96c48..f8e2721b 100644 --- a/doc/Migrating code to new API.md +++ b/doc/Migrating code to new API.md @@ -1,7 +1,7 @@ Migrating code written for Arduino JSON v3 to v4 ================================================ -Arduino JSON v4 was a major rewrite of the library, and the API change significantly. +Arduino JSON v4 was a major rewrite of the library, and the API changed significantly. ## Includes @@ -14,12 +14,6 @@ Arduino JSON v4 only has one: #include -Node: the header `src/ArduinoJson.h` is intended to be used within the Arduino IDE, if you're in another environment, you may need to include the following headers: - - #include - #include - #include - ## Namespaces Arduino JSON v3 had two namespaces: @@ -27,15 +21,12 @@ Arduino JSON v3 had two namespaces: using namespace ArduinoJson::Parser; using namespace ArduinoJson::Generator; -Arduino JSON v4 only has one: - - using namespace ArduinoJson; - -If you include the header `ArduinoJson.h` (recommended if in Arduino IDE), the `using` directivei is already done for you, so you don't have to write it. +Arduino JSON v4 doesn't require the `using namespace` statement. +It has a namespace but the `using namespace` is done in the header file. ## StaticJsonBuffer -Arduino JSON v3 had different memory allocation models for parser: +Arduino JSON v3 had different memory allocation models for the parser: JsonParser<16> parser; // 16 being the capacity in "tokens" @@ -44,18 +35,18 @@ and for the generator: JsonArray<4> array; // 4 being the number of element JsonObject<4> object; -Arduino JSON v4 only has one memory allocation mode: +Arduino JSON v4 only has one memory allocation model: StaticJsonBuffer<128> buffer; // 128 being the capacity in bytes ## Return values for the parser -Arduino JSON v3 returned `JsonArray` and `JsonObject`: +Arduino JSON v3 returned value types: JsonArray array = parser.parseArray(json); JsonObject object = parser.parseObject(json); -Arduino JSON v4 returns references: +Arduino JSON v4 returns references types: JsonArray& array = buffer.parseArray(json); JsonObject& object = buffer.parseObject(json); @@ -78,11 +69,11 @@ Note: you don't have to specify the capacity anymore. ## Printable interface -Arduino JSON v3 used to implement the Printable interface, that allowed that kind of statement: +Arduino JSON v3 used to implement the Printable interface, which allowed statements like: Serial.print(array); -Arduino JSON v4 doesn't, so you need to write this: +But Arduino JSON v4 doesn't, instead you need to write this: array.printTo(Serial); From 79f6f0dd86c95a083defe3bc4e477b49838b3496 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Sat, 29 Nov 2014 13:53:18 +0100 Subject: [PATCH 3/4] Minor changes in doc --- doc/Migrating code to new API.md | 2 +- doc/Using the library with Arduino.md | 7 +++---- doc/Using the library without Arduino.md | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/doc/Migrating code to new API.md b/doc/Migrating code to new API.md index f8e2721b..24471479 100644 --- a/doc/Migrating code to new API.md +++ b/doc/Migrating code to new API.md @@ -77,4 +77,4 @@ But Arduino JSON v4 doesn't, instead you need to write this: array.printTo(Serial); -Note: there is a good reason for that: reducing the size of `JsonArray` and `JsonObject`. \ No newline at end of file +Note: there was a good reason for removing that feature, and it's reducing the size of `JsonArray` and `JsonObject`. \ No newline at end of file diff --git a/doc/Using the library with Arduino.md b/doc/Using the library with Arduino.md index 31efebc4..5530289a 100644 --- a/doc/Using the library with Arduino.md +++ b/doc/Using the library with Arduino.md @@ -1,7 +1,7 @@ -Using the library without Arduino -================================= +Using the library with Arduino +============================== -This library is primarily design to be used with the Arduino IDE and therefore has a simplified setup procedure for that purpose. +This library is primarily design to be used with the Arduino IDE and therefore has a simplified setup procedure for that environment. If you don't use the Arduino IDE, please read [Using the library without Arduino](Using the library without Arduino.md). ## Install the library @@ -18,7 +18,6 @@ Click `File` / `Example` / `ArduinoJson`. ![Screen capture of Arduino IDE](http://i.imgur.com/g5UwkVh.png) - ## Use the library in your sketches Just add the following line at the top of your program: diff --git a/doc/Using the library without Arduino.md b/doc/Using the library without Arduino.md index 3fbb4043..05c307a9 100644 --- a/doc/Using the library without Arduino.md +++ b/doc/Using the library without Arduino.md @@ -1,7 +1,7 @@ Using the library without Arduino ================================= -This library is primarily design to be used with the Arduino IDE and therefore has a simplified setup procedure for that purpose. +This library is primarily design to be used with the Arduino IDE and therefore has a simplified setup procedure for that environment. If you use the Arduino IDE, please read [Using the library with Arduino](Using the library with Arduino.md). However, it can be used without Arduino IDE with very little effort. From 4e3f554b68d3b6c8669000efce5cf051b65e36ef Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Sat, 29 Nov 2014 14:03:32 +0100 Subject: [PATCH 4/4] Made script executable on unix --- scripts/build-arduino-package.sh | 0 scripts/format-code.sh | 0 scripts/run-tests.sh | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/build-arduino-package.sh mode change 100644 => 100755 scripts/format-code.sh mode change 100644 => 100755 scripts/run-tests.sh diff --git a/scripts/build-arduino-package.sh b/scripts/build-arduino-package.sh old mode 100644 new mode 100755 diff --git a/scripts/format-code.sh b/scripts/format-code.sh old mode 100644 new mode 100755 diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh old mode 100644 new mode 100755