diff --git a/README.md b/README.md index 0496564d..fcd0763d 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,6 @@ Quick start double latitude = root["data"][0]; double longitude = root["data"][1]; -[See complete guide](/doc/Decoding JSON.md) - #### Encoding / Generating StaticJsonBuffer<200> jsonBuffer; @@ -53,7 +51,11 @@ Quick start // This prints: // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} -[See complete guide](/doc/Encoding JSON.md) + +Documentation +------------- + +The documentation is available online in the [Arduino JSON wiki](https://github.com/bblanchon/ArduinoJson/wiki) Testimonials ------------ diff --git a/doc/Avoiding pitfalls.md b/doc/Avoiding pitfalls.md deleted file mode 100644 index 834b9ee8..00000000 --- a/doc/Avoiding pitfalls.md +++ /dev/null @@ -1,102 +0,0 @@ -Avoiding common pitfalls in Arduino JSON -======================================== - -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. - -## 1. Make `StaticJsonBuffer` big enough - -By design, the library has no way to tell you why `parseArray()` or `parseObject()` failed. - -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 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. - -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 - 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 - -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: - - JsonArray& getArray(char* json) - { - StaticJsonBuffer<200> buffer; - return buffer.parseArray(json); - } - -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 reuse the same `StaticJsonBuffer` - -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. - -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 - -The library never make memory duplication. -This has an important implication on string values, it means that the library will return pointer to chunks of the string. - -For instance, let's imagine that you parse `["hello","world"]`, like this: - - char[] json = "[\"hello\",\"world\"]"; - StaticJsonBuffer<32> buffer; - JsonArray& array = buffer.parseArray(json); - - const char* first = array[0]; - const char* second = array[1]; - -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 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 there are some corner cases that can be problematic. - -Let take the example bellow: - - char[] json = "[\"hello\",\"world\"]"; - StaticJsonBuffer<32> buffer; - JsonArray& array = buffer.parseArray(json); - -If you replace it by: - - char* json = "[\"hello\",\"world\"]"; - StaticJsonBuffer<32> buffer; - JsonArray& array = buffer.parseArray(json); - -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*`. - diff --git a/doc/Contributing.md b/doc/Contributing.md deleted file mode 100644 index acdd00c6..00000000 --- a/doc/Contributing.md +++ /dev/null @@ -1,15 +0,0 @@ -Contributing to Arduino JSON -============================ - -If you want to contribute to the project, please: - -1. Use GitHub pull request feature -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) 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. - -You should have a look at the `scripts/` folder as it contains a few helpers scripts. \ No newline at end of file diff --git a/doc/Decoding JSON.md b/doc/Decoding JSON.md deleted file mode 100644 index 57417479..00000000 --- a/doc/Decoding JSON.md +++ /dev/null @@ -1,148 +0,0 @@ -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]}`: - - char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; - - // - // Step 1: Reserve memory space - // - StaticJsonBuffer<200> jsonBuffer; - - // - // Step 2: Deserialize the JSON string - // - JsonObject& root = jsonBuffer.parseObject(json); - - if (!root.success()) - { - Serial.println("parseObject() failed"); - return; - } - - // - // Step 3: Retrieve the values - // - const char* sensor = root["sensor"]; - long time = root["time"]; - double latitude = root["data"][0]; - double longitude = root["data"][1]; - -## Step 1: Reserve memory space - -Arduino JSON uses a preallocated memory pool to store the object tree, this is done by the `StaticJsonBuffer`. - -Before continuing please read the page [Arduino JSON memory model](Memory model.md) that explains everything you need to know about `StaticJsonBuffer`. - -## Step 2: Parse the JSON string - -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` - -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: - - char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; - - JsonObject& root = jsonBuffer.parseObject(json); - -As you can see `parseObject()` takes a `char*` as a parameter. -Be careful, it's not a `const char*`, the memory must be writable. -Indeed, the parser will modify the string in two cases: - -1. to insert string endings (character `\0`), -2. to translate escaped characters (like `\n` or `\t`). - -Another thing that you must keep in mind is that the string (`char json[]` in the example above) must stay in memory during the whole parsing process. -That is because the in memory object tree will store pointer to chunks of the string, so as to avoid any memory duplication. - -Now, to check if the parsing was successful, you can call `JsonObject::success()`: - - if (!root.success()) - { - // Parsing fail - } - -The result can be `false` for tree reasons: - -1. the JSON string is invalid, -2. the JSON string doesn't represent an object, -3. the `StaticJsonBuffer` is too small. - -We just saw how to parse an object, there is nothing more to say for arrays, the procedure is exactly the same. - -## Step 3: Retrieve the values - -Now that the object or array is in memory, you can extract the data very easily. - -In this section, we'll see how to do it with a `JsonObject`. -Once again, there is nothing more to say about arrays, `JsonArray` works exactly the same as `JsonObject`. - -#### Subscript operator - -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]; - double longitude = root["data"][1]; - -But alternatively, you can get a reference to the nested array: - - JsonArray& nestedArray = root["data"]; - -#### Casting values - -In the previous examples, the values were implicitly casted to the target type. -You can also do this explicitly - - const char* sensor = root["sensor"].asString(); - long time = root["time"].as(); - JsonArray& nestedArray = root["data"].asArray(); - -If the actual value doesn't match the target type, a default value will be return: - -1. `false` for boolean values -2. `0` for integer values -3. `NULL` for string values -4. `JsonArray::invalid()` for nested arrays -5. `JsonObject::invalid()` for nested object - -#### Check values - -If you want to know if some value is present, call `containsKey()`: - - 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()) - { - // root["extra"] is an array - } - -You can also iterate through the key-value pairs of the object: - - for (JsonObject::itertor it=root.begin(); it!=root.end(); ++it) - { - Serial.println(it->key); - Serial.println(i->value.asString()); - } diff --git a/doc/Encoding JSON.md b/doc/Encoding JSON.md deleted file mode 100644 index a45fcbe2..00000000 --- a/doc/Encoding JSON.md +++ /dev/null @@ -1,140 +0,0 @@ -Encoding 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 is an example to generate `{"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}` - - // - // Step 1: Reserve memory space - // - StaticJsonBuffer<200> jsonBuffer; - - // - // Step 2: Build object tree in memory - // - JsonObject& root = jsonBuffer.createObject(); - root["sensor"] = "gps"; - root["time"] = 1351824120; - - JsonArray& data = root.createNestedArray("data"); - data.add(48.756080, 6); // 6 is the number of decimals to print - data.add(2.302038, 6); // if not specified, 2 digits are printed - - // - // Step 3: Generate the JSON string - // - root.printTo(Serial); - -## Step 1: Reserve memory space - -Arduino JSON uses a preallocated memory pool to store the object tree, this is done by the `StaticJsonBuffer`. - -Before continuing please read the page [Arduino JSON memory model](Memory model.md) that explains everything you need to know about `StaticJsonBuffer`. - -## Step 2: Build object tree in memory - -Now that you have enough memory hold by the `StaticJsonBuffer`, you can use it to build your in-memory representation of the JSON string. - -#### Arrays - -You create an array like this: - - JsonArray& array = jsonBuffer.createArray(); - -Don't forget the `&` after `JsonArray`, it needs to be a reference to the array. - -Then you can add strings, integer, booleans, etc: - - array.add("bazinga!"); - array.add(42); - array.add(true); - -There are two syntaxes for floating point values: - - 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 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: - - JsonArray& nestedArray = array.createNestedArray(); - JsonObject& nestedObject = array.createNestedObject(); - -#### Objects - -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: - - object["key1"] = "bazinga!"; - object["key2"] = 42; - object["key3"] = true; - -As for the arrays, there are two syntaxes for the floating point values: - - 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. -Or simpler, you can create nested array or nested objects from the object: - - JsonArray& nestedArray = object.createNestedArray("key6"); - JsonObject& nestedObject = object.createNestedObject("key7"); - -> ##### Other JsonObject functions -> * `object.add(key, value)` is a synonym for `object[key] = value` -> * `object.containsKey(key)` returns `true` is the `key` is present in `object` -> * `object.remove(key)` removes the `value` associated with `key` - -## Step 3: Generate the JSON string - -There are two ways tho get the resulting JSON string. - -Depending on your project, you may need to dump the string in a classic `char[]` or send it to a `Print` implementation like `Serial` or `EthernetClient `. - -Both ways are the easy way :-) - -#### Use a classic `char[]` - -Whether you have a `JsonArray&` or a `JsonObject&`, simply call `printTo()` with the destination buffer, like so: - - char buffer[256]; - array.printTo(buffer, sizeof(buffer)); - -> ##### Want an indented output? -> By default the generated JSON is as small as possible. It contains no extra space, nor line break. -> But if you want an indented, more readable output, you can. -> Simply call `prettyPrintTo` instead of `printTo()`: -> -> array.prettyPrintTo(buffer, sizeof(buffer)); - -#### Send to a `Print` implementation - -It's very likely that the generated JSON will end up in a stream like `Serial` or `EthernetClient `, so you can save some time and memory by doing this: - - array.printTo(Serial); - -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 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 deleted file mode 100644 index 45881744..00000000 --- a/doc/Memory model.md +++ /dev/null @@ -1,58 +0,0 @@ -Arduino JSON memory model -========================= - -## Fixed memory allocation - -### Introducing `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 its capacity. For example, the following line create a `StaticJsonBuffer` with a capacity of 200 bytes: - - StaticJsonBuffer<200> jsonBuffer; - -The bigger the buffer is, the more complex the object tree can be, but also the more memory you need. - -### How to determine the buffer 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: - -1. either you can predict the content of the object tree, -2. or, you know how much memory is available. - -In the first case, you know some constraints on the object tree. For instance, let's say that your know in advance (and by that I mean "at compilation time") that you want to generate an object with 3 values, one of them being an array with 2 values, like the following: - - {"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)` 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 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 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. - -## Memory usage - -#### Object size for 8-bit AVR - -| Type | Size | -|-------------------------|------------| -| JsonArray of N element | 4 + 8 * N | -| JsonObject of N element | 4 + 10 * N | \ No newline at end of file diff --git a/doc/Migrating code to new API.md b/doc/Migrating code to new API.md deleted file mode 100644 index 24471479..00000000 --- a/doc/Migrating code to new API.md +++ /dev/null @@ -1,80 +0,0 @@ -Migrating code written for Arduino JSON v3 to v4 -================================================ - -Arduino JSON v4 was a major rewrite of the library, and the API changed significantly. - -## Includes - -Arduino JSON v3 had two include files: - - #include - #include - -Arduino JSON v4 only has one: - - #include - -## Namespaces - -Arduino JSON v3 had two namespaces: - - using namespace ArduinoJson::Parser; - using namespace ArduinoJson::Generator; - -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 the parser: - - JsonParser<16> parser; // 16 being the capacity in "tokens" - -and for the generator: - - JsonArray<4> array; // 4 being the number of element - JsonObject<4> object; - -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 value types: - - JsonArray array = parser.parseArray(json); - JsonObject object = parser.parseObject(json); - -Arduino JSON v4 returns references types: - - JsonArray& array = buffer.parseArray(json); - JsonObject& object = buffer.parseObject(json); - -Everything else is compatible - -## Creating arrays and objects - -Arduino JSON v3 allowed to create `JsonArray` and `JsonObject` directly: - - JsonArray<4> array; - JsonObject<4> object; - -Arduino JSON v4 requires that you use a `StaticJsonBuffer` for that: - - JsonArray& array = buffer.createArray(); - JsonObject& object = buffer.createObject(); - -Note: you don't have to specify the capacity anymore. - -## Printable interface - -Arduino JSON v3 used to implement the Printable interface, which allowed statements like: - - Serial.print(array); - -But Arduino JSON v4 doesn't, instead you need to write this: - - array.printTo(Serial); - -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 deleted file mode 100644 index 5530289a..00000000 --- a/doc/Using the library with Arduino.md +++ /dev/null @@ -1,30 +0,0 @@ -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 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 - -[Download the zip package](https://github.com/bblanchon/ArduinoJson/releases) and extract it to: - - /libraries/ArduinoJson - -Then restart the Arduino IDE. - -## Run the examples sketches - -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: - - #include - -Then follow the instructions: - -1. [Parsing JSON](Parsin JSON.md) -2. [Generating JSON](Generating JSON.md) \ No newline at end of file diff --git a/doc/Using the library without Arduino.md b/doc/Using the library without Arduino.md deleted file mode 100644 index 05c307a9..00000000 --- a/doc/Using the library without Arduino.md +++ /dev/null @@ -1,37 +0,0 @@ -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 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. - -## Compiling the library - -Step 1: Download source code: - - git clone https://github.com/bblanchon/ArduinoJson.git - -Step 2: Generate the `Makefile` for your environment - - cd ArduinoJson - cmake . - -Step 3: Build - - make - -## File paths - -Assuming you installed the library into ``, you need to add: - -1. `/include` to your include path -2. `/lib` to your library path - - ----------- - -You are now ready to follow the instructions: - -1. [Parsing JSON](Parsin JSON.md) -2. [Generating JSON](Generating JSON.md) \ No newline at end of file