Browse Source

improved json parser error messages and fixed default precision of Text was 0 and not -1

Kolja Strohm 1 year ago
parent
commit
8abb323f2d
7 changed files with 169 additions and 12 deletions
  1. 1 1
      CharMap.h
  2. BIN
      Framework Tests/Framwork.dll
  3. 28 0
      Framework Tests/Json.cpp
  4. 28 6
      JSON.cpp
  5. 108 1
      JSON.h
  6. 3 3
      Text.cpp
  7. 1 1
      Trie.h

+ 1 - 1
CharMap.h

@@ -91,7 +91,7 @@ namespace Framework
                      : 0;
         }
 
-        T* z(unsigned char c)
+        T* z(unsigned char c) const
         {
             return entries[(int)c];
         }

BIN
Framework Tests/Framwork.dll


+ 28 - 0
Framework Tests/Json.cpp

@@ -483,6 +483,34 @@ namespace FrameworkTests
             std::cout.rdbuf(&charDebugOutput);
         }
 
+        TEST_METHOD(ValidGreaterThenTest)
+        {
+            Framework::JSON::JSONValue* value
+                = Framework::JSON::Parser::getValue("{\"test\": 0.001}");
+            Framework::JSON::Validator::JSONValidator* validator
+                = Framework::JSON::Validator::JSONValidator::buildForObject()
+                      ->withRequiredNumber("test")
+                      ->whichIsGreaterThen(0)
+                      ->finishNumber()
+                      ->finishObject();
+            Framework::RCArray<Framework::JSON::Validator::JSONValidationResult>
+                removed;
+            Assert::IsTrue(
+                validator->isValid(value), L"Valid value is marked as invalid");
+            Framework::JSON::JSONValue* result
+                = validator->getValidParts(value, &removed);
+            Assert::IsTrue(
+                result->asObject()->zValue("test")->asNumber()->getNumber()
+                    == value->asObject()
+                           ->zValue("test")
+                           ->asNumber()
+                           ->getNumber(),
+                L"Get Valid Parts made the valid value invalid");
+            value->release();
+            validator->release();
+            result->release();
+        }
+
         TEST_METHOD (ValidTest)
         {
             Framework::JSON::Validator::JSONValidator* validator

+ 28 - 6
JSON.cpp

@@ -1202,6 +1202,10 @@ XML::Element* JSONValidator::zConstraints()
 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_:
@@ -1462,11 +1466,22 @@ JSONValidationResult* JSONValidator::validate(
                 {
                     XML::Editor tmp = constraint->selectChildren();
                     bool optional = true;
-                    tmp.forEach([&optional](XML::Element* zElement) {
-                        optional &= zElement->hasAttribute("optional")
-                                 && zElement->getAttributeValue("optional")
-                                        .istGleich("true");
-                    });
+                    std::function<void(XML::Element*)> 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;
@@ -1500,7 +1515,8 @@ JSONValidationResult* JSONValidator::validateMultipleTypes(
     XML::Element* zPossibleChildConstraints,
     Text childPath) const
 {
-    if (zPossibleChildConstraints->getChildCount() == 1)
+    if (zPossibleChildConstraints->getChildCount() == 1
+        && !zPossibleChildConstraints->hasAttribute("typeSpecifiedBy"))
     {
         XML::Editor children = zPossibleChildConstraints->selectChildren();
         return validate(zChildValue,
@@ -1614,4 +1630,10 @@ JSONValidator* JSONValidator::buildForObjectReference(Text objectId)
         new XML::Element(Text("<objectRef ref=\"") + objectId + Text("\"/>")));
 }
 
+OneOfValidationBuilder<JSONValidator>* JSONValidator::buildForOneOf()
+{
+    return new OneOfValidationBuilder<JSONValidator>(
+        [](XML::Element& e) { return new JSONValidator(e.dublicate()); });
+}
+
 #pragma endregion Content

+ 108 - 1
JSON.h

@@ -407,6 +407,8 @@ namespace Framework
 
             template<typename T> class ArrayValidationBuilder;
 
+            template<typename T> class OneOfValidationBuilder;
+
             class JSONValidator : public Framework::ReferenceCounter
             {
             private:
@@ -459,6 +461,8 @@ namespace Framework
                     JSONValidator>* buildForObject();
                 __declspec(dllexport) static ArrayValidationBuilder<
                     JSONValidator>* buildForArray();
+                __declspec(dllexport) static OneOfValidationBuilder<
+                    JSONValidator>* buildForOneOf();
                 __declspec(
                     dllexport) static JSONValidator* buildForObjectReference(Text
                         objectId);
@@ -501,7 +505,7 @@ namespace Framework
                     return this;
                 }
 
-                StringValidationBuilder<T>* whichIsOneOf(RCArray<Text>& values)
+                StringValidationBuilder<T>* whichIsOneOf(RCArray<Text> values)
                 {
                     JSONArray arr;
                     for (Text* str : values)
@@ -510,6 +514,24 @@ namespace Framework
                     return this;
                 }
 
+                StringValidationBuilder<T>* whichIsOneOf(
+                    std::initializer_list<Text> values)
+                {
+                    JSONArray arr;
+                    for (Text str : values)
+                        arr.addValue(new JSONString(str));
+                    element.setAttribute("oneOf", arr.toString());
+                    return this;
+                }
+
+                StringValidationBuilder<T>* whichIs(Text value)
+                {
+                    JSONArray arr;
+                    arr.addValue(new JSONString(value));
+                    element.setAttribute("oneOf", arr.toString());
+                    return this;
+                }
+
                 StringValidationBuilder<T>* withDefault(Text value)
                 {
                     element.setAttribute(
@@ -983,6 +1005,91 @@ namespace Framework
                     return result;
                 }
             };
+
+            template<typename T> class OneOfValidationBuilder
+            {
+            private:
+                XML::Element element;
+                std::function<T*(XML::Element& element)> builder;
+
+            public:
+                OneOfValidationBuilder(
+                    std::function<T*(XML::Element& element)> builder)
+                    : element("<oneOf></oneOf>"),
+                      builder(builder)
+                {}
+
+                StringValidationBuilder<OneOfValidationBuilder<T>>*
+                addAcceptedStringInArray()
+                {
+                    return new StringValidationBuilder<
+                        OneOfValidationBuilder<T>>([this](XML::Element& e) {
+                        element.addChild(e.dublicate());
+                        return this;
+                    });
+                }
+
+                NumberValidationBuilder<OneOfValidationBuilder<T>>*
+                addAcceptedNumberInArray()
+                {
+                    return new NumberValidationBuilder<
+                        OneOfValidationBuilder<T>>([this](XML::Element& e) {
+                        element.addChild(e.dublicate());
+                        return this;
+                    });
+                }
+
+                BoolValidationBuilder<OneOfValidationBuilder<T>>*
+                addAcceptedBooleanInArray()
+                {
+                    return new BoolValidationBuilder<OneOfValidationBuilder<T>>(
+                        [this](XML::Element& e) {
+                            element.addChild(e.dublicate());
+                            return this;
+                        });
+                }
+
+                ObjectValidationBuilder<OneOfValidationBuilder<T>>*
+                addAcceptedObjectInArray()
+                {
+                    return new ObjectValidationBuilder<
+                        OneOfValidationBuilder<T>>([this](XML::Element& e) {
+                        element.addChild(e.dublicate());
+                        return this;
+                    });
+                }
+
+                ArrayValidationBuilder<OneOfValidationBuilder<T>>*
+                addAcceptedArrayInArray()
+                {
+                    return new ArrayValidationBuilder<
+                        OneOfValidationBuilder<T>>([this](XML::Element& e) {
+                        element.addChild(e.dublicate());
+                        return this;
+                    });
+                }
+
+                OneOfValidationBuilder<T>* addAcceptedType(
+                    JSONValidator* validator)
+                {
+                    element.addChild(validator->zConstraints()->dublicate());
+                    validator->release();
+                    return this;
+                }
+
+                OneOfValidationBuilder<T>* typeSpecifiedByAttribute(Text name)
+                {
+                    element.setAttribute("typeSpecifiedBy", name);
+                    return this;
+                }
+
+                T* finishOneOf()
+                {
+                    T* result = builder(element);
+                    delete this;
+                    return result;
+                }
+            };
         } // namespace Validator
     }     // namespace JSON
 } // namespace Framework

+ 3 - 3
Text.cpp

@@ -40,7 +40,7 @@ Text::Text()
       txt(0),
       suchGBeg(0),
       suchGEnd(0),
-      precision(0),
+      precision(-1),
       stringWriter(DynamicBuffer([this](std::stringbuf& buf) {
           std::string str = buf.str();
           this->append(str.c_str());
@@ -270,7 +270,7 @@ void Text::append(unsigned int num)
 void Text::append(double num)
 {
     std::stringstream ss;
-    ss.precision(precision);
+    if (precision >= 0) ss.precision(precision);
     ss << std::fixed << num;
     append(ss.str().c_str());
 }
@@ -278,7 +278,7 @@ void Text::append(double num)
 void Text::append(float num)
 {
     std::stringstream ss;
-    ss.precision(precision);
+    if (precision >= 0) ss.precision(precision);
     ss << std::fixed << num;
     append(ss.str().c_str());
 }

+ 1 - 1
Trie.h

@@ -255,7 +255,7 @@ namespace Framework
             }
         }
 
-        T* z(const char* addr, int addrLen)
+        T* z(const char* addr, int addrLen) const
         {
             if (!addrLen)
                 return map->zValue();