#include "JSON.h" #include "Datei.h" #include "Logging.h" using namespace Framework; using namespace JSON; #pragma region JSONValue JSONValue::JSONValue() : ReferenceCounter() { this->type = JSONType::NULL_; } JSONValue::~JSONValue() {} JSONValue::JSONValue(JSONType type) : ReferenceCounter() { this->type = type; } JSONType JSONValue::getType() const { return type; } Text JSONValue::toString() const { return Text("null"); } JSONValue* JSONValue::clone() const { return new JSONValue(); } JSONBool* JSONValue::asBool() const { return (JSONBool*)this; } JSONNumber* JSONValue::asNumber() const { return (JSONNumber*)this; } JSONString* JSONValue::asString() const { return (JSONString*)this; } JSONArray* JSONValue::asArray() const { return (JSONArray*)this; } JSONObject* JSONValue::asObject() const { return (JSONObject*)this; } #pragma endregion Content #pragma region JSONBool JSONBool::JSONBool(bool b) : JSONValue(JSONType::BOOLEAN) { this->b = b; } bool JSONBool::getBool() const { return b; } Text JSONBool::toString() const { if (b) return Text("true"); else return Text("false"); } JSONValue* JSONBool::clone() const { return new JSONBool(b); } #pragma endregion Content #pragma region JSONNumber JSONNumber::JSONNumber(double num) : JSONValue(JSONType::NUMBER) { number = num; } double JSONNumber::getNumber() const { return number; } Text JSONNumber::toString() const { return Text(number); } JSONValue* JSONNumber::clone() const { return new JSONNumber(number); } #pragma endregion Content #pragma region JSONString JSONString::JSONString(Text string) : JSONValue(JSONType::STRING) { this->string = string; this->string.ersetzen("\\\"", "\""); this->string.ersetzen("\\n", "\n"); } Text JSONString::getString() const { return string; } Text JSONString::toString() const { Text esc = string; esc.ersetzen("\"", "\\\""); esc.ersetzen("\n", "\\n"); return Text(Text("\"") += esc.getText()) += "\""; } JSONValue* JSONString::clone() const { Text esc = string; esc.ersetzen("\"", "\\\""); esc.ersetzen("\n", "\\n"); return new JSONString(esc); } #pragma endregion Content #pragma region JSONArray JSONArray::JSONArray() : JSONValue(JSONType::ARRAY) { array = new RCArray(); } JSONArray::JSONArray(Text string) : JSONValue(JSONType::ARRAY) { array = new RCArray(); string = Parser::removeWhitespace(string); if (string.getText()[0] == '[' && string.getText()[string.getLength() - 1] == ']') { string.remove(0, 1); string.remove(string.getLength() - 1, string.getLength()); while (string.getLength()) { int end = Parser::findObjectEndInArray(string); Text* objStr = string.getTeilText(0, end); string.remove(0, end + 1); array->add(Parser::getValue(*objStr)); objStr->release(); } } } JSONArray::JSONArray(const JSONArray& arr) : JSONValue(JSONType::ARRAY) { array = dynamic_cast*>(arr.array->getThis()); } JSONArray::~JSONArray() { array->release(); } JSONArray& JSONArray::operator=(const JSONArray& arr) { array->release(); array = dynamic_cast*>(arr.array->getThis()); return *this; } void JSONArray::addValue(JSONValue* value) { array->add(value); } void JSONArray::setValue(int i, JSONValue* value) { array->set(value, i); } void JSONArray::removeValue(int i) { array->remove(i); } JSONValue* JSONArray::getValue(int i) const { return array->get(i); } JSONValue* JSONArray::zValue(int i) const { return array->z(i); } int JSONArray::getLength() const { return array->getEintragAnzahl(); } bool JSONArray::isValueOfType(int i, JSONType type) const { return i >= 0 && i < array->getEintragAnzahl() && array->z(i)->getType() == type; } ArrayIterator JSONArray::begin() const { return array->begin(); } ArrayIterator JSONArray::end() const { return array->end(); } Text JSONArray::toString() const { Text str = "["; for (auto i = array->begin(); i; i++) { str += i->toString(); if (i.hasNext()) str += ","; } str += "]"; return str; } JSONValue* JSONArray::clone() const { JSONArray* arr = new JSONArray(); for (JSONValue* value : *this) { arr->addValue(value->clone()); } return arr; } #pragma endregion Content #pragma region JSONObject JSONObject::JSONObject() : JSONValue(JSONType::OBJECT) { fields = new Array(); values = new RCArray(); } JSONObject::JSONObject(Text string) : JSONValue(JSONType::OBJECT) { fields = new Array(); values = new RCArray(); string = Parser::removeWhitespace(string); if (string.getText()[0] == '{' && string.getText()[string.getLength() - 1] == '}') { string.remove(0, 1); string.remove(string.getLength() - 1, string.getLength()); while (string.getLength()) { int endField = Parser::findFieldEndInObject(string); Text* fieldName = string.getTeilText(0, endField); string.remove(0, endField + 1); fieldName->remove(0, 1); fieldName->remove( fieldName->getLength() - 1, fieldName->getLength()); int endValue = Parser::findValueEndInObject(string); Text* value = string.getTeilText(0, endValue); string.remove(0, endValue + 1); fields->add(Text(fieldName->getText())); values->add(Parser::getValue(*value)); fieldName->release(); value->release(); } } } JSONObject::JSONObject(const JSONObject& obj) : JSONValue(JSONType::OBJECT) { fields = dynamic_cast*>(obj.fields->getThis()); values = dynamic_cast*>(obj.values->getThis()); } JSONObject::~JSONObject() { fields->release(); values->release(); } JSONObject& JSONObject::operator=(const JSONObject& obj) { fields->release(); values->release(); fields = dynamic_cast*>(obj.fields->getThis()); values = dynamic_cast*>(obj.values->getThis()); return *this; } bool JSONObject::addValue(Text field, JSONValue* value) { if (hasValue(field)) return 0; fields->add(field); values->add(value); return 1; } bool JSONObject::removeValue(Text field) { for (int i = 0; i < fields->getEintragAnzahl(); i++) { if (fields->get(i).istGleich(field)) { fields->remove(i); values->remove(i); return 1; } } return 0; } bool JSONObject::hasValue(Text field) { for (int i = 0; i < fields->getEintragAnzahl(); i++) { if (fields->get(i).istGleich(field)) return 1; } return 0; } JSONValue* JSONObject::getValue(Text field) { for (int i = 0; i < fields->getEintragAnzahl(); i++) { if (fields->get(i).istGleich(field)) return values->get(i); } return 0; } JSONValue* JSONObject::zValue(Text field) { for (int i = 0; i < fields->getEintragAnzahl(); i++) { if (fields->get(i).istGleich(field)) return values->z(i); } return 0; } ArrayIterator JSONObject::getFields() { return fields->begin(); } ArrayIterator JSONObject::getValues() { return values->begin(); } int JSONObject::getFieldCount() const { return fields->getEintragAnzahl(); } bool JSONObject::isValueOfType(Text field, JSONType type) const { for (int i = 0; i < fields->getEintragAnzahl(); i++) { if (fields->get(i).istGleich(field)) return values->z(i)->getType() == type; } return 0; } Text JSONObject::toString() const { Text str = "{"; ArrayIterator k = fields->begin(); for (auto v = values->begin(); k && v; k++, v++) { str += "\""; str += k._.getText(); str += "\":"; str += v->toString().getText(); if (v.hasNext()) str += ","; } str += "}"; return str; } JSONValue* JSONObject::clone() const { JSONObject* obj = new JSONObject(); auto field = fields->begin(); auto value = values->begin(); for (; field && value; field++, value++) { obj->addValue(field.val(), value->clone()); } return obj; } #pragma endregion Content JSONValue* JSON::loadJSONFromFile(Text path) { Datei d; d.setDatei(path); if (!d.open(Datei::Style::lesen)) { return new JSONValue(); } int size = (int)d.getSize(); char* buffer = new char[size + 1]; buffer[size] = 0; d.lese(buffer, size); d.close(); JSONValue* result = Parser::getValue(buffer); delete[] buffer; return result; } #pragma region Parser int Parser::findObjectEndInArray(const char* str) { return findValueEndInObject(str); } Text Parser::removeWhitespace(const char* str) { int wsc = 0; int i = 0; bool esc = 0; bool strO = 0; for (; str[i]; i++) { switch (str[i]) { case '\\': if (strO) esc = !esc; else esc = 0; break; case '"': if (!esc) strO = !strO; esc = 0; break; case ' ': case '\n': case '\t': case '\r': if (!strO) wsc++; esc = 0; break; default: esc = 0; break; } } Text ret; i = 0; esc = 0; strO = 0; for (; str[i]; i++) { switch (str[i]) { case '\\': if (strO) esc = !esc; else esc = 0; ret.append(str[i]); break; case '"': if (!esc) strO = !strO; esc = 0; ret.append(str[i]); break; case ' ': case '\n': case '\t': case '\r': if (strO) ret.append(str[i]); esc = 0; break; default: ret.append(str[i]); esc = 0; break; } } return ret; } JSONValue* Parser::getValue(const char* str) { Text string = Parser::removeWhitespace(str); if (string.istGleich("true")) return new JSONBool(1); if (string.istGleich("false")) return new JSONBool(0); if (string.getText()[0] == '"') { string.remove(0, 1); string.remove(string.getLength() - 1, string.getLength()); return new JSONString(string); } if (string.getText()[0] == '[') return new JSONArray(string); if (string.getText()[0] == '{') return new JSONObject(string); if (Text((int)string).istGleich(string.getText())) return new JSONNumber((double)string); if (string.anzahlVon('.') == 1) { bool isNumber = 1; for (const char* c = (*string.getText() == '-') ? string.getText() + 1 : string.getText(); *c; c++) isNumber &= (*c >= '0' && *c <= '9') || *c == '.'; if (isNumber) return new JSONNumber((double)string); } return new JSONValue(); } int Parser::findFieldEndInObject(const char* str) { int i = 0; bool esc = 0; bool strO = 0; int objOc = 0; int arrayOc = 0; for (; str[i]; i++) { switch (str[i]) { case '\\': if (strO) esc = !esc; else esc = 0; break; case '"': if (!esc) strO = !strO; esc = 0; break; case '[': if (!strO) arrayOc++; esc = 0; break; case ']': if (!strO) arrayOc--; esc = 0; break; case '{': if (!strO) objOc++; esc = 0; break; case '}': if (!strO) objOc--; esc = 0; break; case ':': if (!strO && objOc == 0 && arrayOc == 0) return i; esc = 0; break; default: esc = 0; break; } } return i; } int Parser::findValueEndInObject(const char* str) { int i = 0; bool esc = 0; bool strO = 0; int objOc = 0; int arrayOc = 0; for (; str[i]; i++) { switch (str[i]) { case '\\': if (strO) esc = !esc; else esc = 0; break; case '"': if (!esc) strO = !strO; esc = 0; break; case '[': if (!strO) arrayOc++; esc = 0; break; case ']': if (!strO) arrayOc--; esc = 0; break; case '{': if (!strO) objOc++; esc = 0; break; case '}': if (!strO) objOc--; esc = 0; break; case ',': if (!strO && objOc == 0 && arrayOc == 0) return i; esc = 0; break; default: esc = 0; break; } } return i; } #pragma endregion Content using namespace Validator; #pragma region JSONValidationResult JSONValidationResult::JSONValidationResult() : ReferenceCounter() {} JSONValidationResult::~JSONValidationResult() {} void Framework::JSON::Validator::JSONValidationResult::logInvalidInfo( std::source_location location) const { Logging::error(location) << getInvalidInfo(0).getText(); } #pragma region JSONTypeMissmatch JSONTypeMissmatch::JSONTypeMissmatch(Text path, JSONValue* foundValue, XML::Element* expected, JSONValidationResult* reason) : JSONValidationResult() { this->path = path; this->foundValue = foundValue; this->expected = expected; this->reason = reason; } JSONTypeMissmatch::~JSONTypeMissmatch() { foundValue->release(); expected->release(); if (reason) reason->release(); } bool JSONTypeMissmatch::isValid() const { return 0; } Text JSONTypeMissmatch::getInvalidInfo(int indent) const { Text ind = ""; ind.fillText(' ', indent); Text error = ""; error.append() << ind.getText() << "Type missmatch at path '" << path.getText() << "'. JSON Value\n" << ind.getText() << foundValue->toString().getText() << "\n" << ind.getText() << "does not match expected type\n"; if (reason) { error.append() << ind.getText() << "Reason for type mismatch:\n" << reason->getInvalidInfo(indent + 4); } else { error.append() << ind.getText() << expected->toString().getText() << "\n"; } return error; } JSONValue* JSONTypeMissmatch::getValidPart( RCArray* removedPartsValidationResults) { if (reason) { RCArray temporaryInvalidParts; JSONValue* valid = reason->getValidPart(&temporaryInvalidParts); Text* p = reason->getPath().getTeilText(path.getLength()); if (foundValue->getType() == JSONType::ARRAY) { if (!valid && (!expected->hasAttribute("removeInvalidEntries") || !expected->getAttributeValue("removeInvalidEntries") .istGleich("true"))) { if (removedPartsValidationResults) { if (temporaryInvalidParts.getEintragAnzahl() == 1) { if (reason) reason->release(); reason = temporaryInvalidParts.get(0); } else { for (JSONValidationResult* res : temporaryInvalidParts) { if (res->isDifferent(this)) { removedPartsValidationResults->add( dynamic_cast( res->getThis())); } } } removedPartsValidationResults->add( dynamic_cast(getThis())); } p->release(); return 0; } if (p->hatAt(0, "[") && p->hatAt(p->getLength() - 1, "]")) { Text* it = p->getTeilText(1, p->getLength() - 1); int i = (int)*it; it->release(); if (i >= 0 && foundValue->asArray()->getLength() > i) { if (removedPartsValidationResults) { for (JSONValidationResult* res : temporaryInvalidParts) { removedPartsValidationResults->add( dynamic_cast( res->getThis())); } } JSONValue* tmp = foundValue->clone(); if (valid) tmp->asArray()->setValue(i, valid); else tmp->asArray()->removeValue(i); JSONValidationResult* res = JSONValidator( dynamic_cast(expected->getThis())) .validate(tmp); res->addBasePath(path); if (res->isValid()) { res->release(); p->release(); return tmp; } else if (res->isDifferent(this) || !valid) { p->release(); tmp->release(); JSONValue* result = res->getValidPart(removedPartsValidationResults); res->release(); return result; } tmp->release(); res->release(); } } } else if (foundValue->getType() == JSONType::OBJECT) { if (!valid && (!expected->hasAttribute("removeInvalidEntries") || !expected->getAttributeValue("removeInvalidEntries") .istGleich("true"))) { if (removedPartsValidationResults) { if (temporaryInvalidParts.getEintragAnzahl() == 1) { if (reason) reason->release(); reason = temporaryInvalidParts.get(0); } else { for (JSONValidationResult* res : temporaryInvalidParts) { if (res->isDifferent(this)) { removedPartsValidationResults->add( dynamic_cast( res->getThis())); } } } removedPartsValidationResults->add( dynamic_cast(getThis())); } p->release(); return 0; } if (p->hatAt(0, ".")) { if (removedPartsValidationResults) { for (JSONValidationResult* res : temporaryInvalidParts) { removedPartsValidationResults->add( dynamic_cast( res->getThis())); } } Text* at = p->getTeilText(1); Text attr = *at; at->release(); JSONValue* tmp = foundValue->clone(); tmp->asObject()->removeValue(attr); if (valid) tmp->asObject()->addValue(attr, valid); JSONValidationResult* res = JSONValidator( dynamic_cast(expected->getThis())) .validate(tmp); res->addBasePath(path); if (res->isValid()) { res->release(); p->release(); return tmp; } else if (res->isDifferent(this) || !valid) { p->release(); tmp->release(); JSONValue* result = res->getValidPart(removedPartsValidationResults); res->release(); return result; } tmp->release(); res->release(); } } p->release(); if (valid) valid->release(); } if (removedPartsValidationResults) { removedPartsValidationResults->add( dynamic_cast(getThis())); } return 0; } Text JSONTypeMissmatch::getPath() const { return path; } bool JSONTypeMissmatch::isDifferent(const JSONValidationResult* zResult) const { const JSONTypeMissmatch* casted = dynamic_cast(zResult); if (casted == 0) return 1; if (!casted->getPath().istGleich(path)) return 1; return reason->isDifferent(casted->reason); } void Framework::JSON::Validator::JSONTypeMissmatch::addBasePath(Text basePath) { path = basePath + path; if (reason) reason->addBasePath(basePath); } #pragma endregion Content #pragma region JSONUnknownValue JSONUnknownValue::JSONUnknownValue(Text path, JSONValue* foundValue) : JSONValidationResult() { this->path = path; this->foundValue = foundValue; } JSONUnknownValue::~JSONUnknownValue() { foundValue->release(); } bool JSONUnknownValue::isValid() const { return 0; } Text JSONUnknownValue::getInvalidInfo(int indent) const { Text ind = ""; ind.fillText(' ', indent); Text error = ""; error.append() << ind.getText() << "Unknown Value at '" << path.getText() << "'. Value found:\n" << ind.getText() << foundValue->toString().getText() << "\n"; return error; } JSONValue* JSONUnknownValue::getValidPart( RCArray* removedPartsValidationResults) { if (removedPartsValidationResults) { removedPartsValidationResults->add( dynamic_cast(getThis())); } return 0; } Text JSONUnknownValue::getPath() const { return path; } bool JSONUnknownValue::isDifferent(const JSONValidationResult* zResult) const { const JSONUnknownValue* casted = dynamic_cast(zResult); if (casted == 0) return 1; if (!casted->getPath().istGleich(path)) return 1; return 0; } void Framework::JSON::Validator::JSONUnknownValue::addBasePath(Text basePath) { path = basePath + path; } #pragma endregion Content #pragma region JSONMissingValue JSONMissingValue::JSONMissingValue(Text path, XML::Element* expected) : JSONValidationResult() { this->path = path; this->expected = expected; } JSONMissingValue::~JSONMissingValue() { expected->release(); } bool JSONMissingValue::isValid() const { return 0; } Text JSONMissingValue::getInvalidInfo(int indent) const { Text ind = ""; ind.fillText(' ', indent); Text error = ""; error.append() << ind.getText() << "Missing Value at '" << path.getText() << "'. Expected type:\n" << ind.getText() << expected->toString().getText() << "\n"; return error; } JSONValue* JSONMissingValue::getValidPart( RCArray* removedPartsValidationResults) { if (expected->hasAttribute("default")) { JSONValue* def = Parser::getValue(expected->getAttributeValue("default")); JSONValidationResult* res = JSONValidator(dynamic_cast(expected->getThis())) .validate(def); res->addBasePath(path); if (res->isValid()) { res->release(); return def; } else if (res->isDifferent(this)) { def->release(); JSONValue* result = res->getValidPart(removedPartsValidationResults); res->release(); return result; } def->release(); } if (removedPartsValidationResults) { removedPartsValidationResults->add( dynamic_cast(getThis())); } return 0; } Text JSONMissingValue::getPath() const { return path; } bool JSONMissingValue::isDifferent(const JSONValidationResult* zResult) const { const JSONMissingValue* casted = dynamic_cast(zResult); if (casted == 0) return 1; if (!casted->getPath().istGleich(path)) return 1; return 0; } void Framework::JSON::Validator::JSONMissingValue::addBasePath(Text basePath) { path = basePath + path; } #pragma endregion Content #pragma region JSONMissingOneOf JSONMissingOneOf::JSONMissingOneOf(Text path, XML::Editor expected) : JSONValidationResult() { this->path = path; for (XML::Element* e : expected) this->expected.add(dynamic_cast(e->getThis())); } JSONMissingOneOf::~JSONMissingOneOf() {} bool JSONMissingOneOf::isValid() const { return 0; } Text JSONMissingOneOf::getInvalidInfo(int indent) const { Text ind = ""; ind.fillText(' ', indent); Text error = ""; error.append() << ind.getText() << "Missing Value at '" << path.getText() << "'. The value should have one of the specified types:\n"; ind += " "; for (XML::Element* e : expected) { error.append() << ind.getText() << e->toString().getText() << "\n"; } return error; } JSONValue* JSONMissingOneOf::getValidPart( RCArray* removedPartsValidationResults) { for (XML::Element* e : expected) { if (e->hasAttribute("default")) { JSONValue* defaultValue = Parser::getValue(e->getAttributeValue("default")); if (defaultValue) { JSONValue* valid = JSONValidator(dynamic_cast(e->getThis())) .getValidParts( defaultValue, removedPartsValidationResults); defaultValue->release(); if (valid) { return valid; } } } } if (removedPartsValidationResults) { removedPartsValidationResults->add( dynamic_cast(getThis())); } // multiple possibilities are undecidable return 0; } Text JSONMissingOneOf::getPath() const { return path; } bool JSONMissingOneOf::isDifferent(const JSONValidationResult* zResult) const { const JSONMissingOneOf* casted = dynamic_cast(zResult); if (casted == 0) return 1; if (!casted->getPath().istGleich(path)) return 1; return 0; } void Framework::JSON::Validator::JSONMissingOneOf::addBasePath(Text basePath) { path = basePath + path; } #pragma endregion Content #pragma region JSONNoTypeMatching JSONNoTypeMatching::JSONNoTypeMatching(Text path, JSONValue* foundValue, RCArray& expected, RCArray& reasons) : JSONValidationResult() { this->path = path; this->foundValue = foundValue; this->expected = expected; this->reasons = reasons; } JSONNoTypeMatching::~JSONNoTypeMatching() { foundValue->release(); } bool JSONNoTypeMatching::isValid() const { return 0; } Text JSONNoTypeMatching::getInvalidInfo(int indent) const { Text ind = ""; ind.fillText(' ', indent); Text error = ""; error.append() << ind.getText() << "Found Value at '" << path.getText() << "' did not match any of the specified types\n"; Text ind2 = ind + " "; error.append() << ind.getText() << "Reasons:\n"; for (JSONValidationResult* reason : reasons) { error += reason->getInvalidInfo(indent + 4); } return error; } JSONValue* JSONNoTypeMatching::getValidPart( RCArray* removedPartsValidationResults) { RCArray tempErrors; for (JSONValidationResult* reason : reasons) { JSONValue* result = reason->getValidPart(&tempErrors); if (result) { return result; } } if (removedPartsValidationResults) { removedPartsValidationResults->add( dynamic_cast(getThis())); reasons.leeren(); for (JSONValidationResult* error : tempErrors) { reasons.add(dynamic_cast(error->getThis())); } } // multiple possibilities are undecidable return 0; } Text JSONNoTypeMatching::getPath() const { return path; } bool JSONNoTypeMatching::isDifferent(const JSONValidationResult* zResult) const { const JSONNoTypeMatching* casted = dynamic_cast(zResult); if (casted == 0) return 1; if (!casted->getPath().istGleich(path)) return 1; for (int i = 0; i < reasons.getEintragAnzahl(); i++) { if (reasons.z(i)->isDifferent(casted->reasons.z(i))) return 1; } return 0; } void Framework::JSON::Validator::JSONNoTypeMatching::addBasePath(Text basePath) { path = basePath + path; for (JSONValidationResult* reason : reasons) { reason->addBasePath(basePath); } } #pragma endregion Content #pragma region JSONValidValue JSONValidValue::JSONValidValue(Text path, JSONValue* value) : JSONValidationResult() { this->path = path; this->value = value; } JSONValidValue::~JSONValidValue() { value->release(); } bool JSONValidValue::isValid() const { return 1; } Text JSONValidValue::getInvalidInfo(int indent) const { return ""; } JSONValue* JSONValidValue::getValidPart( RCArray* removedPartsValidationResults) { return value->clone(); } Text JSONValidValue::getPath() const { return path; } bool JSONValidValue::isDifferent(const JSONValidationResult* zResult) const { const JSONValidValue* casted = dynamic_cast(zResult); if (casted == 0) return 1; if (!casted->getPath().istGleich(path)) return 1; return 0; } void Framework::JSON::Validator::JSONValidValue::addBasePath(Text basePath) { path = basePath + path; } #pragma endregion Content #pragma endregion Content #pragma region JSONValidator JSONValidator::JSONValidator(XML::Element* constraints) : ReferenceCounter(), constraints(constraints) { for (XML::Element* e : constraints->select() .selectAllElements() .whereNameEquals("object") .whereAttributeExists("id")) { Framework::Text id = e->getAttributeValue("id"); typeConstraints.set(id, id.getLength(), e); } } JSONValidator::~JSONValidator() { constraints->release(); } JSONValidationResult* JSONValidator::validate(JSONValue* zValue) const { return validate(zValue, constraints, ""); } bool JSONValidator::isValid(JSONValue* zValue) const { JSONValidationResult* res = validate(zValue); if (res->isValid()) { res->release(); return 1; } res->release(); return 0; } JSONValue* JSONValidator::getValidParts(JSONValue* zValue, RCArray* removedPartsValidationResults) const { JSONValidationResult* res = validate(zValue); if (res->isValid()) { res->release(); return zValue->clone(); } JSONValue* valid = res->getValidPart(removedPartsValidationResults); res->release(); return valid; } XML::Element* JSONValidator::zConstraints() { return constraints; } JSONValidationResult* JSONValidator::validate( JSONValue* zValue, XML::Element* zConstraints, Text path) const { if (zConstraints->getName().istGleich("oneOf")) { return validateMultipleTypes(zValue, zConstraints, path); } switch (zValue->getType()) { case JSONType::NULL_: if (!zConstraints->hasAttribute("nullable") || !zConstraints->getAttributeValue("nullable").istGleich("true")) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } break; case JSONType::BOOLEAN: if (!zConstraints->getName().istGleich("bool")) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } else if (zConstraints->hasAttribute("equals")) { if (!zConstraints->getAttributeValue("equals").istGleich("true") == !zValue->asBool()->getBool()) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } } break; case JSONType::NUMBER: if (!zConstraints->getName().istGleich("number")) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } else { if (zConstraints->hasAttribute("equals") && (double)zConstraints->getAttributeValue("equals") != zValue->asNumber()->getNumber()) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("lessOrEqual") && zValue->asNumber()->getNumber() > (double)zConstraints->getAttributeValue("lessOrEqual")) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("greaterOrEqual") && zValue->asNumber()->getNumber() < (double)zConstraints->getAttributeValue( "greaterOrEqual")) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("less") && zValue->asNumber()->getNumber() >= (double)zConstraints->getAttributeValue("less")) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("greater") && zValue->asNumber()->getNumber() <= (double)zConstraints->getAttributeValue("greater")) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } } break; case JSONType::STRING: if (!zConstraints->getName().istGleich("string")) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } else { if (zConstraints->hasAttribute("equals") && !zConstraints->getAttributeValue("equals").istGleich( zValue->asString()->getString())) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("contains") && zValue->asString()->getString().hat( zConstraints->getAttributeValue("contains").getText())) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("startsWith") && zValue->asString()->getString().positionVon( zConstraints->getAttributeValue("startsWith").getText()) == 0) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("endsWith") && zValue->asString()->getString().positionVon( zConstraints->getAttributeValue("endsWith").getText()) == zValue->asString()->getString().getLength() - zConstraints->getAttributeValue("endsWith") .getLength()) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("oneOf")) { JSONArray* array = Parser::getValue(zConstraints->getAttributeValue("oneOf")) ->asArray(); bool ok = 0; for (JSONValue* v : *array) { if (v->asString()->getString().istGleich( zValue->asString()->getString())) { ok = 1; break; } } array->release(); if (!ok) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } } } break; case JSONType::ARRAY: if (!zConstraints->getName().istGleich("array")) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } else { int index = 0; for (JSON::JSONValue* value : *zValue->asArray()) { Text p = path; p += "["; p += index; p += "]"; JSONValidationResult* res = validateMultipleTypes(value, zConstraints, p); if (!res->isValid()) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), res); } res->release(); index++; } } break; case JSONType::OBJECT: if (zConstraints->getName().istGleich("objectRef")) { Text id = zConstraints->getAttributeValue("ref"); XML::Element* e = typeConstraints.get(id, id.getLength()); if (e) { zConstraints = e; } } if (!zConstraints->getName().istGleich("object")) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } else { JSON::JSONObject* obj = zValue->asObject(); for (auto i = obj->getFields(); i; i++) { Text p = path; p += "."; p += i.val(); if (!zConstraints->selectChildsByAttribute("name", i.val()) .exists()) { if (!zConstraints ->getAttributeValue("allowAdditionalAttributes") .istGleich("true")) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast( zConstraints->getThis()), new JSONUnknownValue( p, zValue->asObject()->getValue(i.val()))); } } else { XML::Editor tmp = zConstraints->selectChildsByAttribute( "name", i.val()); JSONValidationResult* res = validateMultipleTypes( zValue->asObject()->zValue(i.val()), tmp.begin().val(), p); if (!res->isValid()) { return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast( zConstraints->getThis()), res); } res->release(); } } for (XML::Element* constraint : zConstraints->selectChildren()) { if (!zValue->asObject()->hasValue( constraint->getAttributeValue("name"))) { XML::Editor tmp = constraint->selectChildren(); bool optional = true; std::function checkOptional; checkOptional = [&optional, &checkOptional]( XML::Element* zElement) { if (zElement->getName().istGleich("oneOf")) { XML::Editor tmp = zElement->selectChildren(); tmp.forEach(checkOptional); } else { optional &= zElement->hasAttribute("optional") && zElement->getAttributeValue("optional") .istGleich("true"); } }; tmp.forEach(checkOptional); if (!optional) { Text p = path; p += "."; p += constraint->getAttributeValue("name"); if (constraint->getChildCount() != 1) return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast( zConstraints->getThis()), new JSONMissingOneOf(p, tmp)); return new JSONTypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast( zConstraints->getThis()), new JSONMissingValue(p, dynamic_cast( tmp.begin()->getThis()))); } } } } break; } return new JSONValidValue( path, dynamic_cast(zValue->getThis())); } JSONValidationResult* JSONValidator::validateMultipleTypes( JSONValue* zChildValue, XML::Element* zPossibleChildConstraints, Text childPath) const { if (zPossibleChildConstraints->getChildCount() == 1 && !zPossibleChildConstraints->hasAttribute("typeSpecifiedBy")) { XML::Editor children = zPossibleChildConstraints->selectChildren(); return validate(zChildValue, children.begin().val(), childPath); // only one type is possible } bool hasTypeAttr = 0; RCArray possibleConstraints; if (zPossibleChildConstraints->hasAttribute("typeSpecifiedBy")) { // try to find the correct constraints based on the type attribute hasTypeAttr = 1; Text typeAttr = zPossibleChildConstraints->getAttributeValue("typeSpecifiedBy"); if (zChildValue->getType() == JSONType::OBJECT) { if (zChildValue->asObject()->hasValue(typeAttr)) { JSONValue* typeV = zChildValue->asObject()->zValue(typeAttr); for (XML::Element* constraint : zPossibleChildConstraints->selectChildsByName("object") .whereChildWithAttributeExists("name", typeAttr)) { XML::Editor nameChildren = constraint->selectChildsByAttribute("name", typeAttr); XML::Element* typeAttrContraints = nameChildren.begin().val(); JSONValidationResult* res = validateMultipleTypes( typeV, typeAttrContraints, childPath + "." + typeAttr); if (res->isValid()) possibleConstraints.add( dynamic_cast(constraint->getThis())); res->release(); } } } } if (hasTypeAttr && possibleConstraints.getEintragAnzahl() == 1) return validate(zChildValue, possibleConstraints.begin().val(), childPath); // if the type is clear else if (hasTypeAttr && possibleConstraints.getEintragAnzahl() > 0) { // more then one type is possible RCArray invalidResults; for (XML::Element* constraint : possibleConstraints) { JSONValidationResult* res = validate(zChildValue, constraint, childPath); invalidResults.add(res); if (res->isValid()) return new JSONValidValue(childPath, dynamic_cast(zChildValue->getThis())); } return new JSONNoTypeMatching(childPath, dynamic_cast(zChildValue->getThis()), possibleConstraints, invalidResults); } // try all types possibleConstraints.leeren(); RCArray invalidResults; for (XML::Element* constraint : zPossibleChildConstraints->selectChildren()) { JSONValidationResult* res = validate(zChildValue, constraint, childPath); invalidResults.add(res); if (res->isValid()) return new JSONValidValue( childPath, dynamic_cast(zChildValue->getThis())); possibleConstraints.add( dynamic_cast(constraint->getThis())); } return new JSONNoTypeMatching(childPath, dynamic_cast(zChildValue->getThis()), possibleConstraints, invalidResults); } StringValidationBuilder* JSONValidator::buildForString() { return new StringValidationBuilder( [](XML::Element& e) { return new JSONValidator(e.dublicate()); }); } NumberValidationBuilder* JSONValidator::buildForNumber() { return new NumberValidationBuilder( [](XML::Element& e) { return new JSONValidator(e.dublicate()); }); } BoolValidationBuilder* JSONValidator::buildForBool() { return new BoolValidationBuilder( [](XML::Element& e) { return new JSONValidator(e.dublicate()); }); } ObjectValidationBuilder* JSONValidator::buildForObject() { return new ObjectValidationBuilder( [](XML::Element& e) { return new JSONValidator(e.dublicate()); }); } ArrayValidationBuilder* JSONValidator::buildForArray() { return new ArrayValidationBuilder( [](XML::Element& e) { return new JSONValidator(e.dublicate()); }); } JSONValidator* JSONValidator::buildForObjectReference(Text objectId) { return new JSONValidator( new XML::Element(Text(""))); } OneOfValidationBuilder* JSONValidator::buildForOneOf() { return new OneOfValidationBuilder( [](XML::Element& e) { return new JSONValidator(e.dublicate()); }); } #pragma endregion Content