-
-
Save maurice-schuppe/db1f8de1bf3d7d1918c5c8218b963712 to your computer and use it in GitHub Desktop.
JSON parser in modern C++
This file contains hidden or 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
#include "json.hpp" | |
namespace json { | |
Wrapper::Wrapper(Value const &value) : v{value} {} | |
Wrapper::Wrapper(Value &value) : v{value} {} | |
Wrapper::operator Value &() { return v; } | |
Wrapper::operator Value const &() const { return v; } | |
Value & | |
Wrapper::value() { | |
return v; | |
} | |
Value const & | |
Wrapper::value() const { | |
return v; | |
} | |
std::optional<Array> | |
detail::JSONParser::parseArray() { | |
Array array; | |
if (!accept('[')) | |
return {}; | |
if (expect(']')) | |
return array; | |
while (true) { | |
if (auto v = parseValue(); v) { | |
array.emplace_back(Wrapper(v.value())); | |
if (expect(',')) | |
continue; | |
if (expect(']')) | |
return array; | |
return {}; | |
} | |
return {}; | |
} | |
} | |
std::optional<Object> | |
detail::JSONParser::parseObject() { | |
Object object; | |
if (!accept('{')) | |
return {}; | |
if (expect('}')) | |
return object; | |
while (true) { | |
if (auto s = parseString(); s) { | |
if (!expect(':')) | |
return {}; | |
if (auto v = parseValue(); v) | |
object.emplace(s.value(), Wrapper(v.value())); | |
else | |
return {}; | |
if (expect(',')) | |
continue; | |
if (expect('}')) | |
return object; | |
return {}; | |
} | |
return {}; | |
} | |
} | |
std::optional<String> | |
detail::JSONParser::parseString() { | |
if (!accept('\"')) | |
return {}; | |
auto ptr = std::next(this->ptr); | |
unsigned length{0}; | |
while (true) { | |
advance(); | |
if (*(this->ptr - 1) != '\\' && *(this->ptr) == '"') | |
return String{ptr, length}; | |
++length; | |
} | |
return {}; | |
} | |
std::optional<Number> | |
detail::JSONParser::parseNumber() { | |
auto ptr = this->ptr; | |
accept('-'); | |
if (!check(::isdigit)) | |
return {}; | |
while (check(::isdigit)) | |
advance(); | |
if (accept('.')) { | |
if (!check(::isdigit)) | |
return {}; | |
while (check(::isdigit)) | |
advance(); | |
} | |
if (accept('e') || accept('E')) { | |
accept('+') || accept('-'); | |
if (!check(::isdigit)) | |
return {}; | |
while (check(::isdigit)) | |
advance(); | |
} | |
return std::stod( | |
std::string{ptr, static_cast<unsigned long>(this->ptr - ptr)}); | |
} | |
std::optional<Value> | |
detail::JSONParser::parseValue() { | |
auto matches = [](String string) { | |
return [&string](char &c) { return string.compare(&c) == 0; }; | |
}; | |
if (check(matches("true"))) { | |
advance(4); | |
return bool{true}; | |
} | |
if (check(matches("false"))) { | |
advance(5); | |
return bool{true}; | |
} | |
if (check(matches("null"))) { | |
advance(4); | |
return Null{}; | |
} | |
if (auto n = parseNumber(); n) | |
return n.value(); | |
if (auto s = parseString(); s) | |
return s.value(); | |
if (auto o = parseObject(); o) | |
return o.value(); | |
if (auto a = parseArray(); a) | |
return a.value(); | |
return {}; | |
} | |
detail::JSONParser::JSONParser(std::string const &str) | |
: ptr{nullptr}, data{str}, obj{parseObject()} {} | |
bool | |
detail::JSONParser::accept(char c) { | |
bool result = (*ptr == c); | |
ptr += result; | |
return result; | |
} | |
bool | |
detail::JSONParser::expect(char c) { | |
return (*ptr == c); | |
} | |
void | |
detail::JSONParser::advance(int i) { | |
ptr += i; | |
} | |
detail::JSONParser::operator std::optional<Object> &() { return obj; } | |
detail::JSONParser::operator std::optional<Object> const &() const { | |
return obj; | |
} | |
} // end namespace json |
This file contains hidden or 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
// WIP -- not done yet :) | |
#include <iomanip> | |
#include <iostream> | |
#include <optional> | |
#include <string> | |
#include <string_view> | |
#include <unordered_map> | |
#include <variant> | |
#include <vector> | |
namespace json { | |
struct Wrapper; | |
using Null = std::nullptr_t; | |
using Bool = bool; | |
using Number = double; | |
using String = std::string_view; | |
using Array = std::vector<Wrapper>; | |
using Object = std::unordered_map<String, Wrapper>; | |
using Value = std::variant<Bool, Number, String, Array, Object, Null>; | |
struct Wrapper { | |
Wrapper() = default; | |
Wrapper(Wrapper &&) = default; | |
Wrapper(Wrapper const &) = default; | |
Wrapper(Value &value); | |
Wrapper(Value const &value); | |
Wrapper & | |
operator=(Wrapper const &) = default; | |
operator Value &(); | |
operator const Value &() const; | |
Value & | |
value(); | |
Value const & | |
value() const; | |
private: | |
Value v{Null{}}; | |
}; | |
namespace detail { | |
struct JSONParser { | |
JSONParser() = delete; | |
JSONParser(std::string const &); | |
JSONParser(JSONParser const &) = default; | |
JSONParser(JSONParser &&) = default; | |
JSONParser & | |
operator=(JSONParser const &) = default; | |
std::optional<Object> & | |
object(); | |
std::optional<Object> const & | |
object() const; | |
operator std::optional<Object> &(); | |
operator std::optional<Object> const &() const; | |
private: | |
std::optional<Array> | |
parseArray(); | |
std::optional<Object> | |
parseObject(); | |
std::optional<String> | |
parseString(); | |
std::optional<Number> | |
parseNumber(); | |
std::optional<Value> | |
parseValue(); | |
bool | |
accept(char); | |
bool | |
expect(char); | |
void | |
advance(int i = 1); | |
template <typename Fn> | |
bool | |
check(Fn &&fun) { | |
return fun(*ptr); | |
} | |
std::string::pointer ptr; | |
std::string data; | |
std::optional<Object> obj; | |
}; | |
} | |
using Parser = detail::JSONParser; | |
template <typename T> | |
void | |
print(std::optional<T> const &type) { | |
std::visit( | |
[](auto &&arg) { | |
using Type = std::decay_t<decltype(arg)>; | |
if | |
constexpr(std::is_same_v<Type, Number>) { | |
std::cout << std::get<Number>(arg).value(); | |
} | |
else if | |
constexpr(std::is_same_v<Type, Bool>) { | |
std::cout << std::boolalpha << std::get<Bool>(arg).value(); | |
} | |
else if | |
constexpr(std::is_same_v<Type, Null>) { std::cout << "null"; } | |
else if | |
constexpr(std::is_same_v<Type, String>) { | |
std::cout << '"' << std::quoted(std::get<String>(arg).value()) | |
<< '"'; | |
} | |
else if | |
constexpr(std::is_same_v<Type, Array>) { | |
std::cout << '['; | |
auto const &arr = std::get<Array>(arg).value(); | |
print(arr[0]); | |
for (unsigned int i = 1; i < arr.size(); ++i) { | |
std::cout << ','; | |
print(arr[i]); | |
} | |
std::cout << ']'; | |
} | |
else if | |
constexpr(std::is_same_v<Type, Object>) { | |
std::cout << '{'; | |
auto const &map = std::get<Object>(arg).value(); | |
unsigned int index = 0; | |
for (auto & kv : map) { | |
if (index++ != 0) { | |
std::cout << ','; | |
} | |
auto key = std::optional<String>(kv.first); | |
print(key); | |
std::cout << ':'; | |
print(kv.second); | |
} | |
std::cout << '}'; | |
} | |
else | |
static_assert("Whoops"); | |
}, | |
type.value()); | |
} | |
} // end namespace json |
This file contains hidden or 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
#include <iostream> | |
#include <utility> | |
#include "json.hpp" | |
int | |
main() { | |
// std::string num = "-123.40e-2"; | |
// std::string trueString = "true"; | |
// std::string falseString = "false"; | |
// std::string string = "Hello \"a quote\" world!"; | |
// std::string arrNums = "[1,2,3,4]"; | |
// std::string arrString = "[\"hello\",\"goodbye\"]"; | |
std::string obj = "{\"key\":1.2,\"key2\":true}"; | |
{ | |
std::string object = "{\"key\":1.2,\"key2\":true}"; | |
auto p = json::Parser(object); | |
json::print(p.object()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment