feat: 全量同步 254 个常用的 Arduino 扩展库文件
This commit is contained in:
3
arduino-libs/arduino-cli/libraries/json-streaming-parser/.gitignore
vendored
Normal file
3
arduino-libs/arduino-cli/libraries/json-streaming-parser/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.pioenvs
|
||||
.clang_complete
|
||||
.gcc-flags.json
|
||||
@@ -0,0 +1,19 @@
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
|
||||
# Cache PlatformIO packages using Travis CI container-based infrastructure
|
||||
sudo: false
|
||||
cache:
|
||||
directories:
|
||||
- "~/.platformio"
|
||||
|
||||
env:
|
||||
- PLATFORMIO_CI_SRC=examples/JsonStreamingParser
|
||||
|
||||
|
||||
install:
|
||||
- pip install -U platformio
|
||||
|
||||
script:
|
||||
- platformio ci --lib="." --board=nodemcuv2
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/**The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 by Daniel Eichhorn
|
||||
|
||||
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.
|
||||
|
||||
See more at http://blog.squix.ch and https://github.com/squix78/json-streaming-parser
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class JsonListener {
|
||||
private:
|
||||
|
||||
public:
|
||||
|
||||
virtual void whitespace(char c) = 0;
|
||||
|
||||
virtual void startDocument() = 0;
|
||||
|
||||
virtual void key(String key) = 0;
|
||||
|
||||
virtual void value(String value) = 0;
|
||||
|
||||
virtual void endArray() = 0;
|
||||
|
||||
virtual void endObject() = 0;
|
||||
|
||||
virtual void endDocument() = 0;
|
||||
|
||||
virtual void startArray() = 0;
|
||||
|
||||
virtual void startObject() = 0;
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,516 @@
|
||||
/**The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 by Daniel Eichhorn
|
||||
|
||||
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.
|
||||
|
||||
See more at http://blog.squix.ch and https://github.com/squix78/json-streaming-parser
|
||||
*/
|
||||
|
||||
#include "JsonStreamingParser.h"
|
||||
|
||||
JsonStreamingParser::JsonStreamingParser() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void JsonStreamingParser::reset() {
|
||||
state = STATE_START_DOCUMENT;
|
||||
bufferPos = 0;
|
||||
unicodeEscapeBufferPos = 0;
|
||||
unicodeBufferPos = 0;
|
||||
characterCounter = 0;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::setListener(JsonListener* listener) {
|
||||
myListener = listener;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::parse(char c) {
|
||||
//System.out.print(c);
|
||||
// valid whitespace characters in JSON (from RFC4627 for JSON) include:
|
||||
// space, horizontal tab, line feed or new line, and carriage return.
|
||||
// thanks:
|
||||
// http://stackoverflow.com/questions/16042274/definition-of-whitespace-in-json
|
||||
if ((c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
||||
&& !(state == STATE_IN_STRING || state == STATE_UNICODE || state == STATE_START_ESCAPE
|
||||
|| state == STATE_IN_NUMBER || state == STATE_START_DOCUMENT)) {
|
||||
return;
|
||||
}
|
||||
switch (state) {
|
||||
case STATE_IN_STRING:
|
||||
if (c == '"') {
|
||||
endString();
|
||||
} else if (c == '\\') {
|
||||
state = STATE_START_ESCAPE;
|
||||
} else if ((c < 0x1f) || (c == 0x7f)) {
|
||||
//throw new RuntimeException("Unescaped control character encountered: " + c + " at position" + characterCounter);
|
||||
} else {
|
||||
buffer[bufferPos] = c;
|
||||
increaseBufferPointer();
|
||||
}
|
||||
break;
|
||||
case STATE_IN_ARRAY:
|
||||
if (c == ']') {
|
||||
endArray();
|
||||
} else {
|
||||
startValue(c);
|
||||
}
|
||||
break;
|
||||
case STATE_IN_OBJECT:
|
||||
if (c == '}') {
|
||||
endObject();
|
||||
} else if (c == '"') {
|
||||
startKey();
|
||||
} else {
|
||||
//throw new RuntimeException("Start of string expected for object key. Instead got: " + c + " at position" + characterCounter);
|
||||
}
|
||||
break;
|
||||
case STATE_END_KEY:
|
||||
if (c != ':') {
|
||||
//throw new RuntimeException("Expected ':' after key. Instead got " + c + " at position" + characterCounter);
|
||||
}
|
||||
state = STATE_AFTER_KEY;
|
||||
break;
|
||||
case STATE_AFTER_KEY:
|
||||
startValue(c);
|
||||
break;
|
||||
case STATE_START_ESCAPE:
|
||||
processEscapeCharacters(c);
|
||||
break;
|
||||
case STATE_UNICODE:
|
||||
processUnicodeCharacter(c);
|
||||
break;
|
||||
case STATE_UNICODE_SURROGATE:
|
||||
unicodeEscapeBuffer[unicodeEscapeBufferPos] = c;
|
||||
unicodeEscapeBufferPos++;
|
||||
if (unicodeEscapeBufferPos == 2) {
|
||||
endUnicodeSurrogateInterstitial();
|
||||
}
|
||||
break;
|
||||
case STATE_AFTER_VALUE: {
|
||||
// not safe for size == 0!!!
|
||||
int within = stack[stackPos - 1];
|
||||
if (within == STACK_OBJECT) {
|
||||
if (c == '}') {
|
||||
endObject();
|
||||
} else if (c == ',') {
|
||||
state = STATE_IN_OBJECT;
|
||||
} else {
|
||||
//throw new RuntimeException("Expected ',' or '}' while parsing object. Got: " + c + ". " + characterCounter);
|
||||
}
|
||||
} else if (within == STACK_ARRAY) {
|
||||
if (c == ']') {
|
||||
endArray();
|
||||
} else if (c == ',') {
|
||||
state = STATE_IN_ARRAY;
|
||||
} else {
|
||||
//throw new RuntimeException("Expected ',' or ']' while parsing array. Got: " + c + ". " + characterCounter);
|
||||
|
||||
}
|
||||
} else {
|
||||
//throw new RuntimeException("Finished a literal, but unclear what state to move to. Last state: " + characterCounter);
|
||||
}
|
||||
}break;
|
||||
case STATE_IN_NUMBER:
|
||||
if (c >= '0' && c <= '9') {
|
||||
buffer[bufferPos] = c;
|
||||
increaseBufferPointer();
|
||||
} else if (c == '.') {
|
||||
if (doesCharArrayContain(buffer, bufferPos, '.')) {
|
||||
//throw new RuntimeException("Cannot have multiple decimal points in a number. " + characterCounter);
|
||||
} else if (doesCharArrayContain(buffer, bufferPos, 'e')) {
|
||||
//throw new RuntimeException("Cannot have a decimal point in an exponent." + characterCounter);
|
||||
}
|
||||
buffer[bufferPos] = c;
|
||||
increaseBufferPointer();
|
||||
} else if (c == 'e' || c == 'E') {
|
||||
if (doesCharArrayContain(buffer, bufferPos, 'e')) {
|
||||
//throw new RuntimeException("Cannot have multiple exponents in a number. " + characterCounter);
|
||||
}
|
||||
buffer[bufferPos] = c;
|
||||
increaseBufferPointer();
|
||||
} else if (c == '+' || c == '-') {
|
||||
char last = buffer[bufferPos - 1];
|
||||
if (!(last == 'e' || last == 'E')) {
|
||||
//throw new RuntimeException("Can only have '+' or '-' after the 'e' or 'E' in a number." + characterCounter);
|
||||
}
|
||||
buffer[bufferPos] = c;
|
||||
increaseBufferPointer();
|
||||
} else {
|
||||
endNumber();
|
||||
// we have consumed one beyond the end of the number
|
||||
parse(c);
|
||||
}
|
||||
break;
|
||||
case STATE_IN_TRUE:
|
||||
buffer[bufferPos] = c;
|
||||
increaseBufferPointer();
|
||||
if (bufferPos == 4) {
|
||||
endTrue();
|
||||
}
|
||||
break;
|
||||
case STATE_IN_FALSE:
|
||||
buffer[bufferPos] = c;
|
||||
increaseBufferPointer();
|
||||
if (bufferPos == 5) {
|
||||
endFalse();
|
||||
}
|
||||
break;
|
||||
case STATE_IN_NULL:
|
||||
buffer[bufferPos] = c;
|
||||
increaseBufferPointer();
|
||||
if (bufferPos == 4) {
|
||||
endNull();
|
||||
}
|
||||
break;
|
||||
case STATE_START_DOCUMENT:
|
||||
myListener->startDocument();
|
||||
if (c == '[') {
|
||||
startArray();
|
||||
} else if (c == '{') {
|
||||
startObject();
|
||||
} else {
|
||||
// throw new ParsingError($this->_line_number,
|
||||
// $this->_char_number,
|
||||
// "Document must start with object or array.");
|
||||
}
|
||||
break;
|
||||
//case STATE_DONE:
|
||||
// throw new ParsingError($this->_line_number, $this->_char_number,
|
||||
// "Expected end of document.");
|
||||
//default:
|
||||
// throw new ParsingError($this->_line_number, $this->_char_number,
|
||||
// "Internal error. Reached an unknown state: ".$this->_state);
|
||||
}
|
||||
characterCounter++;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::increaseBufferPointer() {
|
||||
bufferPos = min(bufferPos + 1, BUFFER_MAX_LENGTH - 1);
|
||||
}
|
||||
|
||||
void JsonStreamingParser::endString() {
|
||||
int popped = stack[stackPos - 1];
|
||||
stackPos--;
|
||||
if (popped == STACK_KEY) {
|
||||
buffer[bufferPos] = '\0';
|
||||
myListener->key(String(buffer));
|
||||
state = STATE_END_KEY;
|
||||
} else if (popped == STACK_STRING) {
|
||||
buffer[bufferPos] = '\0';
|
||||
myListener->value(String(buffer));
|
||||
state = STATE_AFTER_VALUE;
|
||||
} else {
|
||||
// throw new ParsingError($this->_line_number, $this->_char_number,
|
||||
// "Unexpected end of string.");
|
||||
}
|
||||
bufferPos = 0;
|
||||
}
|
||||
void JsonStreamingParser::startValue(char c) {
|
||||
if (c == '[') {
|
||||
startArray();
|
||||
} else if (c == '{') {
|
||||
startObject();
|
||||
} else if (c == '"') {
|
||||
startString();
|
||||
} else if (isDigit(c)) {
|
||||
startNumber(c);
|
||||
} else if (c == 't') {
|
||||
state = STATE_IN_TRUE;
|
||||
buffer[bufferPos] = c;
|
||||
increaseBufferPointer();
|
||||
} else if (c == 'f') {
|
||||
state = STATE_IN_FALSE;
|
||||
buffer[bufferPos] = c;
|
||||
increaseBufferPointer();
|
||||
} else if (c == 'n') {
|
||||
state = STATE_IN_NULL;
|
||||
buffer[bufferPos] = c;
|
||||
increaseBufferPointer();
|
||||
} else {
|
||||
// throw new ParsingError($this->_line_number, $this->_char_number,
|
||||
// "Unexpected character for value: ".$c);
|
||||
}
|
||||
}
|
||||
|
||||
boolean JsonStreamingParser::isDigit(char c) {
|
||||
// Only concerned with the first character in a number.
|
||||
return (c >= '0' && c <= '9') || c == '-';
|
||||
}
|
||||
|
||||
void JsonStreamingParser::endArray() {
|
||||
int popped = stack[stackPos - 1];
|
||||
stackPos--;
|
||||
if (popped != STACK_ARRAY) {
|
||||
// throw new ParsingError($this->_line_number, $this->_char_number,
|
||||
// "Unexpected end of array encountered.");
|
||||
}
|
||||
myListener->endArray();
|
||||
state = STATE_AFTER_VALUE;
|
||||
if (stackPos == 0) {
|
||||
endDocument();
|
||||
}
|
||||
}
|
||||
|
||||
void JsonStreamingParser::startKey() {
|
||||
stack[stackPos] = STACK_KEY;
|
||||
stackPos++;
|
||||
state = STATE_IN_STRING;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::endObject() {
|
||||
int popped = stack[stackPos];
|
||||
stackPos--;
|
||||
if (popped != STACK_OBJECT) {
|
||||
// throw new ParsingError($this->_line_number, $this->_char_number,
|
||||
// "Unexpected end of object encountered.");
|
||||
}
|
||||
myListener->endObject();
|
||||
state = STATE_AFTER_VALUE;
|
||||
if (stackPos == 0) {
|
||||
endDocument();
|
||||
}
|
||||
}
|
||||
|
||||
void JsonStreamingParser::processEscapeCharacters(char c) {
|
||||
if (c == '"') {
|
||||
buffer[bufferPos] = '"';
|
||||
increaseBufferPointer();
|
||||
} else if (c == '\\') {
|
||||
buffer[bufferPos] = '\\';
|
||||
increaseBufferPointer();
|
||||
} else if (c == '/') {
|
||||
buffer[bufferPos] = '/';
|
||||
increaseBufferPointer();
|
||||
} else if (c == 'b') {
|
||||
buffer[bufferPos] = 0x08;
|
||||
increaseBufferPointer();
|
||||
} else if (c == 'f') {
|
||||
buffer[bufferPos] = '\f';
|
||||
increaseBufferPointer();
|
||||
} else if (c == 'n') {
|
||||
buffer[bufferPos] = '\n';
|
||||
increaseBufferPointer();
|
||||
} else if (c == 'r') {
|
||||
buffer[bufferPos] = '\r';
|
||||
increaseBufferPointer();
|
||||
} else if (c == 't') {
|
||||
buffer[bufferPos] = '\t';
|
||||
increaseBufferPointer();
|
||||
} else if (c == 'u') {
|
||||
state = STATE_UNICODE;
|
||||
} else {
|
||||
// throw new ParsingError($this->_line_number, $this->_char_number,
|
||||
// "Expected escaped character after backslash. Got: ".$c);
|
||||
}
|
||||
if (state != STATE_UNICODE) {
|
||||
state = STATE_IN_STRING;
|
||||
}
|
||||
}
|
||||
|
||||
void JsonStreamingParser::processUnicodeCharacter(char c) {
|
||||
if (!isHexCharacter(c)) {
|
||||
// throw new ParsingError($this->_line_number, $this->_char_number,
|
||||
// "Expected hex character for escaped Unicode character. Unicode parsed: "
|
||||
// . implode($this->_unicode_buffer) . " and got: ".$c);
|
||||
}
|
||||
|
||||
unicodeBuffer[unicodeBufferPos] = c;
|
||||
unicodeBufferPos++;
|
||||
|
||||
if (unicodeBufferPos == 4) {
|
||||
int codepoint = getHexArrayAsDecimal(unicodeBuffer, unicodeBufferPos);
|
||||
endUnicodeCharacter(codepoint);
|
||||
return;
|
||||
/*if (codepoint >= 0xD800 && codepoint < 0xDC00) {
|
||||
unicodeHighSurrogate = codepoint;
|
||||
unicodeBufferPos = 0;
|
||||
state = STATE_UNICODE_SURROGATE;
|
||||
} else if (codepoint >= 0xDC00 && codepoint <= 0xDFFF) {
|
||||
if (unicodeHighSurrogate == -1) {
|
||||
// throw new ParsingError($this->_line_number,
|
||||
// $this->_char_number,
|
||||
// "Missing high surrogate for Unicode low surrogate.");
|
||||
}
|
||||
int combinedCodePoint = ((unicodeHighSurrogate - 0xD800) * 0x400) + (codepoint - 0xDC00) + 0x10000;
|
||||
endUnicodeCharacter(combinedCodePoint);
|
||||
} else if (unicodeHighSurrogate != -1) {
|
||||
// throw new ParsingError($this->_line_number,
|
||||
// $this->_char_number,
|
||||
// "Invalid low surrogate following Unicode high surrogate.");
|
||||
endUnicodeCharacter(codepoint);
|
||||
} else {
|
||||
endUnicodeCharacter(codepoint);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
boolean JsonStreamingParser::isHexCharacter(char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||
}
|
||||
|
||||
int JsonStreamingParser::getHexArrayAsDecimal(char hexArray[], int length) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char current = hexArray[length - i - 1];
|
||||
int value = 0;
|
||||
if (current >= 'a' && current <= 'f') {
|
||||
value = current - 'a' + 10;
|
||||
} else if (current >= 'A' && current <= 'F') {
|
||||
value = current - 'A' + 10;
|
||||
} else if (current >= '0' && current <= '9') {
|
||||
value = current - '0';
|
||||
}
|
||||
result += value * 16^i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
boolean JsonStreamingParser::doesCharArrayContain(char myArray[], int length, char c) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (myArray[i] == c) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::endUnicodeSurrogateInterstitial() {
|
||||
char unicodeEscape = unicodeEscapeBuffer[unicodeEscapeBufferPos - 1];
|
||||
if (unicodeEscape != 'u') {
|
||||
// throw new ParsingError($this->_line_number, $this->_char_number,
|
||||
// "Expected '\\u' following a Unicode high surrogate. Got: " .
|
||||
// $unicode_escape);
|
||||
}
|
||||
unicodeBufferPos = 0;
|
||||
unicodeEscapeBufferPos = 0;
|
||||
state = STATE_UNICODE;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::endNumber() {
|
||||
buffer[bufferPos] = '\0';
|
||||
String value = String(buffer);
|
||||
//float result = 0.0;
|
||||
//if (doesCharArrayContain(buffer, bufferPos, '.')) {
|
||||
// result = value.toFloat();
|
||||
//} else {
|
||||
// needed special treatment in php, maybe not in Java and c
|
||||
// result = value.toFloat();
|
||||
//}
|
||||
myListener->value(value.c_str());
|
||||
bufferPos = 0;
|
||||
state = STATE_AFTER_VALUE;
|
||||
}
|
||||
|
||||
int JsonStreamingParser::convertDecimalBufferToInt(char myArray[], int length) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char current = myArray[length - i - 1];
|
||||
result += (current - '0') * 10;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::endDocument() {
|
||||
myListener->endDocument();
|
||||
state = STATE_DONE;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::endTrue() {
|
||||
buffer[bufferPos] = '\0';
|
||||
String value = String(buffer);
|
||||
if (value.equals("true")) {
|
||||
myListener->value("true");
|
||||
} else {
|
||||
// throw new ParsingError($this->_line_number, $this->_char_number,
|
||||
// "Expected 'true'. Got: ".$true);
|
||||
}
|
||||
bufferPos = 0;
|
||||
state = STATE_AFTER_VALUE;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::endFalse() {
|
||||
buffer[bufferPos] = '\0';
|
||||
String value = String(buffer);
|
||||
if (value.equals("false")) {
|
||||
myListener->value("false");
|
||||
} else {
|
||||
// throw new ParsingError($this->_line_number, $this->_char_number,
|
||||
// "Expected 'true'. Got: ".$true);
|
||||
}
|
||||
bufferPos = 0;
|
||||
state = STATE_AFTER_VALUE;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::endNull() {
|
||||
buffer[bufferPos] = '\0';
|
||||
String value = String(buffer);
|
||||
if (value.equals("null")) {
|
||||
myListener->value("null");
|
||||
} else {
|
||||
// throw new ParsingError($this->_line_number, $this->_char_number,
|
||||
// "Expected 'true'. Got: ".$true);
|
||||
}
|
||||
bufferPos = 0;
|
||||
state = STATE_AFTER_VALUE;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::startArray() {
|
||||
myListener->startArray();
|
||||
state = STATE_IN_ARRAY;
|
||||
stack[stackPos] = STACK_ARRAY;
|
||||
stackPos++;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::startObject() {
|
||||
myListener->startObject();
|
||||
state = STATE_IN_OBJECT;
|
||||
stack[stackPos] = STACK_OBJECT;
|
||||
stackPos++;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::startString() {
|
||||
stack[stackPos] = STACK_STRING;
|
||||
stackPos++;
|
||||
state = STATE_IN_STRING;
|
||||
}
|
||||
|
||||
void JsonStreamingParser::startNumber(char c) {
|
||||
state = STATE_IN_NUMBER;
|
||||
buffer[bufferPos] = c;
|
||||
increaseBufferPointer();
|
||||
}
|
||||
|
||||
void JsonStreamingParser::endUnicodeCharacter(int codepoint) {
|
||||
buffer[bufferPos] = convertCodepointToCharacter(codepoint);
|
||||
increaseBufferPointer();
|
||||
unicodeBufferPos = 0;
|
||||
unicodeHighSurrogate = -1;
|
||||
state = STATE_IN_STRING;
|
||||
}
|
||||
|
||||
char JsonStreamingParser::convertCodepointToCharacter(int num) {
|
||||
if (num <= 0x7F)
|
||||
return (char) (num);
|
||||
// if(num<=0x7FF) return (char)((num>>6)+192) + (char)((num&63)+128);
|
||||
// if(num<=0xFFFF) return
|
||||
// chr((num>>12)+224).chr(((num>>6)&63)+128).chr((num&63)+128);
|
||||
// if(num<=0x1FFFFF) return
|
||||
// chr((num>>18)+240).chr(((num>>12)&63)+128).chr(((num>>6)&63)+128).chr((num&63)+128);
|
||||
return ' ';
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/**The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 by Daniel Eichhorn
|
||||
|
||||
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.
|
||||
|
||||
See more at http://blog.squix.ch and https://github.com/squix78/json-streaming-parser
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "JsonListener.h"
|
||||
|
||||
#define STATE_START_DOCUMENT 0
|
||||
#define STATE_DONE -1
|
||||
#define STATE_IN_ARRAY 1
|
||||
#define STATE_IN_OBJECT 2
|
||||
#define STATE_END_KEY 3
|
||||
#define STATE_AFTER_KEY 4
|
||||
#define STATE_IN_STRING 5
|
||||
#define STATE_START_ESCAPE 6
|
||||
#define STATE_UNICODE 7
|
||||
#define STATE_IN_NUMBER 8
|
||||
#define STATE_IN_TRUE 9
|
||||
#define STATE_IN_FALSE 10
|
||||
#define STATE_IN_NULL 11
|
||||
#define STATE_AFTER_VALUE 12
|
||||
#define STATE_UNICODE_SURROGATE 13
|
||||
|
||||
#define STACK_OBJECT 0
|
||||
#define STACK_ARRAY 1
|
||||
#define STACK_KEY 2
|
||||
#define STACK_STRING 3
|
||||
|
||||
#define BUFFER_MAX_LENGTH 512
|
||||
|
||||
class JsonStreamingParser {
|
||||
private:
|
||||
|
||||
|
||||
int state;
|
||||
int stack[20];
|
||||
int stackPos = 0;
|
||||
JsonListener* myListener;
|
||||
|
||||
boolean doEmitWhitespace = false;
|
||||
// fixed length buffer array to prepare for c code
|
||||
char buffer[BUFFER_MAX_LENGTH];
|
||||
int bufferPos = 0;
|
||||
|
||||
char unicodeEscapeBuffer[10];
|
||||
int unicodeEscapeBufferPos = 0;
|
||||
|
||||
char unicodeBuffer[10];
|
||||
int unicodeBufferPos = 0;
|
||||
|
||||
int characterCounter = 0;
|
||||
|
||||
int unicodeHighSurrogate = 0;
|
||||
|
||||
void increaseBufferPointer();
|
||||
|
||||
void endString();
|
||||
|
||||
void endArray();
|
||||
|
||||
void startValue(char c);
|
||||
|
||||
void startKey();
|
||||
|
||||
void processEscapeCharacters(char c);
|
||||
|
||||
boolean isDigit(char c);
|
||||
|
||||
boolean isHexCharacter(char c);
|
||||
|
||||
char convertCodepointToCharacter(int num);
|
||||
|
||||
void endUnicodeCharacter(int codepoint);
|
||||
|
||||
void startNumber(char c);
|
||||
|
||||
void startString();
|
||||
|
||||
void startObject();
|
||||
|
||||
void startArray();
|
||||
|
||||
void endNull();
|
||||
|
||||
void endFalse();
|
||||
|
||||
void endTrue();
|
||||
|
||||
void endDocument();
|
||||
|
||||
int convertDecimalBufferToInt(char myArray[], int length);
|
||||
|
||||
void endNumber();
|
||||
|
||||
void endUnicodeSurrogateInterstitial();
|
||||
|
||||
boolean doesCharArrayContain(char myArray[], int length, char c);
|
||||
|
||||
int getHexArrayAsDecimal(char hexArray[], int length);
|
||||
|
||||
void processUnicodeCharacter(char c);
|
||||
|
||||
void endObject();
|
||||
|
||||
|
||||
|
||||
public:
|
||||
JsonStreamingParser();
|
||||
void parse(char c);
|
||||
void setListener(JsonListener* listener);
|
||||
void reset();
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 by Daniel Eichhorn
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,62 @@
|
||||
# json-streaming-parser
|
||||
Arduino library for parsing potentially huge json streams on devices with scarce memory.
|
||||
|
||||
This library is a port of Salsify's PHP based json streaming parser (https://github.com/salsify/jsonstreamingparser).
|
||||
|
||||
## Why yet another JSON parser?
|
||||
|
||||
When working with small (connected) devices you might quickly get to the point where you need to process potentially huge JSON object received from a REST interface.
|
||||
All libraries for processing JSON objects on the Arduino platform had some deficiencies which led me to create one on my own.
|
||||
Not really being an expert in writing parsers I miserably failed the first time.
|
||||
|
||||
This is the second attempt and I took Salsify's PHP based parser and ported it to C++.
|
||||
This library has been tested on the Arduino/ESP8266 target platform but should work in theory on all platforms available for the Arduino environment
|
||||
|
||||
## Why a streaming parser?
|
||||
|
||||
Generally speaking when parsing data you have two options to make sense of this data:
|
||||
you either create a document object model (DOM) which contains the whole information of that document and lets you retrieve the
|
||||
nodes with random access. A DOM parser needs the whole document to start parsing and only lets you access the data
|
||||
after it has finished the process.
|
||||
The other option you have when parsing a document is to process it char by char (or byte by byte) while it becomes
|
||||
available to your client code. Compared to the DOM parser this has two advantages: a) you can react
|
||||
the data as soon as meaningful parts are available and b) you can drop information as soon as the parser has processed
|
||||
it. This reduces the memory consumption a lot, especially if you retrieve huge documents when only being interested
|
||||
by a small subset of it. But this efficiency comes at a price: your code will have to do more "magic" than with a
|
||||
DOM parser, the business logic becomes part of the parser.
|
||||
|
||||
## How to install
|
||||
|
||||
Until the library becomes available in the Arduino IDE library manager you'll have to do a bit more work by hand.
|
||||
1) Download this library: https://github.com/squix78/json-streaming-parser/archive/master.zip
|
||||
2) Rename master.zip to json-streaming-parser.zip
|
||||
3) Open the zip file in the Arduino IDE from menu Sketch > Include Library > Add ZIP Library...
|
||||
|
||||
## How to use
|
||||
|
||||
This is a streaming parser, which means that you feed a stream of chars into the parser and you take out from that
|
||||
stream whatever you are interested in. In order to do that you will create a subclass of JsonListener class and
|
||||
implement methods which will be notified in case of certain events in the feed occure. Available events are:
|
||||
|
||||
* startDocument()
|
||||
* key(String key)
|
||||
* value(String value)
|
||||
* endArray()
|
||||
* endObject()
|
||||
* endDocument()
|
||||
* startArray()
|
||||
* startObject()
|
||||
|
||||
In your implementation of these methods you will have to write problem specific code to find the parts of the document that you are interested in. Please see the example to understand what that means. In the example the ExampleListener implements the event methods declared in the JsonListener interface and prints to the serial console when they are called.
|
||||
|
||||
## License
|
||||
|
||||
This code is available under the MIT license, which basically means that you can use, modify the distribute the code as long as you give credits to me (and Salsify) and add a reference back to this repository. Please read https://github.com/squix78/json-streaming-parser/blob/master/LICENSE for more detail...
|
||||
|
||||
## Credits
|
||||
|
||||
First of all I'd like to thank Salsify for making their PHP parser available to the public. You find their repository here: https://github.com/salsify/jsonstreamingparser
|
||||
|
||||
Then I'd like to thank my employer Netcetera (https://github.com/netceteragroup) to let us hackers go twice a year to the CodeCamp and work on software projects like this one.
|
||||
|
||||
And last but not least I'd like to thank my wife that she led me spend three days away from the family hacking in the wonderful mountains of Berne.
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "ExampleParser.h"
|
||||
#include "JsonListener.h"
|
||||
|
||||
|
||||
void ExampleListener::whitespace(char c) {
|
||||
Serial.println("whitespace");
|
||||
}
|
||||
|
||||
void ExampleListener::startDocument() {
|
||||
Serial.println("start document");
|
||||
}
|
||||
|
||||
void ExampleListener::key(String key) {
|
||||
Serial.println("key: " + key);
|
||||
}
|
||||
|
||||
void ExampleListener::value(String value) {
|
||||
Serial.println("value: " + value);
|
||||
}
|
||||
|
||||
void ExampleListener::endArray() {
|
||||
Serial.println("end array. ");
|
||||
}
|
||||
|
||||
void ExampleListener::endObject() {
|
||||
Serial.println("end object. ");
|
||||
}
|
||||
|
||||
void ExampleListener::endDocument() {
|
||||
Serial.println("end document. ");
|
||||
}
|
||||
|
||||
void ExampleListener::startArray() {
|
||||
Serial.println("start array. ");
|
||||
}
|
||||
|
||||
void ExampleListener::startObject() {
|
||||
Serial.println("start object. ");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "JsonListener.h"
|
||||
|
||||
class ExampleListener: public JsonListener {
|
||||
|
||||
public:
|
||||
virtual void whitespace(char c);
|
||||
|
||||
virtual void startDocument();
|
||||
|
||||
virtual void key(String key);
|
||||
|
||||
virtual void value(String value);
|
||||
|
||||
virtual void endArray();
|
||||
|
||||
virtual void endObject();
|
||||
|
||||
virtual void endDocument();
|
||||
|
||||
virtual void startArray();
|
||||
|
||||
virtual void startObject();
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "JsonStreamingParser.h"
|
||||
#include "JsonListener.h"
|
||||
#include "ExampleParser.h"
|
||||
|
||||
JsonStreamingParser parser;
|
||||
ExampleListener listener;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println(String(ESP.getFreeHeap()));
|
||||
parser.setListener(&listener);
|
||||
// put your setup code here, to run once:
|
||||
char json[] = "{\"a\":3, \"b\":{\"c\":\"d\"}}";
|
||||
for (int i = 0; i < sizeof(json); i++) {
|
||||
parser.parse(json[i]);
|
||||
}
|
||||
Serial.println(String(ESP.getFreeHeap()));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "JsonStreamingParser",
|
||||
"keywords": "json, streaming",
|
||||
"description": "A very memory efficient library to parse (large) JSON objects on embedded devices",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/squix78/json-streaming-parser.git"
|
||||
},
|
||||
"authors": {
|
||||
"name": "Daniel Eichhorn",
|
||||
"url": "http://blog.squix.ch"
|
||||
},
|
||||
"version": "1.0.5",
|
||||
"frameworks": "arduino",
|
||||
"platforms": "*",
|
||||
"examples": [
|
||||
"examples/JsonStreamingParser/*.ino",
|
||||
"examples/JsonStreamingParser/*.cpp",
|
||||
"examples/JsonStreamingParser/*.h"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
name=Json Streaming Parser
|
||||
version=1.0.5
|
||||
author=Daniel Eichhorn
|
||||
maintainer=Daniel Eichhorn <squix78@gmail.com>
|
||||
sentence=A very memory efficient library to parse (large) JSON objects on small devices
|
||||
paragraph=A very memory efficient library to parse (large) JSON objects on small devices
|
||||
category=Data Processing
|
||||
url=https://github.com/squix78/json-streaming-parser.git
|
||||
architectures=*
|
||||
Reference in New Issue
Block a user