diff --git a/platformio.ini b/platformio.ini
index 94e3e9f5b..0581a84ec 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -94,7 +94,6 @@ build_src_filter = ${env.build_src_filter} -
lib_deps =
knolleary/PubSubClient@^2.8
arduino-libraries/NTPClient@^3.1.0
- meshtastic/json11@^1.0.2
; Common libs for environmental measurements in telemetry module
; (not included in native / portduino)
diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp
index 82ac8feef..d01f3c4a5 100644
--- a/src/mesh/http/ContentHandler.cpp
+++ b/src/mesh/http/ContentHandler.cpp
@@ -12,7 +12,7 @@
#include
#include
#include
-#include
+#include "mqtt/JSON.h"
#ifdef ARCH_ESP32
#include "esp_task_wdt.h"
@@ -246,15 +246,15 @@ void htmlDeleteDir(const char *dirname)
root.close();
}
-std::vector> *htmlListDir(std::vector> *fileList, const char *dirname,
- uint8_t levels)
+JSONArray htmlListDir(const char *dirname, uint8_t levels)
{
File root = FSCom.open(dirname, FILE_O_READ);
+ JSONArray fileList;
if (!root) {
- return NULL;
+ return fileList;
}
if (!root.isDirectory()) {
- return NULL;
+ return fileList;
}
// iterate over the file list
@@ -263,19 +263,19 @@ std::vector> *htmlListDir(std::vector thisFileMap;
- thisFileMap[strdup("size")] = strdup(String(file.size()).c_str());
+ JSONObject thisFileMap;
+ thisFileMap["size"] = new JSONValue((int)file.size());
#ifdef ARCH_ESP32
- thisFileMap[strdup("name")] = strdup(String(file.path()).substring(1).c_str());
+ thisFileMap["name"] = new JSONValue(String(file.path()).substring(1).c_str());
#else
- thisFileMap[strdup("name")] = strdup(String(file.name()).substring(1).c_str());
+ thisFileMap["name"] = new JSONValue(String(file.name()).substring(1).c_str());
#endif
if (String(file.name()).substring(1).endsWith(".gz")) {
#ifdef ARCH_ESP32
@@ -284,9 +284,9 @@ std::vector> *htmlListDir(std::vectorpush_back(thisFileMap);
+ fileList.push_back(new JSONValue(thisFileMap));
}
file.close();
file = root.openNextFile();
@@ -301,29 +301,31 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
- using namespace json11;
- auto fileList = htmlListDir(new std::vector>(), "/static", 10);
+ auto fileList = htmlListDir("/static", 10);
// create json output structure
- Json filesystemObj = Json::object{
- {"total", String(FSCom.totalBytes()).c_str()},
- {"used", String(FSCom.usedBytes()).c_str()},
- {"free", String(FSCom.totalBytes() - FSCom.usedBytes()).c_str()},
- };
+ JSONObject filesystemObj;
+ filesystemObj["total"] = new JSONValue((int)FSCom.totalBytes());
+ filesystemObj["used"] = new JSONValue((int)FSCom.usedBytes());
+ filesystemObj["free"] = new JSONValue(int(FSCom.totalBytes() - FSCom.usedBytes()));
- Json jsonObjInner = Json::object{{"files", Json(*fileList)}, {"filesystem", filesystemObj}};
+ JSONObject jsonObjInner;
+ jsonObjInner["files"] = new JSONValue(fileList);
+ jsonObjInner["filesystem"] = new JSONValue(filesystemObj);
- Json jsonObjOuter = Json::object{{"data", jsonObjInner}, {"status", "ok"}};
+ JSONObject jsonObjOuter;
+ jsonObjOuter["data"] = new JSONValue(jsonObjInner);
+ jsonObjOuter["status"] = new JSONValue("ok");
- // serialize and write it to the stream
- std::string jsonStr = jsonObjOuter.dump();
- res->print(jsonStr.c_str());
+ JSONValue *value = new JSONValue(jsonObjOuter);
+
+ res->print(value->Stringify().c_str());
+
+ delete value;
}
void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
{
- using namespace json11;
-
ResourceParameters *params = req->getParams();
std::string paramValDelete;
@@ -334,15 +336,19 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
std::string pathDelete = "/" + paramValDelete;
if (FSCom.remove(pathDelete.c_str())) {
Serial.println(pathDelete.c_str());
- Json jsonObjOuter = Json::object{{"status", "ok"}};
- std::string jsonStr = jsonObjOuter.dump();
- res->print(jsonStr.c_str());
+ JSONObject jsonObjOuter;
+ jsonObjOuter["status"] = new JSONValue("ok");
+ JSONValue *value = new JSONValue(jsonObjOuter);
+ res->print(value->Stringify().c_str());
+ delete value;
return;
} else {
Serial.println(pathDelete.c_str());
- Json jsonObjOuter = Json::object{{"status", "Error"}};
- std::string jsonStr = jsonObjOuter.dump();
- res->print(jsonStr.c_str());
+ JSONObject jsonObjOuter;
+ jsonObjOuter["status"] = new JSONValue("Error");
+ JSONValue *value = new JSONValue(jsonObjOuter);
+ res->print(value->Stringify().c_str());
+ delete value;
return;
}
}
@@ -559,8 +565,6 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
void handleReport(HTTPRequest *req, HTTPResponse *res)
{
- using namespace json11;
-
ResourceParameters *params = req->getParams();
std::string content;
@@ -579,81 +583,87 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
}
// data->airtime->tx_log
- std::vector txLogValues;
+ JSONArray txLogValues;
uint32_t *logArray;
logArray = airTime->airtimeReport(TX_LOG);
for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
- uint32_t tmp;
- tmp = *(logArray + i);
- txLogValues.push_back(String(tmp));
+ txLogValues.push_back(new JSONValue((int)logArray[i]));
}
// data->airtime->rx_log
- std::vector rxLogValues;
+ JSONArray rxLogValues;
logArray = airTime->airtimeReport(RX_LOG);
for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
- uint32_t tmp;
- tmp = *(logArray + i);
- rxLogValues.push_back(String(tmp));
+ rxLogValues.push_back(new JSONValue((int)logArray[i]));
}
// data->airtime->rx_all_log
- std::vector rxAllLogValues;
+ JSONArray rxAllLogValues;
logArray = airTime->airtimeReport(RX_ALL_LOG);
for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
- uint32_t tmp;
- tmp = *(logArray + i);
- rxAllLogValues.push_back(String(tmp));
+ rxAllLogValues.push_back(new JSONValue((int)logArray[i]));
}
- Json jsonObjAirtime = Json::object{
- {"tx_log", Json(txLogValues)},
- {"rx_log", Json(rxLogValues)},
- {"rx_all_log", Json(rxAllLogValues)},
- {"channel_utilization", Json(airTime->channelUtilizationPercent())},
- {"utilization_tx", Json(airTime->utilizationTXPercent())},
- {"seconds_since_boot", Json(int(airTime->getSecondsSinceBoot()))},
- {"seconds_per_period", Json(int(airTime->getSecondsPerPeriod()))},
- {"periods_to_log", Json(airTime->getPeriodsToLog())},
- };
+ // data->airtime
+ JSONObject jsonObjAirtime;
+ jsonObjAirtime["tx_log"] = new JSONValue(txLogValues);
+ jsonObjAirtime["rx_log"] = new JSONValue(rxLogValues);
+ jsonObjAirtime["rx_all_log"] = new JSONValue(rxAllLogValues);
+ jsonObjAirtime["channel_utilization"] = new JSONValue(airTime->channelUtilizationPercent());
+ jsonObjAirtime["utilization_tx"] = new JSONValue(airTime->utilizationTXPercent());
+ jsonObjAirtime["seconds_since_boot"] = new JSONValue(int(airTime->getSecondsSinceBoot()));
+ jsonObjAirtime["seconds_per_period"] = new JSONValue(int(airTime->getSecondsPerPeriod()));
+ jsonObjAirtime["periods_to_log"] = new JSONValue(airTime->getPeriodsToLog());
// data->wifi
- String ipStr = String(WiFi.localIP().toString());
-
- Json jsonObjWifi = Json::object{{"rssi", String(WiFi.RSSI())}, {"ip", ipStr.c_str()}};
+ JSONObject jsonObjWifi;
+ jsonObjWifi["rssi"] = new JSONValue(WiFi.RSSI());
+ jsonObjWifi["ip"] = new JSONValue(WiFi.localIP().toString().c_str());
// data->memory
- Json jsonObjMemory = Json::object{{"heap_total", Json(int(ESP.getHeapSize()))},
- {"heap_free", Json(int(ESP.getFreeHeap()))},
- {"psram_total", Json(int(ESP.getPsramSize()))},
- {"psram_free", Json(int(ESP.getFreePsram()))},
- {"fs_total", String(FSCom.totalBytes()).c_str()},
- {"fs_used", String(FSCom.usedBytes()).c_str()},
- {"fs_free", String(FSCom.totalBytes() - FSCom.usedBytes()).c_str()}};
+ JSONObject jsonObjMemory;
+ jsonObjMemory["heap_total"] = new JSONValue((int)ESP.getHeapSize());
+ jsonObjMemory["heap_free"] = new JSONValue((int)ESP.getFreeHeap());
+ jsonObjMemory["psram_total"] = new JSONValue((int)ESP.getPsramSize());
+ jsonObjMemory["psram_free"] = new JSONValue((int)ESP.getFreePsram());
+ jsonObjMemory["fs_total"] = new JSONValue((int)FSCom.totalBytes());
+ jsonObjMemory["fs_used"] = new JSONValue((int)FSCom.usedBytes());
+ jsonObjMemory["fs_free"] = new JSONValue(int(FSCom.totalBytes() - FSCom.usedBytes()));
// data->power
- Json jsonObjPower = Json::object{{"battery_percent", Json(powerStatus->getBatteryChargePercent())},
- {"battery_voltage_mv", Json(powerStatus->getBatteryVoltageMv())},
- {"has_battery", BoolToString(powerStatus->getHasBattery())},
- {"has_usb", BoolToString(powerStatus->getHasUSB())},
- {"is_charging", BoolToString(powerStatus->getIsCharging())}};
+ JSONObject jsonObjPower;
+ jsonObjPower["battery_percent"] = new JSONValue(powerStatus->getBatteryChargePercent());
+ jsonObjPower["battery_voltage_mv"] = new JSONValue(powerStatus->getBatteryVoltageMv());
+ jsonObjPower["has_battery"] = new JSONValue(BoolToString(powerStatus->getHasBattery()));
+ jsonObjPower["has_usb"] = new JSONValue(BoolToString(powerStatus->getHasUSB()));
+ jsonObjPower["is_charging"] = new JSONValue(BoolToString(powerStatus->getIsCharging()));
// data->device
- Json jsonObjDevice = Json::object{{"reboot_counter", Json(int(myNodeInfo.reboot_count))}};
+ JSONObject jsonObjDevice;
+ jsonObjDevice["reboot_counter"] = new JSONValue((int)myNodeInfo.reboot_count);
// data->radio
- Json jsonObjRadio = Json::object{{"frequency", Json(RadioLibInterface::instance->getFreq())},
- {"lora_channel", Json(int(RadioLibInterface::instance->getChannelNum()))}};
+ JSONObject jsonObjRadio;
+ jsonObjRadio["frequency"] = new JSONValue(RadioLibInterface::instance->getFreq());
+ jsonObjRadio["lora_channel"] = new JSONValue((int)RadioLibInterface::instance->getChannelNum());
// collect data to inner data object
- Json jsonObjInner = Json::object{{"airtime", jsonObjAirtime}, {"wifi", jsonObjWifi}, {"memory", jsonObjMemory},
- {"power", jsonObjPower}, {"device", jsonObjDevice}, {"radio", jsonObjRadio}};
+ JSONObject jsonObjInner;
+ jsonObjInner["airtime"] = new JSONValue(jsonObjAirtime);
+ jsonObjInner["wifi"] = new JSONValue(jsonObjWifi);
+ jsonObjInner["memory"] = new JSONValue(jsonObjMemory);
+ jsonObjInner["power"] = new JSONValue(jsonObjPower);
+ jsonObjInner["device"] = new JSONValue(jsonObjDevice);
+ jsonObjInner["radio"] = new JSONValue(jsonObjRadio);
// create json output structure
- Json jsonObjOuter = Json::object{{"data", jsonObjInner}, {"status", "ok"}};
+ JSONObject jsonObjOuter;
+ jsonObjOuter["data"] = new JSONValue(jsonObjInner);
+ jsonObjOuter["status"] = new JSONValue("ok");
// serialize and write it to the stream
- std::string jsonStr = jsonObjOuter.dump();
- res->print(jsonStr.c_str());
+ JSONValue *value = new JSONValue(jsonObjOuter);
+ res->print(value->Stringify().c_str());
+ delete value;
}
/*
@@ -767,8 +777,6 @@ void handleRestart(HTTPRequest *req, HTTPResponse *res)
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
{
- using namespace json11;
-
res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "POST");
@@ -797,15 +805,15 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
#endif
}
- Json jsonObjOuter = Json::object{{"status", "ok"}};
- std::string jsonStr = jsonObjOuter.dump();
- res->print(jsonStr.c_str());
+ JSONObject jsonObjOuter;
+ jsonObjOuter["status"] = new JSONValue("ok");
+ JSONValue *value = new JSONValue(jsonObjOuter);
+ res->print(value->Stringify().c_str());
+ delete value;
}
void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
{
- using namespace json11;
-
res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
@@ -814,7 +822,7 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
int n = WiFi.scanNetworks();
// build list of network objects
- std::vector networkObjs;
+ JSONArray networkObjs;
if (n > 0) {
for (int i = 0; i < n; ++i) {
char ssidArray[50];
@@ -823,8 +831,10 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
ssidString.toCharArray(ssidArray, 50);
if (WiFi.encryptionType(i) != WIFI_AUTH_OPEN) {
- Json thisNetwork = Json::object{{"ssid", ssidArray}, {"rssi", WiFi.RSSI(i)}};
- networkObjs.push_back(thisNetwork);
+ JSONObject thisNetwork;
+ thisNetwork["ssid"] = new JSONValue(ssidArray);
+ thisNetwork["rssi"] = new JSONValue(WiFi.RSSI(i));
+ networkObjs.push_back(new JSONValue(thisNetwork));
}
// Yield some cpu cycles to IP stack.
// This is important in case the list is large and it takes us time to return
@@ -834,9 +844,12 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
}
// build output structure
- Json jsonObjOuter = Json::object{{"data", networkObjs}, {"status", "ok"}};
+ JSONObject jsonObjOuter;
+ jsonObjOuter["data"] = new JSONValue(networkObjs);
+ jsonObjOuter["status"] = new JSONValue("ok");
// serialize and write it to the stream
- std::string jsonStr = jsonObjOuter.dump();
- res->print(jsonStr.c_str());
+ JSONValue *value = new JSONValue(jsonObjOuter);
+ res->print(value->Stringify().c_str());
+ delete value;
}
diff --git a/src/mqtt/JSON.cpp b/src/mqtt/JSON.cpp
new file mode 100644
index 000000000..65bff304a
--- /dev/null
+++ b/src/mqtt/JSON.cpp
@@ -0,0 +1,241 @@
+/*
+ * File JSON.cpp part of the SimpleJSON Library - http://mjpa.in/json
+ *
+ * Copyright (C) 2010 Mike Anchor
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "JSON.h"
+
+/**
+ * Blocks off the public constructor
+ *
+ * @access private
+ *
+ */
+JSON::JSON()
+{
+}
+
+/**
+ * Parses a complete JSON encoded string
+ *
+ * @access public
+ *
+ * @param char* data The JSON text
+ *
+ * @return JSONValue* Returns a JSON Value representing the root, or NULL on error
+ */
+JSONValue *JSON::Parse(const char *data)
+{
+ // Skip any preceding whitespace, end of data = no JSON = fail
+ if (!SkipWhitespace(&data))
+ return NULL;
+
+ // We need the start of a value here now...
+ JSONValue *value = JSONValue::Parse(&data);
+ if (value == NULL)
+ return NULL;
+
+ // Can be white space now and should be at the end of the string then...
+ if (SkipWhitespace(&data))
+ {
+ delete value;
+ return NULL;
+ }
+
+ // We're now at the end of the string
+ return value;
+}
+
+/**
+ * Turns the passed in JSONValue into a JSON encode string
+ *
+ * @access public
+ *
+ * @param JSONValue* value The root value
+ *
+ * @return std::string Returns a JSON encoded string representation of the given value
+ */
+std::string JSON::Stringify(const JSONValue *value)
+{
+ if (value != NULL)
+ return value->Stringify();
+ else
+ return "";
+}
+
+/**
+ * Skips over any whitespace characters (space, tab, \r or \n) defined by the JSON spec
+ *
+ * @access protected
+ *
+ * @param char** data Pointer to a char* that contains the JSON text
+ *
+ * @return bool Returns true if there is more data, or false if the end of the text was reached
+ */
+bool JSON::SkipWhitespace(const char **data)
+{
+ while (**data != 0 && (**data == ' ' || **data == '\t' || **data == '\r' || **data == '\n'))
+ (*data)++;
+
+ return **data != 0;
+}
+
+/**
+ * Extracts a JSON String as defined by the spec - ""
+ * Any escaped characters are swapped out for their unescaped values
+ *
+ * @access protected
+ *
+ * @param char** data Pointer to a char* that contains the JSON text
+ * @param std::string& str Reference to a std::string to receive the extracted string
+ *
+ * @return bool Returns true on success, false on failure
+ */
+bool JSON::ExtractString(const char **data, std::string &str)
+{
+ str = "";
+
+ while (**data != 0)
+ {
+ // Save the char so we can change it if need be
+ char next_char = **data;
+
+ // Escaping something?
+ if (next_char == '\\')
+ {
+ // Move over the escape char
+ (*data)++;
+
+ // Deal with the escaped char
+ switch (**data)
+ {
+ case '"': next_char = '"'; break;
+ case '\\': next_char = '\\'; break;
+ case '/': next_char = '/'; break;
+ case 'b': next_char = '\b'; break;
+ case 'f': next_char = '\f'; break;
+ case 'n': next_char = '\n'; break;
+ case 'r': next_char = '\r'; break;
+ case 't': next_char = '\t'; break;
+ case 'u':
+ {
+ // We need 5 chars (4 hex + the 'u') or its not valid
+ if (!simplejson_csnlen(*data, 5))
+ return false;
+
+ // Deal with the chars
+ next_char = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ // Do it first to move off the 'u' and leave us on the
+ // final hex digit as we move on by one later on
+ (*data)++;
+
+ next_char <<= 4;
+
+ // Parse the hex digit
+ if (**data >= '0' && **data <= '9')
+ next_char |= (**data - '0');
+ else if (**data >= 'A' && **data <= 'F')
+ next_char |= (10 + (**data - 'A'));
+ else if (**data >= 'a' && **data <= 'f')
+ next_char |= (10 + (**data - 'a'));
+ else
+ {
+ // Invalid hex digit = invalid JSON
+ return false;
+ }
+ }
+ break;
+ }
+
+ // By the spec, only the above cases are allowed
+ default:
+ return false;
+ }
+ }
+
+ // End of the string?
+ else if (next_char == '"')
+ {
+ (*data)++;
+ str.reserve(); // Remove unused capacity
+ return true;
+ }
+
+ // Disallowed char?
+ else if (next_char < ' ' && next_char != '\t')
+ {
+ // SPEC Violation: Allow tabs due to real world cases
+ return false;
+ }
+
+ // Add the next char
+ str += next_char;
+
+ // Move on
+ (*data)++;
+ }
+
+ // If we're here, the string ended incorrectly
+ return false;
+}
+
+/**
+ * Parses some text as though it is an integer
+ *
+ * @access protected
+ *
+ * @param char** data Pointer to a char* that contains the JSON text
+ *
+ * @return double Returns the double value of the number found
+ */
+double JSON::ParseInt(const char **data)
+{
+ double integer = 0;
+ while (**data != 0 && **data >= '0' && **data <= '9')
+ integer = integer * 10 + (*(*data)++ - '0');
+
+ return integer;
+}
+
+/**
+ * Parses some text as though it is a decimal
+ *
+ * @access protected
+ *
+ * @param char** data Pointer to a char* that contains the JSON text
+ *
+ * @return double Returns the double value of the decimal found
+ */
+double JSON::ParseDecimal(const char **data)
+{
+ double decimal = 0.0;
+ double factor = 0.1;
+ while (**data != 0 && **data >= '0' && **data <= '9')
+ {
+ int digit = (*(*data)++ - '0');
+ decimal = decimal + digit * factor;
+ factor *= 0.1;
+ }
+ return decimal;
+}
diff --git a/src/mqtt/JSON.h b/src/mqtt/JSON.h
new file mode 100644
index 000000000..d6532d696
--- /dev/null
+++ b/src/mqtt/JSON.h
@@ -0,0 +1,70 @@
+/*
+ * File JSON.h part of the SimpleJSON Library - http://mjpa.in/json
+ *
+ * Copyright (C) 2010 Mike Anchor
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _JSON_H_
+#define _JSON_H_
+
+#include
+#include
+#include