Created
May 24, 2020 01:02
-
-
Save gatopeich/62afe96e2e9616787e73440ffeb8f3dd to your computer and use it in GitHub Desktop.
C++17 in-place JSON parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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