mirror of
https://codeberg.org/vyn/rei-json.git
synced 2025-07-01 17:43:18 +00:00
Add Exceptions indicating which line contains error when parsing JSON
This commit is contained in:
parent
fd034eff71
commit
2639dba60a
9 changed files with 307 additions and 177 deletions
|
@ -21,7 +21,7 @@ Include(FetchContent)
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
Catch2
|
Catch2
|
||||||
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||||
GIT_TAG v3.4.0 # or a later release
|
GIT_TAG v3.7.1 # or a later release
|
||||||
)
|
)
|
||||||
|
|
||||||
FetchContent_MakeAvailable(Catch2)
|
FetchContent_MakeAvailable(Catch2)
|
||||||
|
@ -30,6 +30,7 @@ add_executable(tests
|
||||||
tests/usage.cpp
|
tests/usage.cpp
|
||||||
tests/parsing.cpp
|
tests/parsing.cpp
|
||||||
tests/stringify.cpp
|
tests/stringify.cpp
|
||||||
|
tests/errors.cpp
|
||||||
)
|
)
|
||||||
target_include_directories(tests PRIVATE "include")
|
target_include_directories(tests PRIVATE "include")
|
||||||
target_link_libraries(tests PRIVATE rei-json)
|
target_link_libraries(tests PRIVATE rei-json)
|
||||||
|
|
|
@ -14,6 +14,8 @@ namespace rei::json {
|
||||||
|
|
||||||
JsonArray();
|
JsonArray();
|
||||||
|
|
||||||
|
JsonArray& push(FieldValue& value);
|
||||||
|
JsonArray& push(FieldValue&& value);
|
||||||
JsonArray& push(int value);
|
JsonArray& push(int value);
|
||||||
JsonArray& push(bool value);
|
JsonArray& push(bool value);
|
||||||
JsonArray& push(const std::string& value);
|
JsonArray& push(const std::string& value);
|
||||||
|
|
|
@ -16,6 +16,8 @@ namespace rei::json {
|
||||||
|
|
||||||
bool contains(const std::string& key) const;
|
bool contains(const std::string& key) const;
|
||||||
|
|
||||||
|
JsonObject& set(const std::string& key, FieldValue& value);
|
||||||
|
JsonObject& set(const std::string& key, FieldValue&& value);
|
||||||
JsonObject& set(const std::string& key, int value);
|
JsonObject& set(const std::string& key, int value);
|
||||||
JsonObject& set(const std::string& key, bool value);
|
JsonObject& set(const std::string& key, bool value);
|
||||||
JsonObject& set(const std::string& key, const std::string& value);
|
JsonObject& set(const std::string& key, const std::string& value);
|
||||||
|
|
|
@ -2,13 +2,27 @@
|
||||||
#include "Object.h"
|
#include "Object.h"
|
||||||
#include "Array.h"
|
#include "Array.h"
|
||||||
#include "Field.h"
|
#include "Field.h"
|
||||||
|
#include <exception>
|
||||||
|
#include <format>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
|
||||||
namespace rei::json {
|
namespace rei::json {
|
||||||
class std::variant<JsonObject, JsonArray> parse(const std::string& jsonStr);
|
|
||||||
|
|
||||||
std::string toString(std::variant<JsonObject, JsonArray>& element);
|
class ParsingError : public std::exception {
|
||||||
|
public:
|
||||||
|
ParsingError(const std::string& reason, int line, int column) {
|
||||||
|
str = std::format("{}:{}: {}", line, column, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * what() const noexcept override {
|
||||||
|
return str.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string str;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FieldValue parse(const std::string& jsonStr);
|
||||||
|
|
||||||
std::string toString(JsonObject& object);
|
std::string toString(JsonObject& object);
|
||||||
std::string toString(JsonArray& object);
|
std::string toString(JsonArray& object);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
namespace rei::json {
|
namespace rei::json {
|
||||||
|
|
||||||
JsonArray::JsonArray() {
|
JsonArray::JsonArray() {
|
||||||
|
@ -23,6 +24,16 @@ namespace rei::json {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonArray& JsonArray::push(FieldValue& value) {
|
||||||
|
elements.push_back(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonArray& JsonArray::push(FieldValue&& value) {
|
||||||
|
elements.push_back(std::move(value));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
JsonArray& JsonArray::push(bool value) {
|
JsonArray& JsonArray::push(bool value) {
|
||||||
elements.push_back(value);
|
elements.push_back(value);
|
||||||
return *this;
|
return *this;
|
||||||
|
|
|
@ -24,6 +24,16 @@ namespace rei::json {
|
||||||
return elements.contains(key);
|
return elements.contains(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonObject& JsonObject::set(const std::string& key, FieldValue& value) {
|
||||||
|
elements.insert_or_assign(key, value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject& JsonObject::set(const std::string& key, FieldValue&& value) {
|
||||||
|
elements.insert_or_assign(key, std::move(value));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
JsonObject& JsonObject::set(const std::string& key, int value) {
|
JsonObject& JsonObject::set(const std::string& key, int value) {
|
||||||
elements.insert_or_assign(key, value);
|
elements.insert_or_assign(key, value);
|
||||||
return *this;
|
return *this;
|
||||||
|
|
347
src/parse.cpp
347
src/parse.cpp
|
@ -1,202 +1,209 @@
|
||||||
#include "rei-json/Array.h"
|
#include "rei-json/Array.h"
|
||||||
#include "rei-json/Field.h"
|
#include "rei-json/Field.h"
|
||||||
#include "rei-json/json.h"
|
#include "rei-json/json.h"
|
||||||
#include <algorithm>
|
#include <cassert>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
#include <chrono>
|
|
||||||
#include <memory>
|
|
||||||
#include <ostream>
|
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <variant>
|
#include <utility>
|
||||||
namespace rei::json {
|
|
||||||
|
|
||||||
void goToNextDoubleQuote(const std::string& str, int* const i) {
|
namespace rei::json {
|
||||||
while (str[*i] != '"') {
|
|
||||||
if (str[*i] == '\\') {
|
|
||||||
++(*i);
|
|
||||||
}
|
|
||||||
++(*i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void skipDigits(const std::string& str, int* const i) {
|
class StringParser {
|
||||||
while (isdigit(str[*i]) || str[*i] == '-') {
|
|
||||||
++(*i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void skipWhiteSpaces(const std::string& str, int* const i) {
|
public:
|
||||||
while (str[*i] == ' ' || str[*i] == '\t' || str[*i] == '\n' || str[*i] == '\v') {
|
|
||||||
++(*i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void parseObject(const std::string &str, int *i, JsonObject* object);
|
StringParser(const std::string& str) : str(str) { }
|
||||||
void parseArray(const std::string &str, int *i, JsonArray* object);
|
|
||||||
|
|
||||||
void parseObjectField(const std::string &str, int *i, JsonObject* object) {
|
int currentLine() const {
|
||||||
if (str[*i] != '"') {
|
return line;
|
||||||
throw "wrong json: expected opening quote";
|
|
||||||
}
|
|
||||||
++(*i);
|
|
||||||
const int keyStart = *i;
|
|
||||||
goToNextDoubleQuote(str, i);
|
|
||||||
const int keyLength = *i - keyStart;
|
|
||||||
const std::string key = str.substr(keyStart, keyLength);
|
|
||||||
++(*i);
|
|
||||||
skipWhiteSpaces(str, i);
|
|
||||||
if (str[*i] != ':') {
|
|
||||||
throw "wrong json: expect 2 dots";
|
|
||||||
}
|
|
||||||
++(*i);
|
|
||||||
skipWhiteSpaces(str, i);
|
|
||||||
|
|
||||||
if (str[*i] == '"') {
|
|
||||||
++(*i);
|
|
||||||
const int valueStart = *i;
|
|
||||||
goToNextDoubleQuote(str, i);
|
|
||||||
const int valueLength = *i - valueStart;
|
|
||||||
const std::string valueStr = str.substr(valueStart, valueLength);
|
|
||||||
object->set(key, std::move(valueStr));
|
|
||||||
++(*i);
|
|
||||||
} else if (isdigit(str[*i]) || str[*i] == '-') {
|
|
||||||
const int valueStart = *i;
|
|
||||||
skipDigits(str, i);
|
|
||||||
const int valueLength = *i - valueStart;
|
|
||||||
const std::string valueStr = str.substr(valueStart, valueLength);
|
|
||||||
int value;
|
|
||||||
std::from_chars(str.c_str() + valueStart, str.c_str() + valueStart + valueLength, value);
|
|
||||||
object->set(key, value);
|
|
||||||
} else if (std::string_view{str}.substr(*i, 4) == "true") {
|
|
||||||
const int valueStart = *i;
|
|
||||||
*i += 4;
|
|
||||||
const int valueLength = *i - valueStart;
|
|
||||||
object->set(key, true);
|
|
||||||
} else if (std::string_view{str}.substr(*i, 5) == "false") {
|
|
||||||
const int valueStart = *i;
|
|
||||||
*i += 5;
|
|
||||||
const int valueLength = *i - valueStart;
|
|
||||||
object->set(key, false);
|
|
||||||
} else if (std::string_view{str}.substr(*i, 4) == "null") {
|
|
||||||
*i += 4;
|
|
||||||
object->setNull(key);
|
|
||||||
} else if (str[*i] == '{'){
|
|
||||||
JsonObject newObject{};
|
|
||||||
parseObject(str, i, &newObject);
|
|
||||||
object->set(key, std::move(newObject));
|
|
||||||
} else if (str[*i] == '['){
|
|
||||||
JsonArray newArray{};
|
|
||||||
parseArray(str, i, &newArray);
|
|
||||||
object->set(key, std::move(newArray));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void parseArrayField(const std::string &str, int *i, JsonArray* array) {
|
|
||||||
skipWhiteSpaces(str, i);
|
|
||||||
if (str[*i] == '"') {
|
|
||||||
++(*i);
|
|
||||||
const int valueStart = *i;
|
|
||||||
goToNextDoubleQuote(str, i);
|
|
||||||
const int valueLength = *i - valueStart;
|
|
||||||
const std::string valueStr = str.substr(valueStart, valueLength);
|
|
||||||
array->push(std::move(valueStr));
|
|
||||||
++(*i);
|
|
||||||
} else if (isdigit(str[*i])) {
|
|
||||||
const int valueStart = *i;
|
|
||||||
skipDigits(str, i);
|
|
||||||
const int valueLength = *i - valueStart;
|
|
||||||
const std::string valueStr = str.substr(valueStart, valueLength);
|
|
||||||
array->push(std::stoi(valueStr));
|
|
||||||
} else if (std::string_view{str}.substr(*i, 4) == "true") {
|
|
||||||
const int valueStart = *i;
|
|
||||||
*i += 4;
|
|
||||||
const int valueLength = *i - valueStart;
|
|
||||||
array->push(true);
|
|
||||||
} else if (std::string_view{str}.substr(*i, 4) == "null") {
|
|
||||||
*i += 4;
|
|
||||||
array->pushNull();
|
|
||||||
} else if (std::string_view{str}.substr(*i, 5) == "false") {
|
|
||||||
const int valueStart = *i;
|
|
||||||
*i += 5;
|
|
||||||
const int valueLength = *i - valueStart;
|
|
||||||
array->push(false);
|
|
||||||
} else if (str[*i] == '{'){
|
|
||||||
JsonObject newObject{};
|
|
||||||
parseObject(str, i, &newObject);
|
|
||||||
array->push(std::move(newObject));
|
|
||||||
} else if (str[*i] == '['){
|
|
||||||
JsonArray newArray{};
|
|
||||||
parseArray(str, i, &newArray);
|
|
||||||
array->push(std::move(newArray));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
int currentColumn() const {
|
||||||
|
return i - lineStartingIndex;
|
||||||
void parseArray(const std::string &str, int *i, JsonArray* object) {
|
|
||||||
if (str[*i] != '[') {
|
|
||||||
throw "wrong json";
|
|
||||||
}
|
}
|
||||||
++(*i);
|
|
||||||
skipWhiteSpaces(str, i);
|
const char& current() const {
|
||||||
while (str[*i] != ']') {
|
return str[i];
|
||||||
parseArrayField(str, i, object);
|
}
|
||||||
skipWhiteSpaces(str, i);
|
|
||||||
if (str[*i] == ',') {
|
bool is(const char& c) const {
|
||||||
++(*i);
|
return str[i] == c;
|
||||||
skipWhiteSpaces(str, i);
|
}
|
||||||
if (str[*i] == ']') {
|
|
||||||
throw "wrong json: extra comma on last array field";
|
bool match(const char* c, int length) const {
|
||||||
|
return std::string_view{str}.substr(i, length) == c;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDigit() const {
|
||||||
|
return isdigit(str[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void skipWhitespaces() {
|
||||||
|
while (str[i] == ' ' || str[i] == '\t' || str[i] == '\n' || str[i] == '\v') {
|
||||||
|
if (str[i] == '\n') {
|
||||||
|
++line;
|
||||||
|
lineStartingIndex = i + 1;
|
||||||
}
|
}
|
||||||
} else if (str[*i] != ']') {
|
++i;
|
||||||
throw "wrong json: expected array end";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++(*i);
|
|
||||||
}
|
|
||||||
|
|
||||||
void parseObject(const std::string &str, int *i, JsonObject* object) {
|
void goToNextDoubleQuote() {
|
||||||
if (str[*i] != '{') {
|
while (str[i] != '"') {
|
||||||
throw "wrong json";
|
if (str[i] == '\\') {
|
||||||
}
|
++(i);
|
||||||
++(*i);
|
|
||||||
skipWhiteSpaces(str, i);
|
|
||||||
while (str[*i] != '}') {
|
|
||||||
if (str[*i] != '"') {
|
|
||||||
throw "wrong json: expect opening key quote";
|
|
||||||
}
|
|
||||||
parseObjectField(str, i, object);
|
|
||||||
skipWhiteSpaces(str, i);
|
|
||||||
if (str[*i] == ',') {
|
|
||||||
++(*i);
|
|
||||||
skipWhiteSpaces(str, i);
|
|
||||||
if (str[*i] == '}') {
|
|
||||||
throw "wrong json: extra comma on last object field";
|
|
||||||
}
|
}
|
||||||
} else if (str[*i] != '}') {
|
++(i);
|
||||||
const std::string sstr = str.substr(*i - 1);
|
|
||||||
throw "wrong json: expected object end";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++(*i);
|
|
||||||
|
|
||||||
}
|
void skipDigits() {
|
||||||
|
while (isdigit(str[i]) || str[i] == '-') {
|
||||||
|
++(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::variant<JsonObject, JsonArray> parse(const std::string& jsonStr) {
|
void nextChar(int count = 1) {
|
||||||
int i = 0;
|
i += count;
|
||||||
skipWhiteSpaces(jsonStr, &i);
|
}
|
||||||
if (jsonStr[i] == '{') {
|
|
||||||
|
int currentIndex() const {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string substr(int start, int length) const {
|
||||||
|
return str.substr(start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *c_str() const {
|
||||||
|
return str.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldValue parseField(StringParser& parser) {
|
||||||
|
if (parser.is('"')) {
|
||||||
|
parser.nextChar();
|
||||||
|
const int valueStart = parser.currentIndex();
|
||||||
|
parser.goToNextDoubleQuote();
|
||||||
|
const int valueLength = parser.currentIndex() - valueStart;
|
||||||
|
parser.nextChar();
|
||||||
|
return FieldValue{parser.substr(valueStart, valueLength)};
|
||||||
|
} else if (parser.isDigit() || parser.is('-')) {
|
||||||
|
const int valueStart = parser.currentIndex();
|
||||||
|
parser.skipDigits();
|
||||||
|
const int valueLength = parser.currentIndex() - valueStart;
|
||||||
|
int value;
|
||||||
|
std::from_chars(parser.c_str() + valueStart, parser.c_str() + valueStart + valueLength, value);
|
||||||
|
return FieldValue{value};
|
||||||
|
} else if (parser.match("true", 4)) {
|
||||||
|
parser.nextChar(4);
|
||||||
|
return FieldValue{true};
|
||||||
|
} else if (parser.match("false", 5)) {
|
||||||
|
parser.nextChar(5);
|
||||||
|
return FieldValue{false};
|
||||||
|
} else if (parser.match("null", 4)) {
|
||||||
|
parser.nextChar(4);
|
||||||
|
return FieldValue{FieldType::Null};
|
||||||
|
} else if (parser.is('{')){
|
||||||
|
JsonObject newObject{};
|
||||||
|
parseObject(parser, &newObject);
|
||||||
|
return FieldValue{std::move(newObject)};
|
||||||
|
} else if (parser.is('[')){
|
||||||
|
JsonArray newArray{};
|
||||||
|
parseArray(parser, &newArray);
|
||||||
|
return FieldValue{std::move(newArray)};
|
||||||
|
}
|
||||||
|
throw ParsingError("Invalid JSON value", currentLine(), currentColumn());
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseObjectField(StringParser& parser, JsonObject* object) {
|
||||||
|
assert(parser.is('"'));
|
||||||
|
parser.nextChar();
|
||||||
|
const int keyStart = parser.currentIndex();
|
||||||
|
parser.goToNextDoubleQuote();
|
||||||
|
const int keyLength = parser.currentIndex() - keyStart;
|
||||||
|
const std::string key = parser.substr(keyStart, keyLength);
|
||||||
|
parser.nextChar();
|
||||||
|
parser.skipWhitespaces();
|
||||||
|
if (!parser.is(':')) {
|
||||||
|
throw ParsingError("Expected ':'", currentLine(), currentColumn());
|
||||||
|
}
|
||||||
|
parser.nextChar();
|
||||||
|
parser.skipWhitespaces();
|
||||||
|
object->set(key, parseField(parser));
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseArrayField(StringParser& parser, JsonArray* array) {
|
||||||
|
parser.skipWhitespaces();
|
||||||
|
array->push(parseField(parser));
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseArray(StringParser& parser, JsonArray* object) {
|
||||||
|
assert(parser.is('['));
|
||||||
|
parser.nextChar();
|
||||||
|
parser.skipWhitespaces();
|
||||||
|
while (!parser.is(']')) {
|
||||||
|
parseArrayField(parser, object);
|
||||||
|
parser.skipWhitespaces();
|
||||||
|
if (parser.is(',')) {
|
||||||
|
parser.nextChar();
|
||||||
|
parser.skipWhitespaces();
|
||||||
|
if (parser.is(']')) {
|
||||||
|
throw ParsingError("Found extra comma (',')", currentLine(), currentColumn());
|
||||||
|
}
|
||||||
|
} else if (!parser.is(']')) {
|
||||||
|
throw ParsingError("Expected comma (',') or array ending (']')", currentLine(), currentColumn());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parser.nextChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseObject(StringParser& parser, JsonObject* object) {
|
||||||
|
assert(parser.is('{'));
|
||||||
|
parser.nextChar();
|
||||||
|
parser.skipWhitespaces();
|
||||||
|
while (!parser.is('}')) {
|
||||||
|
if (!parser.is('"')) {
|
||||||
|
throw ParsingError("Expected '\"'", currentLine(), currentColumn());
|
||||||
|
}
|
||||||
|
parseObjectField(parser, object);
|
||||||
|
parser.skipWhitespaces();
|
||||||
|
if (parser.is(',')) {
|
||||||
|
parser.nextChar();
|
||||||
|
parser.skipWhitespaces();
|
||||||
|
if (parser.is('}')) {
|
||||||
|
throw ParsingError("Found extra comma (',')", currentLine(), currentColumn());
|
||||||
|
}
|
||||||
|
} else if (!parser.is('}')) {
|
||||||
|
throw ParsingError("Expected comma (',') or object ending ('}')", currentLine(), currentColumn());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parser.nextChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
unsigned i = 0;
|
||||||
|
unsigned line = 1;
|
||||||
|
unsigned lineStartingIndex = 1;
|
||||||
|
const std::string str;
|
||||||
|
};
|
||||||
|
|
||||||
|
FieldValue parse(const std::string& jsonStr) {
|
||||||
|
StringParser parser{jsonStr};
|
||||||
|
|
||||||
|
parser.skipWhitespaces();
|
||||||
|
if (parser.is('{')) {
|
||||||
JsonObject object{};
|
JsonObject object{};
|
||||||
parseObject(jsonStr, &i, &object);
|
parser.parseObject(parser, &object);
|
||||||
return std::move(object);
|
return std::move(object);
|
||||||
} else if (jsonStr[i] == '[') {
|
} else if (parser.is('[')) {
|
||||||
JsonArray array{};
|
JsonArray array{};
|
||||||
parseArray(jsonStr, &i, &array);
|
parser.parseArray(parser, &array);
|
||||||
return std::move(array);
|
return std::move(array);
|
||||||
}
|
}
|
||||||
throw std::runtime_error("Bad json syntax");
|
throw ParsingError("Expected JSON object or array", parser.currentLine(), parser.currentColumn());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
83
tests/errors.cpp
Normal file
83
tests/errors.cpp
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
#include "catch2/catch_test_macros.hpp"
|
||||||
|
#include "catch2/matchers/catch_matchers.hpp"
|
||||||
|
#include "rei-json/json.h"
|
||||||
|
#include <catch2/catch_all.hpp>
|
||||||
|
#include <exception>
|
||||||
|
#include <print>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
TEST_CASE("Parsing errors") {
|
||||||
|
SECTION("Root object") {
|
||||||
|
std::string jsonStr = R"(
|
||||||
|
"keyArray": [
|
||||||
|
42,
|
||||||
|
"elemString",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
null
|
||||||
|
],
|
||||||
|
"keyBooleanFalse": false,
|
||||||
|
"keyBooleanTrue": true,
|
||||||
|
"keyEmptyString": "",
|
||||||
|
"keyNegativeNumber": -13,
|
||||||
|
"keyNull": null,
|
||||||
|
"keyObject": {
|
||||||
|
"keyNumberOnObject": 42
|
||||||
|
},
|
||||||
|
"keyPositiveNumber": 12,
|
||||||
|
"keyString": "YEP"
|
||||||
|
})";
|
||||||
|
REQUIRE_THROWS_WITH(rei::json::parse(jsonStr), "2:1: Expected JSON object or array");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Missing comma") {
|
||||||
|
std::string jsonStr = R"({
|
||||||
|
"keyArray": [
|
||||||
|
42,
|
||||||
|
"elemString",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
null
|
||||||
|
],
|
||||||
|
"keyBooleanFalse": false,
|
||||||
|
"keyBooleanTrue": true
|
||||||
|
"keyEmptyString": "",
|
||||||
|
"keyNegativeNumber": -13,
|
||||||
|
"keyNull": null,
|
||||||
|
"keyObject": {
|
||||||
|
"keyNumberOnObject": 42
|
||||||
|
},
|
||||||
|
"keyPositiveNumber": 12,
|
||||||
|
"keyString": "YEP"
|
||||||
|
})";
|
||||||
|
REQUIRE_THROWS_WITH(rei::json::parse(jsonStr), "12:1: Expected comma (',') or object ending ('}')");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Missing object key quote") {
|
||||||
|
std::string jsonStr = R"({
|
||||||
|
"keyArray": [
|
||||||
|
42,
|
||||||
|
"elemString",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
null
|
||||||
|
],
|
||||||
|
"keyBooleanFalse": false,
|
||||||
|
keyBooleanTrue": true,
|
||||||
|
"keyEmptyString": "",
|
||||||
|
"keyNegativeNumber": -13,
|
||||||
|
"keyNull": null,
|
||||||
|
"keyObject": {
|
||||||
|
"keyNumberOnObject": 42
|
||||||
|
},
|
||||||
|
"keyPositiveNumber": 12,
|
||||||
|
"keyString": "YEP"
|
||||||
|
})";
|
||||||
|
REQUIRE_THROWS_WITH(rei::json::parse(jsonStr), "11:1: Expected '\"'");
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ TEST_CASE("Parsing json object") {
|
||||||
"keyString": "YEP"
|
"keyString": "YEP"
|
||||||
})";
|
})";
|
||||||
auto json = rei::json::parse(jsonStr);
|
auto json = rei::json::parse(jsonStr);
|
||||||
auto& objectJson = std::get<rei::json::JsonObject>(json);
|
auto& objectJson = json.asObject();
|
||||||
|
|
||||||
REQUIRE(objectJson.getNumber("keyPositiveNumber") == 12);
|
REQUIRE(objectJson.getNumber("keyPositiveNumber") == 12);
|
||||||
REQUIRE(objectJson.getNumber("keyNegativeNumber") == -13);
|
REQUIRE(objectJson.getNumber("keyNegativeNumber") == -13);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue