Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -----------------------------------------------------------------------
- json.h
- -----------------------------------------------------------------------
- #pragma once
- #include <iostream>
- #include <map>
- #include <string>
- #include <variant>
- #include <vector>
- namespace json {
- class Node;
- using Dict = std::map<std::string, Node>;
- using Array = std::vector<Node>;
- class ParsingError : public std::runtime_error {
- public:
- using runtime_error::runtime_error;
- };
- class Node final : private std::variant<std::nullptr_t, Array, Dict, bool, int, double, std::string> {
- public:
- using variant::variant;
- using Value = variant;
- bool IsInt() const {
- return std::holds_alternative<int>(*this);
- }
- int AsInt() const {
- using namespace std::literals;
- if (!IsInt()) {
- throw std::logic_error("Not an int"s);
- }
- return std::get<int>(*this);
- }
- bool IsPureDouble() const {
- return std::holds_alternative<double>(*this);
- }
- bool IsDouble() const {
- return IsInt() || IsPureDouble();
- }
- double AsDouble() const {
- using namespace std::literals;
- if (!IsDouble()) {
- throw std::logic_error("Not a double"s);
- }
- return IsPureDouble() ? std::get<double>(*this) : AsInt();
- }
- bool IsBool() const {
- return std::holds_alternative<bool>(*this);
- }
- bool AsBool() const {
- using namespace std::literals;
- if (!IsBool()) {
- throw std::logic_error("Not a bool"s);
- }
- return std::get<bool>(*this);
- }
- bool IsNull() const {
- return std::holds_alternative<std::nullptr_t>(*this);
- }
- bool IsArray() const {
- return std::holds_alternative<Array>(*this);
- }
- const Array& AsArray() const {
- using namespace std::literals;
- if (!IsArray()) {
- throw std::logic_error("Not an array"s);
- }
- return std::get<Array>(*this);
- }
- bool IsString() const {
- return std::holds_alternative<std::string>(*this);
- }
- const std::string& AsString() const {
- using namespace std::literals;
- if (!IsString()) {
- throw std::logic_error("Not a string"s);
- }
- return std::get<std::string>(*this);
- }
- bool IsDict() const {
- return std::holds_alternative<Dict>(*this);
- }
- const Dict& AsDict() const {
- using namespace std::literals;
- if (!IsDict()) {
- throw std::logic_error("Not a dict"s);
- }
- return std::get<Dict>(*this);
- }
- bool operator==(const Node& rhs) const {
- return GetValue() == rhs.GetValue();
- }
- const Value& GetValue() const {
- return *this;
- }
- };
- inline bool operator!=(const Node& lhs, const Node& rhs) {
- return !(lhs == rhs);
- }
- class Document {
- public:
- explicit Document(Node root)
- : root_(std::move(root)) {
- }
- const Node& GetRoot() const {
- return root_;
- }
- private:
- Node root_;
- };
- inline bool operator==(const Document& lhs, const Document& rhs) {
- return lhs.GetRoot() == rhs.GetRoot();
- }
- inline bool operator!=(const Document& lhs, const Document& rhs) {
- return !(lhs == rhs);
- }
- Document Load(std::istream& input);
- void Print(const Document& doc, std::ostream& output);
- }//end namespace json
- -----------------------------------------------------------------------
- json.cpp
- -----------------------------------------------------------------------
- #include "json.h"
- #include <iterator>
- namespace json {
- namespace {
- using namespace std::literals;
- Node LoadNode(std::istream& input);
- Node LoadString(std::istream& input);
- std::string LoadLiteral(std::istream& input) {
- std::string s;
- while (std::isalpha(input.peek())) {
- s.push_back(static_cast<char>(input.get()));
- }
- return s;
- }
- Node LoadArray(std::istream& input) {
- std::vector<Node> result;
- for (char c; input >> c && c != ']';) {
- if (c != ',') {
- input.putback(c);
- }
- result.push_back(LoadNode(input));
- }
- if (!input) {
- throw ParsingError("Array parsing error"s);
- }
- return Node(std::move(result));
- }
- Node LoadDict(std::istream& input) {
- Dict dict;
- for (char c; input >> c && c != '}';) {
- if (c == '"') {
- std::string key = LoadString(input).AsString();
- if (input >> c && c == ':') {
- if (dict.find(key) != dict.end()) {
- throw ParsingError("Duplicate key '"s + key + "' have been found");
- }
- dict.emplace(std::move(key), LoadNode(input));
- } else {
- throw ParsingError(": is expected but '"s + c + "' has been found"s);
- }
- } else if (c != ',') {
- throw ParsingError(R"(',' is expected but ')"s + c + "' has been found"s);
- }
- }
- if (!input) {
- throw ParsingError("Dictionary parsing error"s);
- }
- return Node(std::move(dict));
- }
- Node LoadString(std::istream& input) {
- auto it = std::istreambuf_iterator<char>(input);
- auto end = std::istreambuf_iterator<char>();
- std::string s;
- while (true) {
- if (it == end) {
- throw ParsingError("String parsing error");
- }
- const char ch = *it;
- if (ch == '"') {
- ++it;
- break;
- } else if (ch == '\\') {
- ++it;
- if (it == end) {
- throw ParsingError("String parsing error");
- }
- const char escaped_char = *(it);
- switch (escaped_char) {
- case 'n':
- s.push_back('\n');
- break;
- case 't':
- s.push_back('\t');
- break;
- case 'r':
- s.push_back('\r');
- break;
- case '"':
- s.push_back('"');
- break;
- case '\\':
- s.push_back('\\');
- break;
- default:
- throw ParsingError("Unrecognized escape sequence \\"s + escaped_char);
- }
- } else if (ch == '\n' || ch == '\r') {
- throw ParsingError("Unexpected end of line"s);
- } else {
- s.push_back(ch);
- }
- ++it;
- }
- return Node(std::move(s));
- }
- Node LoadBool(std::istream& input) {
- const auto s = LoadLiteral(input);
- if (s == "true"sv) {
- return Node{true};
- } else if (s == "false"sv) {
- return Node{false};
- } else {
- throw ParsingError("Failed to parse '"s + s + "' as bool"s);
- }
- }
- Node LoadNull(std::istream& input) {
- if (auto literal = LoadLiteral(input); literal == "null"sv) {
- return Node{nullptr};
- } else {
- throw ParsingError("Failed to parse '"s + literal + "' as null"s);
- }
- }
- Node LoadNumber(std::istream& input) {
- std::string parsed_num;
- auto read_char = [&parsed_num, &input] {
- parsed_num += static_cast<char>(input.get());
- if (!input) {
- throw ParsingError("Failed to read number from stream"s);
- }
- };
- auto read_digits = [&input, read_char] {
- if (!std::isdigit(input.peek())) {
- throw ParsingError("A digit is expected"s);
- }
- while (std::isdigit(input.peek())) {
- read_char();
- }
- };
- if (input.peek() == '-') {
- read_char();
- }
- if (input.peek() == '0') {
- read_char();
- } else {
- read_digits();
- }
- bool is_int = true;
- if (input.peek() == '.') {
- read_char();
- read_digits();
- is_int = false;
- }
- if (int ch = input.peek(); ch == 'e' || ch == 'E') {
- read_char();
- if (ch = input.peek(); ch == '+' || ch == '-') {
- read_char();
- }
- read_digits();
- is_int = false;
- }
- try {
- if (is_int) {
- try {
- return std::stoi(parsed_num);
- } catch (...) {
- }
- }
- return std::stod(parsed_num);
- } catch (...) {
- throw ParsingError("Failed to convert "s + parsed_num + " to number"s);
- }
- }
- Node LoadNode(std::istream& input) {
- char c;
- if (!(input >> c)) {
- throw ParsingError("Unexpected EOF"s);
- }
- switch (c) {
- case '[':
- return LoadArray(input);
- case '{':
- return LoadDict(input);
- case '"':
- return LoadString(input);
- case 't':
- [[fallthrough]];
- case 'f':
- input.putback(c);
- return LoadBool(input);
- case 'n':
- input.putback(c);
- return LoadNull(input);
- default:
- input.putback(c);
- return LoadNumber(input);
- }
- }
- struct PrintContext {
- std::ostream& out;
- int indent_step = 4;
- int indent = 0;
- void PrintIndent() const {
- for (int i = 0; i < indent; ++i) {
- out.put(' ');
- }
- }
- PrintContext Indented() const {
- return {out,
- indent_step,
- indent_step + indent};
- }
- };
- void PrintNode(const Node& value, const PrintContext& ctx);
- template <typename Value>
- void PrintValue(const Value& value, const PrintContext& ctx) {
- ctx.out << value;
- }
- void PrintString(const std::string& value, std::ostream& out) {
- out.put('"');
- for (const char c : value) {
- switch (c) {
- case '\r':
- out << "\\r"sv;
- break;
- case '\n':
- out << "\\n"sv;
- break;
- case '"':
- [[fallthrough]];
- case '\\':
- out.put('\\');
- [[fallthrough]];
- default:
- out.put(c);
- break;
- }
- }
- out.put('"');
- }
- template <>
- void PrintValue<std::string>(const std::string& value, const PrintContext& ctx) {
- PrintString(value, ctx.out);
- }
- template <>
- void PrintValue<std::nullptr_t>(const std::nullptr_t&, const PrintContext& ctx) {
- ctx.out << "null"sv;
- }
- template <>
- void PrintValue<bool>(const bool& value, const PrintContext& ctx) {
- ctx.out << (value ? "true"sv : "false"sv);
- }
- template <>
- void PrintValue<Array>(const Array& nodes, const PrintContext& ctx) {
- std::ostream& out = ctx.out;
- out << "[\n"sv;
- bool first = true;
- auto inner_ctx = ctx.Indented();
- for (const Node& node : nodes) {
- if (first) {
- first = false;
- } else {
- out << ",\n"sv;
- }
- inner_ctx.PrintIndent();
- PrintNode(node, inner_ctx);
- }
- out.put('\n');
- ctx.PrintIndent();
- out.put(']');
- }
- template <>
- void PrintValue<Dict>(const Dict& nodes, const PrintContext& ctx) {
- std::ostream& out = ctx.out;
- out << "{\n"sv;
- bool first = true;
- auto inner_ctx = ctx.Indented();
- for (const auto& [key, node] : nodes) {
- if (first) {
- first = false;
- } else {
- out << ",\n"sv;
- }
- inner_ctx.PrintIndent();
- PrintString(key, ctx.out);
- out << ": "sv;
- PrintNode(node, inner_ctx);
- }
- out.put('\n');
- ctx.PrintIndent();
- out.put('}');
- }
- void PrintNode(const Node& node, const PrintContext& ctx) {
- std::visit(
- [&ctx](const auto& value) {PrintValue(value, ctx);},node.GetValue());
- }
- }//end namespace
- Document Load(std::istream& input) {
- return Document{LoadNode(input)};
- }
- void Print(const Document& doc, std::ostream& output) {
- PrintNode(doc.GetRoot(), PrintContext{output});
- }
- }//end namespace json
- -----------------------------------------------------------------------
- json_builder.h
- -----------------------------------------------------------------------
- #pragma once
- #include "json.h"
- #include <stack>
- #include <string>
- #include <memory>
- namespace json {
- class KeyContext;
- class DictionaryContext;
- class ArrayContext;
- class Builder {
- public:
- Node make_node(Node::Value value_);
- void add_node(Node node);
- KeyContext Key(std::string key_);
- DictionaryContext StartDict();
- ArrayContext StartArray();
- Builder& Value(Node::Value value);
- Builder& EndDict();
- Builder& EndArray();
- Node Build();
- private:
- Node root_;
- std::vector<std::unique_ptr<Node>> nodes_stack_;
- };
- class BaseContext {
- public:
- BaseContext(Builder& builder);
- KeyContext Key(std::string key);
- DictionaryContext StartDict();
- ArrayContext StartArray();
- Builder& Value(Node::Value value);
- Builder& EndDict();
- Builder& EndArray();
- protected:
- Builder& builder_;
- };
- class KeyContext : public BaseContext {
- public:
- KeyContext(Builder& builder);
- KeyContext Key(std::string key) = delete;
- BaseContext EndDict() = delete;
- BaseContext EndArray() = delete;
- DictionaryContext Value(Node::Value value);
- };
- class DictionaryContext : public BaseContext {
- public:
- DictionaryContext(Builder& builder);
- DictionaryContext StartDict() = delete;
- ArrayContext StartArray() = delete;
- Builder& EndArray() = delete;
- Builder& Value(Node::Value value) = delete;
- };
- class ArrayContext : public BaseContext {
- public:
- ArrayContext(Builder& builder);
- KeyContext Key(std::string key) = delete;
- Builder& EndDict() = delete;
- ArrayContext Value(Node::Value value);
- };
- }//end namespace json
- -----------------------------------------------------------------------
- json_builder.cpp
- -----------------------------------------------------------------------
- #include "json_builder.h"
- namespace json {
- //---------------------BaseContext---------------------
- BaseContext::BaseContext(Builder& builder) : builder_(builder) {}
- KeyContext BaseContext::Key(std::string key) {return builder_.Key(key);}
- Builder& BaseContext::Value(Node::Value value) {return builder_.Value(value);}
- DictionaryContext BaseContext::StartDict() {return DictionaryContext(builder_.StartDict());}
- Builder& BaseContext::EndDict() {return builder_.EndDict();}
- ArrayContext BaseContext::StartArray() {return ArrayContext(builder_.StartArray());}
- Builder& BaseContext::EndArray() {return builder_.EndArray();}
- //---------------------KeyContext-----------------------
- KeyContext::KeyContext(Builder& builder) : BaseContext(builder) {}
- DictionaryContext KeyContext::Value(Node::Value value) {return BaseContext::Value(std::move(value));}
- //---------------------DictionaryContext----------------
- DictionaryContext::DictionaryContext(Builder& builder) : BaseContext(builder) {}
- //---------------------ArrayContext---------------------
- ArrayContext::ArrayContext(Builder& builder) : BaseContext(builder) {}
- ArrayContext ArrayContext::Value (Node::Value value) {return BaseContext::Value(move(value)); }
- //---------------------Builder--------------------------
- Node Builder::make_node(Node::Value value_) {
- Node node;
- if (std::holds_alternative<bool>(value_)) {
- bool bol = std::get<bool>(value_);
- node = Node(bol);
- } else if (std::holds_alternative<int>(value_)) {
- int intt = std::get<int>(value_);
- node = Node(intt);
- } else if (std::holds_alternative<double>(value_)) {
- double doble = std::get<double>(value_);
- node = Node(doble);
- } else if (std::holds_alternative<std::string>(value_)) {
- std::string str = std::get<std::string>(value_);
- node = Node(std::move(str));
- } else if (std::holds_alternative<Array>(value_)) {
- Array arr = std::get<Array>(value_);
- node = Node(std::move(arr));
- } else if (std::holds_alternative<Dict>(value_)) {
- Dict dictionary = std::get<Dict>(value_);
- node = Node(std::move(dictionary));
- } else {
- node = Node();
- }
- return node;
- }
- void Builder::add_node(Node node) {
- if (nodes_stack_.empty()) {
- if (!root_.IsNull()) {
- throw std::logic_error("root has been added");
- }
- root_ = node;
- return;
- } else {
- if (!nodes_stack_.back()->IsArray()
- && !nodes_stack_.back()->IsString()) {
- throw std::logic_error("unable to create node");
- }
- if (nodes_stack_.back()->IsArray()) {
- Array arr = nodes_stack_.back()->AsArray();
- arr.emplace_back(node);
- nodes_stack_.pop_back();
- auto arr_ptr = std::make_unique<Node>(arr);
- nodes_stack_.emplace_back(std::move(arr_ptr));
- return;
- }
- if (nodes_stack_.back()->IsString()) {
- std::string str = nodes_stack_.back()->AsString();
- nodes_stack_.pop_back();
- if (nodes_stack_.back()->IsDict()) {
- Dict dictionary = nodes_stack_.back()->AsDict();
- dictionary.emplace(std::move(str), node);
- nodes_stack_.pop_back();
- auto dictionary_ptr = std::make_unique<Node>(dictionary);
- nodes_stack_.emplace_back(std::move(dictionary_ptr));
- }
- return;
- }
- }
- }
- KeyContext Builder::Key(std::string key_) {
- if (nodes_stack_.empty()) {
- throw std::logic_error("unable to create key");
- }
- auto key_ptr = std::make_unique<Node>(key_);
- if (nodes_stack_.back()->IsDict()) {
- nodes_stack_.emplace_back(std::move(key_ptr));
- }
- return KeyContext(*this);
- }
- Builder& Builder::Value(Node::Value value_) {
- add_node(make_node(value_));
- return *this;
- }
- DictionaryContext Builder::StartDict() {
- nodes_stack_.emplace_back(std::move(std::make_unique<Node>(Dict())));
- return DictionaryContext(*this);
- }
- Builder& Builder::EndDict() {
- if (nodes_stack_.empty()) {
- throw std::logic_error("unable to close as without opening");
- }
- Node node = *nodes_stack_.back();
- if (!node.IsDict()) {
- throw std::logic_error("object isn't dictionary");
- }
- nodes_stack_.pop_back();
- add_node(node);
- return *this;
- }
- ArrayContext Builder::StartArray() {
- nodes_stack_.emplace_back(std::move(std::make_unique<Node>(Array())));
- return ArrayContext(*this);
- }
- Builder& Builder::EndArray() {
- if (nodes_stack_.empty()) {
- throw std::logic_error("unable to close without opening");
- }
- Node node = *nodes_stack_.back();
- if (!node.IsArray()) {
- throw std::logic_error("object isn't array");
- }
- nodes_stack_.pop_back();
- add_node(node);
- return *this;
- }
- Node Builder::Build() {
- if (root_.IsNull()) {
- throw std::logic_error("empty json");
- }
- if (!nodes_stack_.empty()) {
- throw std::logic_error("invalid json");
- }
- return root_;
- }
- }//end namespace json
- -----------------------------------------------------------------------
- main.cpp
- -----------------------------------------------------------------------
- #include "json_builder.h"
- #include <iostream>
- using namespace std;
- int main() {
- json::Print(
- json::Document{
- json::Builder{}
- .StartDict()
- .Key("key1"s).Value(123)
- .Key("key2"s).Value("value2"s)
- .Key("key3"s).StartArray()
- .Value(456)
- .StartDict().EndDict()
- .StartDict()
- .Key(""s)
- .Value(nullptr)
- .EndDict()
- .Value(""s)
- .EndArray()
- .EndDict()
- .Build()
- },
- cout
- );
- cout << endl;
- json::Print(
- json::Document{
- json::Builder{}
- .Value("just a string"s)
- .Build()
- },
- cout
- );
- cout << endl;
- }
Advertisement
Add Comment
Please, Sign In to add comment