Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Convert the aJson library to use Wiring-style streams

Currently, the aJson library uses FILE* avr-libc streams, which
does not play well with the Arduino ecosystem. We introduce a new
class aJsonStream (plus two subclasses aJsonClientStream and
aJsonStringStream) that use the Wiring Stream object. This allows
to easily exchange JSON data e.g. over Serial and EthernetClient.

An accompanying example Json_Serial shows how to use this
functionality to send and receive JSON data over Serial.
  • Loading branch information...
commit fa8387fe8237649e80e5505655542a2f91773494 1 parent 9a20c85
@pasky pasky authored
View
99 Examples/Json_Serial/Json_Serial.ino
@@ -0,0 +1,99 @@
+/*
+ Sample sketch communicating over Serial using JSON
+
+ This sketch communicates over serial link with a computer, sending
+ JSON-encoded state of its analog pints every second and accepting
+ per-line JSON messages with desired values of PWM pins on input.
+
+ Circuit:
+ * (Optional) Analog sensors attached to analog pin.
+ * (Optional) LEDs attached to PWM pins 9 and 8.
+
+ created 1 November 2012
+ by Petr Baudis
+
+ https://github.com/interactive-matter/ajson
+ This code is in the public domain.
+ */
+
+#include <aJSON.h>
+
+unsigned long last_print = 0;
+aJsonStream serial_stream(&Serial);
+
+void setup()
+{
+ Serial.begin(9600);
+}
+
+/* Generate message like: { "analog": [0, 200, 400, 600, 800, 1000] } */
+aJsonObject *createMessage()
+{
+ aJsonObject *msg = aJson.createObject();
+
+ int analogValues[6];
+ for (int i = 0; i < 6; i++) {
+ analogValues[i] = analogRead(i);
+ }
+ aJsonObject *analog = aJson.createIntArray(analogValues, 6);
+ aJson.addItemToObject(msg, "analog", analog);
+
+ return msg;
+}
+
+/* Process message like: { "pwm": { "8": 0, "9": 128 } } */
+void processMessage(aJsonObject *msg)
+{
+ aJsonObject *pwm = aJson.getObjectItem(msg, "pwm");
+ if (!pwm) {
+ Serial.println("no pwm data");
+ return;
+ }
+
+ const static int pins[] = { 8, 9 };
+ const static int pins_n = 2;
+ for (int i = 0; i < pins_n; i++) {
+ char pinstr[3];
+ snprintf(pinstr, sizeof(pinstr), "%d", pins[i]);
+
+ aJsonObject *pwmval = aJson.getObjectItem(pwm, pinstr);
+ if (!pwmval) continue; /* Value not provided, ok. */
+ if (pwmval->type != aJson_Int) {
+ Serial.print("invalid data type ");
+ Serial.print(pwmval->type, DEC);
+ Serial.print(" for pin ");
+ Serial.println(pins[i], DEC);
+ continue;
+ }
+
+ Serial.print("setting pin ");
+ Serial.print(pins[i], DEC);
+ Serial.print(" to value ");
+ Serial.println(pwmval->valueint, DEC);
+ analogWrite(pins[i], pwmval->valueint);
+ }
+}
+
+void loop()
+{
+ if (millis() - last_print > 1000) {
+ /* One second elapsed, send message. */
+ aJsonObject *msg = createMessage();
+ aJson.print(msg, &serial_stream);
+ Serial.println(); /* Add newline. */
+ aJson.deleteItem(msg);
+ last_print = millis();
+ }
+
+ if (serial_stream.available()) {
+ /* First, skip any accidental whitespace like newlines. */
+ serial_stream.skip();
+ }
+
+ if (serial_stream.available()) {
+ /* Something real on input, let's take a look. */
+ aJsonObject *msg = aJson.parse(&serial_stream);
+ processMessage(msg);
+ aJson.deleteItem(msg);
+ }
+}
View
388 aJSON.cpp
@@ -37,8 +37,8 @@
#include <float.h>
#include <ctype.h>
#include <avr/pgmspace.h>
+
#include "aJSON.h"
-#include "utility/streamhelper.h"
#include "utility/stringbuffer.h"
/******************************************************************************
@@ -50,6 +50,123 @@
//how much digits after . for float
#define FLOAT_PRECISION 5
+
+bool
+aJsonStream::available()
+{
+ if (bucket != EOF)
+ return true;
+ while (stream()->available())
+ {
+ /* Make an effort to skip whitespace. */
+ int ch = this->getch();
+ if (ch > 32)
+ {
+ this->ungetch(ch);
+ return true;
+ }
+ }
+ return false;
+}
+
+int
+aJsonStream::getch()
+{
+ if (bucket != EOF)
+ {
+ int ret = bucket;
+ bucket = EOF;
+ return ret;
+ }
+ while (!stream()->available()) /* spin */;
+ return stream()->read();
+}
+
+void
+aJsonStream::ungetch(char ch)
+{
+ bucket = ch;
+}
+
+size_t
+aJsonStream::write(uint8_t ch)
+{
+ return stream()->write(ch);
+}
+
+size_t
+aJsonStream::readBytes(uint8_t *buffer, size_t len)
+{
+ for (size_t i = 0; i < len; i++)
+ {
+ int ch = this->getch();
+ if (ch == EOF)
+ {
+ return i;
+ }
+ buffer[i] = ch;
+ }
+ return len;
+}
+
+
+int
+aJsonClientStream::getch()
+{
+ if (bucket != EOF)
+ {
+ int ret = bucket;
+ bucket = EOF;
+ return ret;
+ }
+ while (!stream()->available() && stream()->connected()) /* spin */;
+ if (!stream()->connected())
+ {
+ stream()->stop();
+ return EOF;
+ }
+ return stream()->read();
+}
+
+bool
+aJsonStringStream::available()
+{
+ if (bucket != EOF)
+ return true;
+ return inbuf_len > 0;
+}
+
+int
+aJsonStringStream::getch()
+{
+ if (bucket != EOF)
+ {
+ int ret = bucket;
+ bucket = EOF;
+ return ret;
+ }
+ if (!inbuf || !inbuf_len)
+ {
+ return EOF;
+ }
+ char ch = *inbuf++;
+ inbuf_len--;
+ return ch;
+}
+
+size_t
+aJsonStringStream::write(uint8_t ch)
+{
+ if (!outbuf || outbuf_len <= 1)
+ {
+ return 0;
+ }
+ *outbuf++ = ch; outbuf_len--;
+ *outbuf = 0;
+ return 1;
+}
+
+
// Internal constructor.
aJsonObject*
aJsonClass::newItem()
@@ -87,12 +204,12 @@ aJsonClass::deleteItem(aJsonObject *c)
// Parse the input text to generate a number, and populate the result into item.
int
-aJsonClass::parseNumber(aJsonObject *item, FILE* stream)
+aJsonStream::parseNumber(aJsonObject *item)
{
int i = 0;
char sign = 1;
- int in = fgetc(stream);
+ int in = this->getch();
if (in == EOF)
{
return EOF;
@@ -103,7 +220,7 @@ aJsonClass::parseNumber(aJsonObject *item, FILE* stream)
{
//it is a negative number
sign = -1;
- in = fgetc(stream);
+ in = this->getch();
if (in == EOF)
{
return EOF;
@@ -113,7 +230,7 @@ aJsonClass::parseNumber(aJsonObject *item, FILE* stream)
do
{
i = (i * 10) + (in - '0');
- in = fgetc(stream);
+ in = this->getch();
}
while (in >= '0' && in <= '9'); // Number?
//end of integer part Ð or isn't it?
@@ -131,30 +248,30 @@ aJsonClass::parseNumber(aJsonObject *item, FILE* stream)
char signsubscale = 1;
if (in == '.')
{
- in = fgetc(stream);
+ in = this->getch();
do
{
n = (n * 10.0) + (in - '0'), scale--;
- in = fgetc(stream);
+ in = this->getch();
}
while (in >= '0' && in <= '9');
} // Fractional part?
if (in == 'e' || in == 'E') // Exponent?
{
- in = fgetc(stream);
+ in = this->getch();
if (in == '+')
{
- in = fgetc(stream);
+ in = this->getch();
}
else if (in == '-')
{
signsubscale = -1;
- in = fgetc(stream);
+ in = this->getch();
}
while (in >= '0' && in <= '9')
{
subscale = (subscale * 10) + (in - '0'); // Number?
- in = fgetc(stream);
+ in = this->getch();
}
}
@@ -165,35 +282,36 @@ aJsonClass::parseNumber(aJsonObject *item, FILE* stream)
item->type = aJson_Float;
}
//preserve the last character for the next routine
- ungetc(in, stream);
+ this->ungetch(in);
return 0;
}
// Render the number nicely from the given item into a string.
int
-aJsonClass::printInt(aJsonObject *item, FILE* stream)
+aJsonStream::printInt(aJsonObject *item)
{
if (item != NULL)
{
- return fprintf_P(stream, PSTR("%d"), item->valueint);
+ return this->print(item->valueint, DEC);
}
//printing nothing is ok
return 0;
}
int
-aJsonClass::printFloat(aJsonObject *item, FILE* stream)
+aJsonStream::printFloat(aJsonObject *item)
{
if (item != NULL)
{
double d = item->valuefloat;
if (d<0.0) {
- fprintf_P(stream,PSTR("-"));
+ this->print("-");
d=-d;
}
//print the integer part
unsigned long integer_number = (unsigned long)d;
- fprintf_P(stream,PSTR("%u."),integer_number);
+ this->print(integer_number, DEC);
+ this->print(".");
//print the fractional part
double fractional_part = d - ((double)integer_number);
//we do a do-while since we want to print at least one zero
@@ -206,7 +324,7 @@ aJsonClass::printFloat(aJsonObject *item, FILE* stream)
//create an int out of it
unsigned int digit = (unsigned int) fractional_part;
//print it
- fprintf_P(stream,PSTR("%u"),digit);
+ this->print(digit, DEC);
//remove it from the number
fractional_part -= (double)digit;
n--;
@@ -218,10 +336,10 @@ aJsonClass::printFloat(aJsonObject *item, FILE* stream)
// Parse the input text into an unescaped cstring, and populate item.
int
-aJsonClass::parseString(aJsonObject *item, FILE* stream)
+aJsonStream::parseString(aJsonObject *item)
{
//we do not need to skip here since the first byte should be '\"'
- int in = fgetc(stream);
+ int in = this->getch();
if (in != '\"')
{
return EOF; // not a string!
@@ -234,7 +352,7 @@ aJsonClass::parseString(aJsonObject *item, FILE* stream)
//unable to allocate the string
return EOF;
}
- in = fgetc(stream);
+ in = this->getch();
if (in == EOF)
{
stringBufferFree(buffer);
@@ -250,7 +368,7 @@ aJsonClass::parseString(aJsonObject *item, FILE* stream)
}
else
{
- in = fgetc(stream);
+ in = this->getch();
if (in == EOF)
{
stringBufferFree(buffer);
@@ -284,7 +402,7 @@ aJsonClass::parseString(aJsonObject *item, FILE* stream)
break;
}
}
- in = fgetc(stream);
+ in = this->getch();
if (in == EOF)
{
stringBufferFree(buffer);
@@ -301,12 +419,9 @@ aJsonClass::parseString(aJsonObject *item, FILE* stream)
// Render the cstring provided to an escaped version that can be printed.
int
-aJsonClass::printStringPtr(const char *str, FILE* stream)
+aJsonStream::printStringPtr(const char *str)
{
- if (fputc('\"', stream) == EOF)
- {
- return EOF;
- }
+ this->print("\"");
char* ptr = (char*) str;
if (ptr != NULL)
{
@@ -314,61 +429,34 @@ aJsonClass::printStringPtr(const char *str, FILE* stream)
{
if ((unsigned char) *ptr > 31 && *ptr != '\"' && *ptr != '\\')
{
- if (fputc(*ptr, stream) == EOF)
- {
- return EOF;
- }
+ this->print(*ptr);
ptr++;
}
else
{
- if (fputc('\\', stream) == EOF)
- {
- return EOF;
- }
+ this->print('\\');
switch (*ptr++)
{
case '\\':
- if (fputc('\\', stream) == EOF)
- {
- return EOF;
- }
+ this->print('\\');
break;
case '\"':
- if (fputc('\"', stream) == EOF)
- {
- return EOF;
- }
+ this->print('\"');
break;
case '\b':
- if (fputc('b', stream) == EOF)
- {
- return EOF;
- }
+ this->print('b');
break;
case '\f':
- if (fputc('f', stream) == EOF)
- {
- return EOF;
- }
+ this->print('f');
break;
case '\n':
- if (fputc('n', stream) == EOF)
- {
- return EOF;
- }
+ this->print('n');
break;
case '\r':
- if (fputc('r', stream) == EOF)
- {
- return EOF;
- }
+ this->print('r');
break;
case '\t':
- if (fputc('t', stream) == EOF)
- {
- return EOF;
- }
+ this->print('t');
break;
default:
break; // eviscerate with prejudice.
@@ -377,39 +465,29 @@ aJsonClass::printStringPtr(const char *str, FILE* stream)
}
}
- if (fputc('\"', stream) == EOF)
- {
- return EOF;
- }
+ this->print('\"');
return 0;
}
// Invote print_string_ptr (which is useful) on an item.
int
-aJsonClass::printString(aJsonObject *item, FILE* stream)
+aJsonStream::printString(aJsonObject *item)
{
- return printStringPtr(item->valuestring, stream);
+ return this->printStringPtr(item->valuestring);
}
// Utility to jump whitespace and cr/lf
int
-aJsonClass::skip(FILE* stream)
+aJsonStream::skip()
{
- if (stream == NULL)
- {
- return EOF;
- }
- int in = fgetc(stream);
+ int in = this->getch();
while (in != EOF && (in <= 32))
{
- in = fgetc(stream);
+ in = this->getch();
}
if (in != EOF)
{
- if (ungetc(in, stream) == EOF)
- {
- return EOF;
- }
+ this->ungetch(in);
return 0;
}
return EOF;
@@ -419,22 +497,21 @@ aJsonClass::skip(FILE* stream)
aJsonObject*
aJsonClass::parse(char *value)
{
- FILE* string_input_stream = openStringInputStream(value);
- aJsonObject* result = parse(string_input_stream, NULL);
- closeStringInputStream(string_input_stream);
+ aJsonStringStream stringStream(value, NULL);
+ aJsonObject* result = parse(&stringStream);
return result;
}
// Parse an object - create a new root, and populate.
aJsonObject*
-aJsonClass::parse(FILE* stream)
+aJsonClass::parse(aJsonStream* stream)
{
return parse(stream, NULL);
}
// Parse an object - create a new root, and populate.
aJsonObject*
-aJsonClass::parse(FILE* stream, char** filter)
+aJsonClass::parse(aJsonStream* stream, char** filter)
{
if (stream == NULL)
{
@@ -444,8 +521,8 @@ aJsonClass::parse(FILE* stream, char** filter)
if (!c)
return NULL; /* memory fail */
- skip(stream);
- if (parseValue(c, stream, filter) == EOF)
+ stream->skip();
+ if (stream->parseValue(c, filter) == EOF)
{
deleteItem(c);
return NULL;
@@ -455,60 +532,54 @@ aJsonClass::parse(FILE* stream, char** filter)
// Render a aJsonObject item/entity/structure to text.
int
-aJsonClass::print(aJsonObject* item, FILE* stream)
+aJsonClass::print(aJsonObject* item, aJsonStream* stream)
{
- return printValue(item, stream);
+ return stream->printValue(item);
}
char*
aJsonClass::print(aJsonObject* item)
{
- FILE* stream = openStringOutputStream();
- if (stream == NULL)
+ char* outBuf = (char*) malloc(256); /* XXX: Dynamic size. */
+ if (outBuf == NULL)
{
return NULL;
}
- print(item, stream);
- return closeStringOutputStream(stream);
+ aJsonStringStream stringStream(NULL, outBuf, 256);
+ print(item, &stringStream);
+ return outBuf;
}
// Parser core - when encountering text, process appropriately.
int
-aJsonClass::parseValue(aJsonObject *item, FILE* stream, char** filter)
+aJsonStream::parseValue(aJsonObject *item, char** filter)
{
- if (stream == NULL)
- {
- return EOF; // Fail on null.
- }
- if (skip(stream) == EOF)
+ if (this->skip() == EOF)
{
return EOF;
}
//read the first byte from the stream
- int in = fgetc(stream);
+ int in = this->getch();
if (in == EOF)
{
return EOF;
}
- if (ungetc(in, stream) == EOF)
- {
- return EOF;
- }
+ this->ungetch(in);
if (in == '\"')
{
- return parseString(item, stream);
+ return this->parseString(item);
}
else if (in == '-' || (in >= '0' && in <= '9'))
{
- return parseNumber(item, stream);
+ return this->parseNumber(item);
}
else if (in == '[')
{
- return parseArray(item, stream, filter);
+ return this->parseArray(item, filter);
}
else if (in == '{')
{
- return parseObject(item, stream, filter);
+ return this->parseObject(item, filter);
}
//it can only be null, false or true
else if (in == 'n')
@@ -516,7 +587,7 @@ aJsonClass::parseValue(aJsonObject *item, FILE* stream, char** filter)
//a buffer to read the value
char buffer[] =
{ 0, 0, 0, 0 };
- if (fread(buffer, sizeof(char), 4, stream) != 4)
+ if (this->readBytes((uint8_t*) buffer, 4) != 4)
{
return EOF;
}
@@ -535,7 +606,7 @@ aJsonClass::parseValue(aJsonObject *item, FILE* stream, char** filter)
//a buffer to read the value
char buffer[] =
{ 0, 0, 0, 0, 0 };
- if (fread(buffer, sizeof(char), 5, stream) != 5)
+ if (this->readBytes((uint8_t*) buffer, 5) != 5)
{
return EOF;
}
@@ -551,7 +622,7 @@ aJsonClass::parseValue(aJsonObject *item, FILE* stream, char** filter)
//a buffer to read the value
char buffer[] =
{ 0, 0, 0, 0 };
- if (fread(buffer, sizeof(char), 4, stream) != 4)
+ if (this->readBytes((uint8_t*) buffer, 4) != 4)
{
return EOF;
}
@@ -568,7 +639,7 @@ aJsonClass::parseValue(aJsonObject *item, FILE* stream, char** filter)
// Render a value to text.
int
-aJsonClass::printValue(aJsonObject *item, FILE* stream)
+aJsonStream::printValue(aJsonObject *item)
{
int result = 0;
if (item == NULL)
@@ -579,60 +650,58 @@ aJsonClass::printValue(aJsonObject *item, FILE* stream)
switch (item->type)
{
case aJson_NULL:
- result = fprintf_P(stream, PSTR("null"));
+ result = this->print("null");
break;
case aJson_False:
- result = fprintf_P(stream, PSTR("false"));
+ result = this->print("false");
break;
case aJson_True:
- result = fprintf_P(stream, PSTR("true"));
+ result = this->print("true");
break;
case aJson_Int:
- result = printInt(item, stream);
+ result = this->printInt(item);
break;
case aJson_Float:
- result = printFloat(item, stream);
+ result = this->printFloat(item);
break;
case aJson_String:
- result = printString(item, stream);
+ result = this->printString(item);
break;
case aJson_Array:
- result = printArray(item, stream);
+ result = this->printArray(item);
break;
case aJson_Object:
- result = printObject(item, stream);
+ result = this->printObject(item);
break;
}
- //good time to flush the stream
- fflush(stream);
return result;
}
// Build an array from input text.
int
-aJsonClass::parseArray(aJsonObject *item, FILE* stream, char** filter)
+aJsonStream::parseArray(aJsonObject *item, char** filter)
{
- int in = fgetc(stream);
+ int in = this->getch();
if (in != '[')
{
return EOF; // not an array!
}
item->type = aJson_Array;
- skip(stream);
- in = fgetc(stream);
+ this->skip();
+ in = this->getch();
//check for empty array
if (in == ']')
{
return 0; // empty array.
}
//now put back the last character
- ungetc(in, stream);
+ this->ungetch(in);
aJsonObject *child;
char first = -1;
while ((first) || (in == ','))
{
- aJsonObject *new_item = newItem();
+ aJsonObject *new_item = aJsonClass::newItem();
if (new_item == NULL)
{
return EOF; // memory fail
@@ -648,13 +717,13 @@ aJsonClass::parseArray(aJsonObject *item, FILE* stream, char** filter)
new_item->prev = child;
}
child = new_item;
- skip(stream);
- if (parseValue(child, stream, filter))
+ this->skip();
+ if (this->parseValue(child, filter))
{
return EOF;
}
- skip(stream);
- in = fgetc(stream);
+ this->skip();
+ in = this->getch();
}
if (in == ']')
{
@@ -668,7 +737,7 @@ aJsonClass::parseArray(aJsonObject *item, FILE* stream, char** filter)
// Render an array to text
int
-aJsonClass::printArray(aJsonObject *item, FILE* stream)
+aJsonStream::printArray(aJsonObject *item)
{
if (item == NULL)
{
@@ -676,26 +745,26 @@ aJsonClass::printArray(aJsonObject *item, FILE* stream)
return 0;
}
aJsonObject *child = item->child;
- if (fputc('[', stream) == EOF)
+ if (this->print('[') == EOF)
{
return EOF;
}
while (child)
{
- if (printValue(child, stream) == EOF)
+ if (this->printValue(child) == EOF)
{
return EOF;
}
child = child->next;
if (child)
{
- if (fputc(',', stream) == EOF)
+ if (this->print(',') == EOF)
{
return EOF;
}
}
}
- if (fputc(']', stream) == EOF)
+ if (this->print(']') == EOF)
{
return EOF;
}
@@ -704,31 +773,30 @@ aJsonClass::printArray(aJsonObject *item, FILE* stream)
// Build an object from the text.
int
-aJsonClass::parseObject(aJsonObject *item, FILE* stream, char** filter)
+aJsonStream::parseObject(aJsonObject *item, char** filter)
{
- int in = fgetc(stream);
+ int in = this->getch();
if (in != '{')
{
return EOF; // not an object!
}
item->type = aJson_Object;
- skip(stream);
+ this->skip();
//check for an empty object
- in = fgetc(stream);
+ in = this->getch();
if (in == '}')
{
return 0; // empty object.
}
//preserve the char for the next parser
- ungetc(in, stream);
+ this->ungetch(in);
aJsonObject* child;
char first = -1;
while ((first) || (in == ','))
{
- //in = fgetc(stream);
- aJsonObject* new_item = newItem();
+ aJsonObject* new_item = aJsonClass::newItem();
if (new_item == NULL)
{
return EOF; // memory fail
@@ -744,28 +812,28 @@ aJsonClass::parseObject(aJsonObject *item, FILE* stream, char** filter)
new_item->prev = child;
}
child = new_item;
- skip(stream);
- if (parseString(child, stream) == EOF)
+ this->skip();
+ if (this->parseString(child) == EOF)
{
return EOF;
}
- skip(stream);
+ this->skip();
child->name = child->valuestring;
child->valuestring = NULL;
- in = fgetc(stream);
+ in = this->getch();
if (in != ':')
{
return EOF; // fail!
}
// skip any spacing, get the value.
- skip(stream);
- if (parseValue(child, stream, filter) == EOF)
+ this->skip();
+ if (this->parseValue(child, filter) == EOF)
{
return EOF;
}
- skip(stream);
- in = fgetc(stream);
+ this->skip();
+ in = this->getch();
}
if (in == '}')
@@ -780,7 +848,7 @@ aJsonClass::parseObject(aJsonObject *item, FILE* stream, char** filter)
// Render an object to text.
int
-aJsonClass::printObject(aJsonObject *item, FILE* stream)
+aJsonStream::printObject(aJsonObject *item)
{
if (item == NULL)
{
@@ -788,34 +856,34 @@ aJsonClass::printObject(aJsonObject *item, FILE* stream)
return 0;
}
aJsonObject *child = item->child;
- if (fputc('{', stream) == EOF)
+ if (this->print('{') == EOF)
{
return EOF;
}
while (child)
{
- if (printStringPtr(child->name, stream) == EOF)
+ if (this->printStringPtr(child->name) == EOF)
{
return EOF;
}
- if (fputc(':', stream) == EOF)
+ if (this->print(':') == EOF)
{
return EOF;
}
- if (printValue(child, stream) == EOF)
+ if (this->printValue(child) == EOF)
{
return EOF;
}
child = child->next;
if (child)
{
- if (fputc(',', stream) == EOF)
+ if (this->print(',') == EOF)
{
return EOF;
}
}
}
- if (fputc('}', stream) == EOF)
+ if (this->print('}') == EOF)
{
return EOF;
}
View
144 aJSON.h
@@ -25,11 +25,9 @@
#ifndef aJson__h
#define aJson__h
-/******************************************************************************
- * Includes
- ******************************************************************************/
-#include <stdio.h>
-
+#include <Print.h>
+#include <Stream.h>
+#include <Client.h>
/******************************************************************************
* Definitions
@@ -46,6 +44,10 @@
#define aJson_IsReference 128
+#ifndef EOF
+#define EOF -1
+#endif
+
// The aJson structure:
typedef struct aJsonObject {
char *name; // The item's name string, if this item is the child of, or is in the list of subitems of an object.
@@ -62,6 +64,106 @@ typedef struct aJsonObject {
};
} aJsonObject;
+/* aJsonStream is stream representation of aJson for its internal use;
+ * it is meant to abstract out differences between Stream (e.g. serial
+ * stream) and Client (which may or may not be connected) or provide even
+ * stream-ish interface to string buffers. */
+class aJsonStream : public Print {
+public:
+ aJsonStream(Stream *stream_)
+ : stream_obj(stream_), bucket(EOF)
+ {}
+ /* Use this to check if more data is available, as aJsonStream
+ * can read some more data than really consumed and automatically
+ * skips separating whitespace if you use this method. */
+ virtual bool available();
+
+ int parseNumber(aJsonObject *item);
+ int printInt(aJsonObject *item);
+ int printFloat(aJsonObject *item);
+
+ int parseString(aJsonObject *item);
+ int printStringPtr(const char *str);
+ int printString(aJsonObject *item);
+
+ int skip();
+
+ int parseValue(aJsonObject *item, char** filter);
+ int printValue(aJsonObject *item);
+
+ int parseArray(aJsonObject *item, char** filter);
+ int printArray(aJsonObject *item);
+
+ int parseObject(aJsonObject *item, char** filter);
+ int printObject(aJsonObject *item);
+
+protected:
+ /* Blocking load of character, returning EOF if the stream
+ * is exhausted. */
+ /* Base implementation just looks at bucket, returns EOF
+ * otherwise; descendats take care of the real reading. */
+ virtual int getch();
+ virtual size_t readBytes(uint8_t *buffer, size_t len);
+ /* Return the character back to the front of the stream
+ * after loading it with getch(). Only returning a single
+ * character is supported. */
+ virtual void ungetch(char ch);
+
+ /* Inherited from class Print. */
+ virtual size_t write(uint8_t ch);
+
+ /* stream attribute is used only from virtual functions,
+ * therefore an object inheriting aJsonStream may avoid
+ * using streams completely. */
+ Stream *stream_obj;
+ /* Use this accessor for stream retrieval; some subclasses
+ * may use their own stream subclass. */
+ virtual inline Stream *stream() { return stream_obj; }
+
+ /* bucket is EOF by default. Otherwise, it is a character
+ * to be returned by next getch() - returned by a call
+ * to ungetch(). */
+ int bucket;
+};
+
+/* JSON stream that consumes data from a connection (usually
+ * Ethernet client) until the connection is closed. */
+class aJsonClientStream : public aJsonStream {
+public:
+ aJsonClientStream(Client *stream_)
+ : aJsonStream(NULL), client_obj(stream_)
+ {}
+
+private:
+ virtual int getch();
+
+ Client *client_obj;
+ virtual inline Client *stream() { return client_obj; }
+};
+
+/* JSON stream that is bound to input and output string buffer. This is
+ * for internal usage by string-based aJsonClass methods. */
+/* TODO: Elastic output buffer support. */
+class aJsonStringStream : public aJsonStream {
+public:
+ /* Either of inbuf, outbuf can be NULL if you do not care about
+ * particular I/O direction. */
+ aJsonStringStream(char *inbuf_, char *outbuf_ = NULL, size_t outbuf_len_ = 0)
+ : aJsonStream(NULL), inbuf(inbuf_), outbuf(outbuf_), outbuf_len(outbuf_len_)
+ {
+ inbuf_len = inbuf ? strlen(inbuf) : 0;
+ }
+
+ virtual bool available();
+
+private:
+ virtual int getch();
+ virtual size_t write(uint8_t ch);
+
+ char *inbuf, *outbuf;
+ size_t inbuf_len, outbuf_len;
+};
+
class aJsonClass {
/******************************************************************************
* Constructors
@@ -72,14 +174,14 @@ class aJsonClass {
******************************************************************************/
public:
// Supply a block of JSON, and this returns a aJson object you can interrogate. Call aJson.deleteItem when finished.
- aJsonObject* parse(FILE* stream); //Reads from a stream
- aJsonObject* parse(FILE* stream,char** filter_values); //Read from a file, but only return values include in the char* array filter_values
+ aJsonObject* parse(aJsonStream* stream); //Reads from a stream
+ aJsonObject* parse(aJsonStream* stream,char** filter_values); //Read from a file, but only return values include in the char* array filter_values
aJsonObject* parse(char *value); //Reads from a string
// Render a aJsonObject entity to text for transfer/storage. Free the char* when finished.
- int print(aJsonObject *item, FILE* stream);
+ int print(aJsonObject *item, aJsonStream* stream);
char* print(aJsonObject* item);
//Renders a aJsonObject directly to a output stream
- char stream(aJsonObject *item, FILE* stream);
+ char stream(aJsonObject *item, aJsonStream* stream);
// Delete a aJsonObject entity and all sub-entities.
void deleteItem(aJsonObject *c);
@@ -136,30 +238,14 @@ class aJsonClass {
void addStringToObject(aJsonObject* object, const char* name,
const char* s);
-private:
- aJsonObject* newItem();
- int parseNumber(aJsonObject *item, FILE* stream);
- int printInt(aJsonObject *item, FILE* stream);
- int printFloat(aJsonObject *item, FILE* stream);
-
- int parseString(aJsonObject *item, FILE* stream);
- int printStringPtr(const char *str, FILE* stream);
- int printString(aJsonObject *item, FILE* stream);
-
- int skip(FILE* stream);
-
- int parseValue(aJsonObject *item, FILE* stream, char** filter);
- int printValue(aJsonObject *item, FILE* stream);
+protected:
+ friend aJsonStream;
+ static aJsonObject* newItem();
- int parseArray(aJsonObject *item, FILE* stream, char** filter);
- int printArray(aJsonObject *item, FILE* stream);
-
- int parseObject(aJsonObject *item, FILE* stream, char** filter);
- int printObject(aJsonObject *item, FILE* stream);
+private:
void suffixObject(aJsonObject *prev, aJsonObject *item);
aJsonObject* createReference(aJsonObject *item);
-
};
extern aJsonClass aJson;
View
142 utility/streamhelper.c
@@ -1,142 +0,0 @@
-/*
- * aJson
- * streamhelper.c
- *
- * http://interactive-matter.org/
- *
- * This file is part of aJson.
- *
- * aJson is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * aJson is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with aJson. If not, see <http://www.gnu.org/licenses/>.
- *
- * Created on: 10.10.2010
- * Author: marcus
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include "streamhelper.h"
-#include "stringbuffer.h"
-
-//internal prototypes
-int
-stringGet(FILE* stream);
-int
-stringPut(char c, FILE* stream);
-
-typedef struct
-{
- char* string;
- unsigned int position;
-} string_input_stream_info;
-
-FILE*
-openStringInputStream(char* string)
-{
- FILE* result = fdevopen(NULL, &stringGet);
- if (result == NULL)
- {
- return NULL;
- }
- string_input_stream_info* udata = malloc(sizeof(string_input_stream_info));
- if (udata != NULL)
- {
- udata->string = string;
- udata->position = 0;
- fdev_set_udata(result,udata);
- }
- else
- {
- fclose(result);
- return NULL;
- }
- return result;
-}
-
-void
-closeStringInputStream(FILE* stream)
-{
- if (stream == NULL)
- {
- return;
- }
- string_input_stream_info* udata =
- (string_input_stream_info*) fdev_get_udata(stream);
- if (udata != NULL)
- {
- free(udata);
- }
- fdev_set_udata(stream,NULL);
- fclose(stream);
-}
-
-FILE*
-openStringOutputStream(void)
-{
- FILE* result = fdevopen(&stringPut, NULL);
- if (result == NULL)
- {
- return NULL;
- }
- string_buffer* buffer = stringBufferCreate();
- if (buffer == NULL)
- {
- fclose(result);
- return NULL;
- }
- fdev_set_udata(result,buffer);
- return result;
-}
-
-char*
-closeStringOutputStream(FILE* stream)
-{
- //write a 0 to the end - that is how a string looks like
- string_buffer* buffer = (string_buffer*) fdev_get_udata(stream);
- char* result = stringBufferToString(buffer);
- if (result == NULL)
- {
- fclose(stream);
- return NULL;
- }
- //free(buffer);
- fdev_set_udata(stream,NULL);
- fclose(stream);
- return result;
-}
-
-int
-stringGet(FILE* stream)
-{
- string_input_stream_info* udata =
- (string_input_stream_info*) fdev_get_udata(stream);
- char result = udata->string[udata->position];
- if (result == 0)
- {
- return EOF;
- }
- else
- {
- udata->position++;
- return result;
- }
-}
-
-int
-stringPut(char c, FILE* stream)
-{
- string_buffer* buffer = (string_buffer*) fdev_get_udata(stream);
- if (stringBufferAdd(c, buffer))
- {
- return EOF;
- }
- return 0;
-}
View
46 utility/streamhelper.h
@@ -1,46 +0,0 @@
-/*
- * aJson
- * streamhelper.h
- *
- * http://interactive-matter.org/
- *
- * This file is part of aJson.
- *
- * aJson is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * aJson is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with aJson. If not, see <http://www.gnu.org/licenses/>.
- *
- * Created on: 10.10.2010
- * Author: marcus
- */
-
-#ifndef STREAMHELPER_H_
-#define STREAMHELPER_H_
-
-#include <stdlib.h>
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
- FILE*
- openStringInputStream(char* string);
- void
- closeStringInputStream(FILE* stream);
- FILE*
- openStringOutputStream(void);
- char*
- closeStringOutputStream(FILE* stream);
-
-#ifdef __cplusplus
-}
-#endif
-#endif /* STREAMHELPER_H_ */
Please sign in to comment.
Something went wrong with that request. Please try again.