Skip to content

Instantly share code, notes, and snippets.

@gatopeich
Created May 24, 2020 01:02
Show Gist options
  • Save gatopeich/62afe96e2e9616787e73440ffeb8f3dd to your computer and use it in GitHub Desktop.
Save gatopeich/62afe96e2e9616787e73440ffeb8f3dd to your computer and use it in GitHub Desktop.
C++17 in-place JSON parser
// In-place JSON parsing concept by gatopeich 2020
// Taking advantage of C++17 features like std::string_view
// No rights reserved, use at your own risk!
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <string_view>
namespace inplacejson
{
using JSONError = std::runtime_error;
struct InPlaceJSON
{
const char *data;
InPlaceJSON(const char* data) : data{data} {
while (*data && *data <= ' ') ++data;
}
int asInt() { return atol(data); }
float asFloat() { return atof(data); }
std::string_view asString() {
if (*data != '"')
throw JSONError("Not a JSON String");
auto str = data + 1;
for (int len = 0; str[len]; len++) {
if (str[len] == '"')
return std::string_view(str, len);
if (str[len] == '\\' && !str[++len])
break;
}
throw JSONError("Badly terminated JSON String");
}
InPlaceJSON operator[](const std::string_view& key) {
if (*data != '{')
throw JSONError("Not a JSON Object");
InPlaceJSON next {data + 1};
while (*next.data != '}') {
auto ikey = next.asString();
InPlaceJSON colon { ikey.end() + 1 };
if (*colon.data != ':')
throw JSONError("Missing colon after JSON key");
InPlaceJSON value {colon.data + 1};
if (ikey == key)
return value;
next = value.skip();
if (*next.data == ',')
next = InPlaceJSON {next.data + 1};
}
throw JSONError("Key not found");
}
InPlaceJSON skip() {
char* after_number;
if (strtof(data, &after_number) || after_number != data)
return InPlaceJSON(after_number);
if (*data == '"')
return InPlaceJSON {asString().end()+1};
if (*data == '{') {
InPlaceJSON next {data + 1};
while (*next.data != '}') {
auto ikey = next.asString();
InPlaceJSON colon { ikey.end() + 1 };
if (*colon.data != ':')
throw JSONError("Missing colon after JSON key");
InPlaceJSON value {colon.data + 1};
next = value.skip();
if (*next.data == ',')
next = InPlaceJSON {next.data + 1};
}
return InPlaceJSON {next.data + 1};
}
throw JSONError(std::string("Cannot skip: ") + data);
}
// Parser operator[](const int n) {}
};
}
using inplacejson::InPlaceJSON;
int main(int argc, char* argv[])
{
auto input = "{\"string\":\"my-string\",\"number\":123.5,\"boolean\":true,\"list\":[1,2,3]}";
std::clog << InPlaceJSON{input}["string"].asString() << std::endl;
std::clog << InPlaceJSON{input}["number"].asFloat() << std::endl;
std::clog << InPlaceJSON{input}["list"].asFloat() << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment