From 651179b5da0777f861e427f96fd8560bf1516ae5 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Mon, 17 Dec 2018 11:55:11 -0500 Subject: Add simple JSON serializer --- libqpdf/JSON.cc | 396 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 396 insertions(+) create mode 100644 libqpdf/JSON.cc (limited to 'libqpdf/JSON.cc') diff --git a/libqpdf/JSON.cc b/libqpdf/JSON.cc new file mode 100644 index 00000000..def439cf --- /dev/null +++ b/libqpdf/JSON.cc @@ -0,0 +1,396 @@ +#include +#include +#include +#include + +JSON::Members::~Members() +{ +} + +JSON::Members::Members(PointerHolder value) : + value(value) +{ +} + +JSON::JSON(PointerHolder value) : + m(new Members(value)) +{ +} + +JSON::JSON_value::~JSON_value() +{ +} + +JSON::JSON_dictionary::~JSON_dictionary() +{ +} + +std::string JSON::JSON_dictionary::unparse(size_t depth) const +{ + std::string result = "{"; + bool first = true; + for (std::map >::const_iterator + iter = members.begin(); + iter != members.end(); ++iter) + { + if (first) + { + first = false; + } + else + { + result.append(1, ','); + } + result.append(1, '\n'); + result.append(2 * (1 + depth), ' '); + result += ("\"" + (*iter).first + "\": " + + (*iter).second->unparse(1 + depth)); + } + if (! first) + { + result.append(1, '\n'); + result.append(2 * depth, ' '); + } + result.append(1, '}'); + return result; +} + +JSON::JSON_array::~JSON_array() +{ +} + +std::string JSON::JSON_array::unparse(size_t depth) const +{ + std::string result = "["; + bool first = true; + for (std::vector >::const_iterator iter = + elements.begin(); + iter != elements.end(); ++iter) + { + if (first) + { + first = false; + } + else + { + result.append(1, ','); + } + result.append(1, '\n'); + result.append(2 * (1 + depth), ' '); + result += (*iter)->unparse(1 + depth); + } + if (! first) + { + result.append(1, '\n'); + result.append(2 * depth, ' '); + } + result.append(1, ']'); + return result; +} + +JSON::JSON_string::JSON_string(std::string const& utf8) : + encoded(encode_string(utf8)) +{ +} + +JSON::JSON_string::~JSON_string() +{ +} + +std::string JSON::JSON_string::unparse(size_t) const +{ + return "\"" + encoded + "\""; +} + +JSON::JSON_number::JSON_number(long long value) : + encoded(QUtil::int_to_string(value)) +{ +} + +JSON::JSON_number::JSON_number(double value) : + encoded(QUtil::double_to_string(value, 6)) +{ +} + +JSON::JSON_number::JSON_number(std::string const& value) : + encoded(value) +{ +} + +JSON::JSON_number::~JSON_number() +{ +} + +std::string JSON::JSON_number::unparse(size_t) const +{ + return encoded; +} + +JSON::JSON_bool::JSON_bool(bool val) : + value(val) +{ +} + +JSON::JSON_bool::~JSON_bool() +{ +} + +std::string JSON::JSON_bool::unparse(size_t) const +{ + return value ? "true" : "false"; +} + +JSON::JSON_null::~JSON_null() +{ +} + +std::string JSON::JSON_null::unparse(size_t) const +{ + return "null"; +} + +std::string +JSON::serialize() const +{ + if (0 == this->m->value.getPointer()) + { + return "null"; + } + else + { + return this->m->value->unparse(0); + } +} + +std::string +JSON::encode_string(std::string const& str) +{ + std::string result; + size_t len = str.length(); + for (size_t i = 0; i < len; ++i) + { + unsigned char ch = static_cast(str.at(i)); + switch (ch) + { + case '\\': + result += "\\\\"; + break; + case '\"': + result += "\\\""; + break; + case '\b': + result += "\\b"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + default: + if (ch < 32) + { + result += "\\u" + QUtil::int_to_string_base(ch, 16, 4); + } + else + { + result.append(1, ch); + } + } + } + return result; +} + +JSON +JSON::makeDictionary() +{ + return JSON(new JSON_dictionary()); +} + +JSON +JSON::addDictionaryMember(std::string const& key, JSON const& val) +{ + JSON_dictionary* obj = dynamic_cast( + this->m->value.getPointer()); + if (0 == obj) + { + throw std::runtime_error( + "JSON::addDictionaryMember called on non-dictionary"); + } + if (val.m->value.getPointer()) + { + obj->members[encode_string(key)] = val.m->value; + } + else + { + obj->members[encode_string(key)] = new JSON_null(); + } + return obj->members[encode_string(key)]; +} + +JSON +JSON::makeArray() +{ + return JSON(new JSON_array()); +} + +JSON +JSON::addArrayElement(JSON const& val) +{ + JSON_array* arr = dynamic_cast( + this->m->value.getPointer()); + if (0 == arr) + { + throw std::runtime_error("JSON::addArrayElement called on non-array"); + } + if (val.m->value.getPointer()) + { + arr->elements.push_back(val.m->value); + } + else + { + arr->elements.push_back(new JSON_null()); + } + return arr->elements.back(); +} + +JSON +JSON::makeString(std::string const& utf8) +{ + return JSON(new JSON_string(utf8)); +} + +JSON +JSON::makeInt(long long int value) +{ + return JSON(new JSON_number(value)); +} + +JSON +JSON::makeReal(double value) +{ + return JSON(new JSON_number(value)); +} + +JSON +JSON::makeNumber(std::string const& encoded) +{ + return JSON(new JSON_number(encoded)); +} + +JSON +JSON::makeBool(bool value) +{ + return JSON(new JSON_bool(value)); +} + +JSON +JSON::makeNull() +{ + return JSON(new JSON_null()); +} + +bool +JSON::checkSchema(JSON schema, std::list& errors) +{ + return checkSchemaInternal(this->m->value.getPointer(), + schema.m->value.getPointer(), + errors, ""); +} + + +bool +JSON::checkSchemaInternal(JSON_value* this_v, JSON_value* sch_v, + std::list& errors, + std::string prefix) +{ + JSON_array* this_arr = dynamic_cast(this_v); + JSON_dictionary* this_dict = dynamic_cast(this_v); + + JSON_array* sch_arr = dynamic_cast(sch_v); + JSON_dictionary* sch_dict = dynamic_cast(sch_v); + + std::string err_prefix; + if (prefix.empty()) + { + err_prefix = "top-level object"; + } + else + { + err_prefix = "json key \"" + prefix + "\""; + } + + if (sch_dict) + { + if (! this_dict) + { + QTC::TC("libtests", "JSON wanted dictionary"); + errors.push_back(err_prefix + " is supposed to be a dictionary"); + return false; + } + for (std::map >::iterator iter = + sch_dict->members.begin(); + iter != sch_dict->members.end(); ++iter) + { + std::string const& key = (*iter).first; + if (this_dict->members.count(key)) + { + checkSchemaInternal( + this_dict->members[key].getPointer(), + (*iter).second.getPointer(), + errors, prefix + "." + key); + } + else + { + QTC::TC("libtests", "JSON key missing in object"); + errors.push_back( + err_prefix + ": key \"" + key + + "\" is present in schema but missing in object"); + } + } + for (std::map >::iterator iter = + this_dict->members.begin(); + iter != this_dict->members.end(); ++iter) + { + std::string const& key = (*iter).first; + if (sch_dict->members.count(key) == 0) + { + QTC::TC("libtests", "JSON key extra in object"); + errors.push_back( + err_prefix + ": key \"" + key + + "\" is not present in schema but appears in object"); + } + } + } + else if (sch_arr) + { + if (! this_arr) + { + QTC::TC("libtests", "JSON wanted array"); + errors.push_back(err_prefix + " is supposed to be an array"); + return false; + } + if (sch_arr->elements.size() != 1) + { + QTC::TC("libtests", "JSON schema array error"); + errors.push_back(err_prefix + + " schema array contains other than one item"); + return false; + } + int i = 0; + for (std::vector >::iterator iter = + this_arr->elements.begin(); + iter != this_arr->elements.end(); ++iter, ++i) + { + checkSchemaInternal( + (*iter).getPointer(), + sch_arr->elements.at(0).getPointer(), + errors, prefix + "." + QUtil::int_to_string(i)); + } + } + + return errors.empty(); +} -- cgit v1.2.3-70-g09d2