Jelajahi Sumber

Initial commit

Alexey Edelev 4 tahun lalu
melakukan
a8ef925505
8 mengubah file dengan 735 tambahan dan 0 penghapusan
  1. 34 0
      .gitignore
  2. 42 0
      CMakeLists.txt
  3. 21 0
      LICENSE
  4. 297 0
      microjson.cpp
  5. 90 0
      microjson.h
  6. 8 0
      microjsonConfig.cmake.in
  7. 5 0
      tests/CMakeLists.txt
  8. 238 0
      tests/main.cpp

+ 34 - 0
.gitignore

@@ -0,0 +1,34 @@
+# Prerequisites
+*.d
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+*.user

+ 42 - 0
CMakeLists.txt

@@ -0,0 +1,42 @@
+cmake_minimum_required(VERSION 3.6)
+
+project(microjson VERSION 0.1.0 LANGUAGES CXX)
+
+set(TARGET microjson)
+set(TARGET_STATIC ${TARGET}Static)
+set(TARGET_EXPORT ${TARGET}Targets)
+set(TARGET_CONFIG ${TARGET}Config)
+
+set(TARGET_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}/${TARGET})
+set(TARGET_LIB_DIR ${CMAKE_INSTALL_LIBDIR})
+set(TARGET_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
+set(TARGET_BINDIR ${CMAKE_INSTALL_BINDIR})
+
+add_library(${TARGET} microjson.cpp)
+set_target_properties(${TARGET} PROPERTIES VERSION ${PROJECT_VERSION} PUBLIC_HEADER "microjson.h" OUTPUT_NAME ${TARGET})
+target_include_directories(${TARGET} PUBLIC
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include/${TARGET}>
+    $<INSTALL_INTERFACE:${TARGET_INCLUDE_DIR}>
+)
+
+install(TARGETS ${TARGET}
+    EXPORT ${TARGET_EXPORT} COMPONENT dev
+    ARCHIVE DESTINATION ${TARGET_LIB_DIR} COMPONENT lib
+    PUBLIC_HEADER DESTINATION ${TARGET_INCLUDE_DIR} COMPONENT dev
+    LIBRARY DESTINATION ${TARGET_LIB_DIR} COMPONENT lib
+    RUNTIME DESTINATION ${TARGET_BINDIR} COMPONENT lib)
+
+install(EXPORT ${TARGET_EXPORT} FILE ${TARGET_EXPORT}.cmake DESTINATION ${TARGET_CMAKE_DIR} COMPONENT dev)
+
+include(CMakePackageConfigHelpers)
+configure_package_config_file(
+    "${TARGET_CONFIG}.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_CONFIG}.cmake"
+    INSTALL_DESTINATION "${TARGET_CMAKE_DIR}")
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_CONFIG}.cmake" DESTINATION "${TARGET_CMAKE_DIR}" COMPONENT dev)
+
+export(TARGETS ${TARGET} FILE ${TARGET_EXPORT}.cmake)
+
+set(CMAKE_PREFIX_PATH ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_PREFIX_PATH})
+add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tests")

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Alexey Edelev <semlanik@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 297 - 0
microjson.cpp

@@ -0,0 +1,297 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of microjson project https://git.semlanik.org/semlanik/microjson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "microjson.h"
+
+#include <iostream>
+#include <functional>
+
+#ifdef MICROJSON_DEBUG
+    #define micorJsonDebug std::cout
+#else
+    struct micorJsonNull : public std::ostream {
+    };
+    static micorJsonNull nullout;
+    #define micorJsonDebug nullout
+#endif
+size_t microjson::extractNextProperty(const char *buffer, size_t size, JsonProperty &property) {
+    if (buffer == nullptr || size == 0 || size == SIZE_MAX) {
+        return SIZE_MAX;
+    }
+
+    enum ParsingState {
+        LookingForNameBegin,
+        LookingForNameEnd,
+        LookingForSeparator,
+        LookingForValueBegin,
+        LookingForValueEnd
+    };
+
+    property.nameBegin = SIZE_MAX;
+    property.nameEnd = SIZE_MAX;
+    property.valueBegin = SIZE_MAX;
+    property.valueEnd = SIZE_MAX;
+    property.type = JsonInvalidType;
+
+    int valueBracesCounter = -1;
+    int valueBracketsCounter = -1;
+    int trueCounter = 3;
+    int falseCounter = 4;
+
+    std::function<bool(void)> valueEndMarker;
+    ParsingState state = LookingForNameBegin;
+    size_t i = 0;
+    for (; i < size; i++) {
+        const char byte = buffer[i];
+        if (state == LookingForNameBegin) {
+            if (byte == '\n' || byte == ' ' || byte == '\r' || byte == '\t') {
+                micorJsonDebug << "Skip space" << std::endl;
+                continue;
+            }
+            if (byte == '"') {
+                micorJsonDebug << "Found name begin" << std::endl;
+                property.nameBegin = i + 1;
+                state = LookingForNameEnd;
+            } else {
+                std::cerr << "Not found name begin, unexpected" << std::endl;
+                break;
+            }
+        } else if (state == LookingForNameEnd && byte == '"') {
+            if (byte == '\n' || byte == ' ' || byte == '\r' || byte == '\t') {
+                micorJsonDebug << "Skip space" << std::endl;
+                continue;
+            }
+            if (i > 0 && buffer[i - 1] == '\\') {
+                micorJsonDebug << "'\"' found in name" << std::endl;
+                continue;
+            }
+            micorJsonDebug << "Found name end" << std::endl;
+            property.nameEnd = i;
+            state = LookingForSeparator;
+        } else if (state == LookingForSeparator) {
+            if (byte == '\n' || byte == ' ' || byte == '\r' || byte == '\t') {
+                micorJsonDebug << "Skip space" << std::endl;
+                continue;
+            }
+            if (byte == ':') {
+                micorJsonDebug << "Found Separator" << std::endl;
+                state = LookingForValueBegin;
+            } else {
+                break;
+            }
+        } else if (state == LookingForValueBegin) {
+            if (byte == '\n' || byte == ' ' || byte == '\r' || byte == '\t') {
+                micorJsonDebug << "Skip space" << std::endl;
+                continue;
+            }
+            switch (byte) {
+            case '"':
+                valueEndMarker = [&](){
+                    return buffer[i] == '"' && buffer[i - 1] != '\\';
+                };
+                property.type = JsonStringType;
+                break;
+            case '-':
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                valueEndMarker = [&buffer, &i](){
+                    if (buffer[i] != '+'
+                            && buffer[i] != '0'
+                            && buffer[i] != '1'
+                            && buffer[i] != '2'
+                            && buffer[i] != '3'
+                            && buffer[i] != '4'
+                            && buffer[i] != '5'
+                            && buffer[i] != '6'
+                            && buffer[i] != '7'
+                            && buffer[i] != '8'
+                            && buffer[i] != '9'
+                            && buffer[i] != 'e'
+                            && buffer[i] != '.') {
+                        i--;
+                        return true;
+                    }
+                    return false;
+                };
+                property.type = JsonNumberType;
+                break;
+            case '{':
+                valueBracesCounter++;
+                valueEndMarker = [&valueBracesCounter](){
+                    return valueBracesCounter < 0;
+                };
+                property.type = JsonObjectType;
+                break;
+            case 't':
+                valueEndMarker = [&trueCounter](){
+                    return trueCounter == 0;
+                };
+                property.type = JsonBoolType;
+                break;
+            case 'f':
+                valueEndMarker = [&falseCounter](){
+                    return falseCounter == 0;
+                };
+                property.type = JsonBoolType;
+                break;
+            case '[':
+                valueBracketsCounter++;
+                valueEndMarker = [&valueBracketsCounter](){
+                    return valueBracketsCounter < 0;
+                };
+                property.type = JsonArrayType;
+                break;
+            }
+
+            if (valueEndMarker) {
+                micorJsonDebug << "Found value begin" << std::endl;
+                property.valueBegin = i;
+                state = LookingForValueEnd;
+            } else {
+                break;
+            }
+        } else if (state == LookingForValueEnd) {
+            switch (byte) {
+            case '}':
+                --valueBracesCounter;
+                break;
+            case '{':
+                --valueBracesCounter;
+                break;
+            case ']':
+                --valueBracketsCounter;
+                break;
+            case '[':
+                ++valueBracketsCounter;
+                break;
+            default:
+                --trueCounter;
+                --falseCounter;
+                break;
+            }
+
+            if (valueEndMarker()) {
+                micorJsonDebug << "Found value end" << std::endl;
+                property.valueEnd = i;
+                break;
+            }
+        }
+    }
+
+    if (property.eofCheck()) {
+        property.valueEnd = size - 1; //EOF case
+        micorJsonDebug << "Found value end at EOF" << std::endl;
+    }
+
+    micorJsonDebug << property.nameBegin << " " << property.nameEnd << " " << property.valueBegin << " " << property.valueEnd << " " << property.type << std::endl;
+    if (property.check()) {
+        if (property.type == JsonStringType) {
+            ++property.valueBegin;
+            --property.valueEnd;
+        }
+        for (size_t j = i + 1; j < size; j++) {
+            const char &byte = buffer[j];
+            if (byte == ',') {
+                return j + 1;
+            } else if (byte != '\n' && byte != ' ' && byte != '\r' && byte != '\t') {
+                micorJsonDebug << "Unexpected: " << byte;
+                return SIZE_MAX;
+            }
+        }
+        return size;
+    } else {
+        micorJsonDebug << "Property check failed" << std::endl;
+    }
+    return SIZE_MAX;
+}
+
+microjson::JsonObject microjson::parseObject(const char *buffer, size_t size) {
+    JsonObject obj;
+
+    if (buffer == nullptr || size == 0 || size == SIZE_MAX) {
+        return obj;
+    }
+
+    size_t objectBeginPosition = SIZE_MAX;
+    size_t objectEndPosition = SIZE_MAX;
+    for (size_t i = 0; i < size; i++) {
+        const char beginByte = buffer[i];
+        const char endByte = buffer[size - i - 1];
+        if (objectBeginPosition == SIZE_MAX) {
+            if (beginByte == '{') {
+                objectBeginPosition = i;
+            } else if (beginByte != '\n' && beginByte != ' ' && beginByte != '\r' && beginByte != '\t') {
+                std::cerr << "Unexpected begin byte" << beginByte << std::endl;
+                break;
+            }
+        }
+
+        if (objectEndPosition == SIZE_MAX) {
+            if (endByte == '}') {
+                objectEndPosition = size - i - 1;
+            } else if (endByte != '\n' && endByte != ' ' && endByte != '\r' && endByte != '\t') {
+                std::cerr << "Unexpected end byte" << endByte << std::endl;
+                break;
+            }
+        }
+
+        if (objectBeginPosition != SIZE_MAX && objectEndPosition != SIZE_MAX) {
+            break;
+        }
+
+    }
+
+    if (objectBeginPosition == SIZE_MAX || objectEndPosition == SIZE_MAX) {
+        return obj;
+    }
+
+    JsonProperty property;
+    buffer += objectBeginPosition + 1;//Skip '{'
+    size = objectEndPosition - objectBeginPosition - 1;//Skip '}'
+
+    micorJsonDebug << "Object buffer size: " << size << " buffer: " << std::string(buffer, size) << std::endl;
+
+    for (size_t nextPropertyPosition = microjson::extractNextProperty(buffer, size, property); nextPropertyPosition != SIZE_MAX; ) {
+        micorJsonDebug << "nextPropertyPosition: " << nextPropertyPosition << "size: " << size << std::endl;
+        if (nextPropertyPosition != SIZE_MAX) {
+            std::string name((buffer + property.nameBegin), property.nameSize());
+            std::string value((buffer + property.valueBegin), property.valueSize());
+            micorJsonDebug << "name: " << name << " value: " << value << std::endl;
+            obj[name] = { value, property.type };
+        }
+        buffer += nextPropertyPosition;
+        size = size - nextPropertyPosition;
+        nextPropertyPosition = microjson::extractNextProperty(buffer, size, property);
+    }
+    return obj;
+}

+ 90 - 0
microjson.h

@@ -0,0 +1,90 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of microjson project https://git.semlanik.org/semlanik/microjson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <unordered_map>
+
+namespace microjson {
+enum JsonType {
+    JsonNumberType,
+    JsonStringType,
+    JsonBoolType,
+    JsonObjectType,
+    JsonArrayType,
+    JsonInvalidType
+};
+
+struct JsonValue {
+    JsonValue() : type(JsonInvalidType) {}
+    JsonValue(const std::string &_value, JsonType _type) :
+    value(_value)
+    , type(_type) {}
+    std::string value;
+    JsonType type;
+};
+
+using JsonObject = std::unordered_map<std::string, JsonValue>;
+
+struct JsonProperty {
+    JsonProperty() : nameBegin(SIZE_MAX)
+      , nameEnd(SIZE_MAX)
+      , valueBegin(SIZE_MAX)
+      , valueEnd(SIZE_MAX)
+      , type(JsonInvalidType){}
+
+    size_t nameBegin;
+    size_t nameEnd;
+    size_t valueBegin;
+    size_t valueEnd;
+    JsonType type;
+
+    size_t nameSize() const {
+        return nameEnd - nameBegin;
+    }
+
+    size_t valueSize() const {
+        return valueEnd - valueBegin + 1;
+    }
+
+    bool check() const {
+        return type != JsonInvalidType && nameBegin != SIZE_MAX && nameEnd != SIZE_MAX &&
+                valueBegin != SIZE_MAX && valueEnd != SIZE_MAX &&
+                nameBegin < nameEnd && nameEnd < valueBegin &&
+                valueBegin <= valueEnd;
+    }
+
+    bool eofCheck() const {
+        return (type == JsonNumberType || type == JsonBoolType) && nameBegin != SIZE_MAX && nameEnd != SIZE_MAX &&
+                valueBegin != SIZE_MAX && valueEnd == SIZE_MAX &&
+                nameBegin < nameEnd && nameEnd < valueBegin;
+    }
+};
+
+extern JsonObject parseObject(const char *buffer, size_t size);
+extern size_t extractNextProperty(const char *buffer, size_t size, JsonProperty &property);
+}

+ 8 - 0
microjsonConfig.cmake.in

@@ -0,0 +1,8 @@
+include(CMakeFindDependencyMacro)
+
+if(NOT TARGET @TARGET@ AND NOT @TARGET@_BINARY_DIR)
+    include("${CMAKE_CURRENT_LIST_DIR}/@TARGET_EXPORT@.cmake")
+endif()
+
+@PACKAGE_INIT@
+

+ 5 - 0
tests/CMakeLists.txt

@@ -0,0 +1,5 @@
+find_package(microjson CONFIG REQUIRED)
+find_package(GTest REQUIRED)
+
+add_executable(microjson_test main.cpp)
+target_link_libraries(microjson_test microjson gtest_main gtest)

+ 238 - 0
tests/main.cpp

@@ -0,0 +1,238 @@
+#include "microjson.h"
+
+#include <iostream>
+#include <string.h>
+#include <gtest/gtest.h>
+
+class MicrojsonDeserializationTest : public ::testing::Test
+{
+public:
+    MicrojsonDeserializationTest() = default;
+};
+
+TEST_F(MicrojsonDeserializationTest, StringValue)
+{
+    const char *buffer1 = "{\"testField\":\"test\"}";
+    size_t size = strlen(buffer1);
+    microjson::JsonObject obj = microjson::parseObject(buffer1, size);
+    auto it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "test");
+
+    obj.clear();
+    const char *buffer2 = "{\"testField\":\"\"}";
+    size = strlen(buffer2);
+    obj = microjson::parseObject(buffer2, size);
+    obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "");
+
+    obj.clear();
+    const char *buffer3 = "{\"testField\":\"t\"}";
+    size = strlen(buffer3);
+    obj = microjson::parseObject(buffer3, size);
+    it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "t");
+
+    obj.clear();
+    const char *buffer4 = "{\"testField\":\"\\\"test\\\"\"}";
+    size = strlen(buffer4);
+    obj = microjson::parseObject(buffer4, size);
+    it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "\\\"test\\\"");
+
+    obj.clear();
+    const char *buffer5 = "{\"testField\":\"\\\"test\"}";
+    size = strlen(buffer5);
+    obj = microjson::parseObject(buffer5, size);
+    it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "\\\"test");
+
+    obj.clear();
+    const char *buffer6 = "{\"testField\":\"\\\"\"}";
+    size = strlen(buffer6);
+    obj = microjson::parseObject(buffer6, size);
+    it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "\\\"");
+
+    obj.clear();
+    const char *buffer7 = "{\"testField\":\"test\",\"testField1\":\"\",\"testField2\":\"t\",\"testField3\":\"\\\"test\\\"\",\"testField4\":\"\\\"test\",\"testField5\":\"\\\"\"}";
+    size = strlen(buffer7);
+    obj = microjson::parseObject(buffer7, size);
+    it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "test");
+
+    it = obj.find("testField1");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField1");
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "");
+
+    it = obj.find("testField2");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField2");
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "t");
+
+    it = obj.find("testField3");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField3");
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "\\\"test\\\"");
+
+    it = obj.find("testField4");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField4");
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "\\\"test");
+
+    it = obj.find("testField5");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField5");
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "\\\"");
+}
+
+TEST_F(MicrojsonDeserializationTest, NumberValue)
+{
+    const char *buffer1 = "{\"testField\":5}";
+    size_t size = strlen(buffer1);
+    microjson::JsonObject obj = microjson::parseObject(buffer1, size);
+    auto it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "5");
+
+    obj.clear();
+    const char *buffer2 = "{\"testField\":56}";
+    size = strlen(buffer2);
+    obj = microjson::parseObject(buffer2, size);
+    obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "56");
+
+    obj.clear();
+    const char *buffer3 = "{\"testField\":0}";
+    size = strlen(buffer3);
+    obj = microjson::parseObject(buffer3, size);
+    it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "0");
+
+    obj.clear();
+    const char *buffer4 = "{\"testField\":-5}";
+    size = strlen(buffer4);
+    obj = microjson::parseObject(buffer4, size);
+    it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "-5");
+
+    obj.clear();
+    const char *buffer5 = "{\"testField\":-131}";
+    size = strlen(buffer5);
+    obj = microjson::parseObject(buffer5, size);
+    it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "-131");
+
+    obj.clear();
+    const char *buffer6 = "{\"testField\":5,\"testField1\":56,\"testField2\":0,\"testField3\":-8,\"testField4\":-124}";
+    size = strlen(buffer6);
+    obj = microjson::parseObject(buffer6, size);
+    it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "5");
+
+    it = obj.find("testField1");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField1");
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "56");
+
+    it = obj.find("testField2");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField2");
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "0");
+
+    it = obj.find("testField3");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField3");
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "-8");
+
+    it = obj.find("testField4");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField4");
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "-124");
+}
+
+TEST_F(MicrojsonDeserializationTest, TextSpacesCutting)
+{
+    const char *buffer1 = "\n\r\t  {\n\r\t \"testField\"\n\r\t :   5\n\t \r}\n\t \r";
+    size_t size = strlen(buffer1);
+    microjson::JsonObject obj = microjson::parseObject(buffer1, size);
+    auto it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_STREQ(it->first.c_str(), "testField");
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "5");
+
+    const char *buffer2 = "\n\r\t  {\n\r\t \"testField\"\n\r\t :   5\n\t \r, \n\r\t \"testField2\"\n\r\t :   -0.932e+10\n\t \r}\n\t \r";
+    size = strlen(buffer2);
+    obj = microjson::parseObject(buffer2, size);
+    it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "5");
+
+    it = obj.find("testField2");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_EQ(it->second.type, microjson::JsonNumberType);
+    EXPECT_STREQ(it->second.value.c_str(), "-0.932e+10");
+
+
+    const char *buffer3 = "\n\r\t  {\n\r\t \"testField\"\n\r\t :   \"5\"\n\t \r, \n\r\t \"testField2\"\n\r\t :   \"\\\"qwerty\t\t\t\t\"\n\t \r}\n\t \r";
+    size = strlen(buffer3);
+    obj = microjson::parseObject(buffer3, size);
+    it = obj.find("testField");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "5");
+
+    it = obj.find("testField2");
+    ASSERT_TRUE(it != obj.end());
+    EXPECT_EQ(it->second.type, microjson::JsonStringType);
+    EXPECT_STREQ(it->second.value.c_str(), "\\\"qwerty\t\t\t\t");
+}