diff --git a/CMakeModules/testing_cpp.cmake b/CMakeModules/testing_cpp.cmake
index dd66bda81163eeb92e410b4beecb1cd2ce503cc6..5efbb83b05f1afd6480039b1d68d65fe60f6a383 100644
--- a/CMakeModules/testing_cpp.cmake
+++ b/CMakeModules/testing_cpp.cmake
@@ -15,7 +15,7 @@ if (BUILD_TESTS)
     if (CMAKE_COMPILER_IS_GNUCXX)
         include(CodeCoverage)
         APPEND_COVERAGE_COMPILER_FLAGS()
-    endif()
+    endif ()
 endif ()
 
 function(gtest target test_source_files linktarget)
@@ -23,14 +23,22 @@ function(gtest target test_source_files linktarget)
         include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
         link_directories(${gtest_SOURCE_DIR}/lib)
 
-        if (NOT ${linktarget} STREQUAL "")
-            get_target_property(target_type ${linktarget} TYPE)
-            if (target_type STREQUAL "OBJECT_LIBRARY")
-                add_executable(test-${target} ${test_source_files} $<TARGET_OBJECTS:${linktarget}>)
-            else()
-                add_executable(test-${target} ${test_source_files})
-                target_link_libraries(test-${target} ${linktarget})
+        FOREACH (lib ${linktarget})
+            if (NOT ${lib} STREQUAL "")
+                get_target_property(target_type ${lib} TYPE)
+                if (target_type STREQUAL "OBJECT_LIBRARY")
+                    list(APPEND OBJECT "$<TARGET_OBJECTS:${lib}>")
+                else ()
+                    list(APPEND libs "${lib}")
+
+                endif ()
             endif ()
+        ENDFOREACH ()
+
+        add_executable(test-${target} ${test_source_files} ${OBJECT})
+
+        if (NOT ${libs} STREQUAL "")
+            target_link_libraries(test-${target} ${libs})
         endif ()
 
         IF (WIN32 AND ${CMAKE_BUILD_TYPE} STREQUAL "Debug")
@@ -46,10 +54,10 @@ function(gtest target test_source_files linktarget)
         message(STATUS "Added test 'test-${target}'")
 
         if (CMAKE_COMPILER_IS_GNUCXX)
-            set(COVERAGE_EXCLUDES "*/unittests/*" "*/3d_party/*" )
+            set(COVERAGE_EXCLUDES "*/unittests/*" "*/3d_party/*")
             if (ARGN)
                 set(COVERAGE_EXCLUDES ${COVERAGE_EXCLUDES} ${ARGN})
-            endif()
+            endif ()
             SETUP_TARGET_FOR_COVERAGE(NAME coverage-${target} EXECUTABLE test-${target} ${target})
             add_test(NAME coveragetest-${target}
                     COMMAND ${CMAKE_MODULE_PATH}/check_test.sh
@@ -91,9 +99,9 @@ function(add_test_setup exename)
     if (BUILD_INTEGRATION_TESTS)
         IF (WIN32)
             add_test(NAME test-${exename}-setup COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/setup_windows.bat)
-        ELSE()
+        ELSE ()
             add_test(NAME test-${exename}-setup COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/setup_linux.sh)
-        ENDIF()
+        ENDIF ()
         set_tests_properties(test-${exename}-setup PROPERTIES FIXTURES_SETUP test-${exename}-fixture)
     endif ()
 endfunction()
@@ -102,9 +110,9 @@ function(add_test_cleanup exename)
     if (BUILD_INTEGRATION_TESTS)
         IF (WIN32)
             add_test(NAME test-${exename}-cleanup COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cleanup_windows.bat)
-        ELSE()
+        ELSE ()
             add_test(NAME test-${exename}-cleanup COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/cleanup_linux.sh)
-        ENDIF()
+        ENDIF ()
         set_tests_properties(test-${exename}-cleanup PROPERTIES FIXTURES_CLEANUP test-${exename}-fixture)
     endif ()
 endfunction()
@@ -142,22 +150,22 @@ function(add_script_test testname arguments)
         separate_arguments(args)
         IF (WIN32)
             add_test(NAME test-${testname} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/check_windows.bat
-                      ${args})
-        ELSE()
+                    ${args})
+        ELSE ()
             add_test(NAME test-${testname} COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/check_linux.sh
-                      ${args})
-        if (MEMORYCHECK_COMMAND)
-            if (ARGN)
-                set(commandargs ${ARGN})
+                    ${args})
+            if (MEMORYCHECK_COMMAND)
+                if (ARGN)
+                    set(commandargs ${ARGN})
+                endif ()
+                if (NOT "${ARGN}" STREQUAL "nomem")
+                    set(memargs ${MEMORYCHECK_COMMAND} ${MEMORYCHECK_COMMAND_OPTIONS} ${arguments})
+                    separate_arguments(memargs)
+                    add_test(NAME memtest-${testname} COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/check_linux.sh
+                            ${memargs})
+                endif ()
             endif ()
-            if (NOT "${ARGN}" STREQUAL "nomem")
-             set(memargs ${MEMORYCHECK_COMMAND} ${MEMORYCHECK_COMMAND_OPTIONS} ${arguments})
-             separate_arguments(memargs)
-             add_test(NAME memtest-${testname} COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/check_linux.sh
-                    ${memargs})
-                    endif()
-            endif()
-        ENDIF()
+        ENDIF ()
         set_tests_properties(test-${testname} PROPERTIES
                 LABELS "example;all"
                 )
diff --git a/common/cpp/CMakeLists.txt b/common/cpp/CMakeLists.txt
index 7865db18df7b3087b583afa1c1a7077664afcf0b..0b489c5d47d4bc9e0ef3ff57d59b27f003dc972e 100644
--- a/common/cpp/CMakeLists.txt
+++ b/common/cpp/CMakeLists.txt
@@ -1,5 +1,8 @@
 add_subdirectory(src/system_io)
 
+
+add_subdirectory(src/json_parser)
+
 add_subdirectory(src/data_structs)
 
 if(BUILD_MONGODB_CLIENTLIB)
diff --git a/common/cpp/include/json_parser/json_parser.h b/common/cpp/include/json_parser/json_parser.h
new file mode 100644
index 0000000000000000000000000000000000000000..46d2b01e896aea4a5b6d41d447c072eddff6646a
--- /dev/null
+++ b/common/cpp/include/json_parser/json_parser.h
@@ -0,0 +1,29 @@
+#ifndef HIDRA2_JSON_PARSER_H
+#define HIDRA2_JSON_PARSER_H
+
+#include <string>
+#include <memory>
+
+#include "common/error.h"
+
+namespace hidra2 {
+
+class RapidJson;
+
+class JsonStringParser {
+  public:
+    JsonStringParser(const std::string& json);
+    ~JsonStringParser();
+    Error GetUInt64(const std::string& name, uint64_t* val) const noexcept;
+    Error GetString(const std::string& name, std::string* val) const noexcept;
+  private:
+    std::unique_ptr<RapidJson>  rapid_json_;
+};
+
+}
+
+
+
+
+
+#endif //HIDRA2_JSON_PARSER_H
diff --git a/common/cpp/src/data_structs/CMakeLists.txt b/common/cpp/src/data_structs/CMakeLists.txt
index 617f5c89bfc19624534c9a28958aa8e922beeb54..203f68cf8f33b42fb32745a7ead515d4b3bc392a 100644
--- a/common/cpp/src/data_structs/CMakeLists.txt
+++ b/common/cpp/src/data_structs/CMakeLists.txt
@@ -16,7 +16,7 @@ target_include_directories(${TARGET_NAME} PUBLIC ${HIDRA2_CXX_COMMON_INCLUDE_DIR
 ################################
 set(TEST_SOURCE_FILES ../../unittests/data_structs/test_data_structs.cpp)
 
-set(TEST_LIBRARIES "${TARGET_NAME}")
+set(TEST_LIBRARIES "${TARGET_NAME};json_parser")
 include_directories(${HIDRA2_CXX_COMMON_INCLUDE_DIR})
 gtest(${TARGET_NAME} "${TEST_SOURCE_FILES}" "${TEST_LIBRARIES}")
 
diff --git a/common/cpp/src/data_structs/data_structs.cpp b/common/cpp/src/data_structs/data_structs.cpp
index 0467bea2933fffee24cc1beebe31235898e744a3..c6127e0ff1ffb895a2d644d5dd26dfe7d6dfeed7 100644
--- a/common/cpp/src/data_structs/data_structs.cpp
+++ b/common/cpp/src/data_structs/data_structs.cpp
@@ -1,8 +1,6 @@
 #include "common/data_structs.h"
 
-#include "rapidjson/document.h"
-
-using namespace rapidjson;
+#include "json_parser/json_parser.h"
 
 namespace hidra2 {
 
@@ -18,21 +16,10 @@ std::string FileInfo::Json() const {
     return s;
 }
 
-bool IntFromJson(const Document& d, const std::string name, uint64_t* val) {
-    auto iterator = d.FindMember(name.c_str());
-    if (iterator == d.MemberEnd()) {
-        return false;
-    }
-    if (iterator->value.IsInt64()) {
-        *val = iterator->value.GetInt64();
-        return true;
-    }
-    return false;
-}
 
-bool TimeFromJson(const Document& d, const std::string name, std::chrono::system_clock::time_point* val) {
+bool TimeFromJson(const JsonStringParser& parser, const std::string name, std::chrono::system_clock::time_point* val) {
     uint64_t nanoseconds_from_epoch;
-    if (!IntFromJson(d, name, &nanoseconds_from_epoch)) {
+    if (parser.GetUInt64(name, &nanoseconds_from_epoch)) {
         return false;
     }
 
@@ -43,31 +30,18 @@ bool TimeFromJson(const Document& d, const std::string name, std::chrono::system
     return true;
 }
 
-bool StringFromJson(const Document& d, const std::string name, std::string* val) {
-    auto iterator = d.FindMember(name.c_str());
-    if (iterator == d.MemberEnd()) {
-        return false;
-    }
-    if (iterator->value.IsString()) {
-        *val = iterator->value.GetString();
-        return true;
-    }
-    return false;
-}
+
 
 bool FileInfo::SetFromJson(const std::string& json_string) {
     auto old = *this;
 
-    Document d;
-    if ( d.Parse(json_string.c_str()).HasParseError()) {
-        return false;
-    }
+    JsonStringParser parser(json_string);
 
-    if (!IntFromJson(d, "_id", &id) ||
-            !IntFromJson(d, "size", &size) ||
-            !StringFromJson(d, "base_name", &base_name) ||
-            !StringFromJson(d, "relative_path", &relative_path) ||
-            !TimeFromJson(d, "lastchange", &modify_date)) {
+    if (parser.GetUInt64("_id", &id) ||
+            parser.GetUInt64("size", &size) ||
+            parser.GetString("base_name", &base_name) ||
+            parser.GetString("relative_path", &relative_path) ||
+            !TimeFromJson(parser, "lastchange", &modify_date)) {
         *this = old;
         return false;
     }
diff --git a/common/cpp/src/database/CMakeLists.txt b/common/cpp/src/database/CMakeLists.txt
index d68b39e61d8aeca945be50df2c3fffce0d32a8cf..44b6ab28bf26da9090b59a8bb653c1fb9ee6db95 100644
--- a/common/cpp/src/database/CMakeLists.txt
+++ b/common/cpp/src/database/CMakeLists.txt
@@ -12,7 +12,7 @@ message ("--   mongoc include path \"${MONGOC_STATIC_INCLUDE_DIRS}\"")
 message ("--   mongoc libraries \"${MONGOC_STATIC_LIBRARIES}\"")
 
 
-add_library(${TARGET_NAME} STATIC ${SOURCE_FILES} $<TARGET_OBJECTS:data_structs>)
+add_library(${TARGET_NAME} STATIC ${SOURCE_FILES} $<TARGET_OBJECTS:data_structs> $<TARGET_OBJECTS:json_parser>)
 target_include_directories(${TARGET_NAME} PUBLIC ${HIDRA2_CXX_COMMON_INCLUDE_DIR}
         PUBLIC "${MONGOC_STATIC_INCLUDE_DIRS}")
 target_link_libraries (${TARGET_NAME} PRIVATE "${MONGOC_STATIC_LIBRARIES}")
diff --git a/common/cpp/src/json_parser/CMakeLists.txt b/common/cpp/src/json_parser/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..286f641e55f2b332f464ca9292366e92e14f1bce
--- /dev/null
+++ b/common/cpp/src/json_parser/CMakeLists.txt
@@ -0,0 +1,23 @@
+set(TARGET_NAME json_parser)
+set(SOURCE_FILES
+        json_string_parser.cpp
+        rapid_json.cpp)
+
+################################
+# Library
+################################
+
+add_library(${TARGET_NAME} OBJECT ${SOURCE_FILES})
+target_include_directories(${TARGET_NAME} PUBLIC ${HIDRA2_CXX_COMMON_INCLUDE_DIR}
+        ${CMAKE_SOURCE_DIR}/3d_party/rapidjson/include)
+
+################################
+# Testing
+################################
+
+
+set(TEST_SOURCE_FILES ../../unittests/json_parser/test_json_string_parser.cpp)
+
+set(TEST_LIBRARIES "${TARGET_NAME}")
+include_directories(${HIDRA2_CXX_COMMON_INCLUDE_DIR})
+gtest(${TARGET_NAME} "${TEST_SOURCE_FILES}" "${TEST_LIBRARIES}")
diff --git a/common/cpp/src/json_parser/json_string_parser.cpp b/common/cpp/src/json_parser/json_string_parser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e3a35f712ca542bc0131d80e5bf5f9e74edd0526
--- /dev/null
+++ b/common/cpp/src/json_parser/json_string_parser.cpp
@@ -0,0 +1,26 @@
+
+#include "json_parser/json_parser.h"
+#include "rapid_json.h"
+
+namespace hidra2 {
+
+
+JsonStringParser::~JsonStringParser() {
+
+}
+
+JsonStringParser::JsonStringParser(const std::string& json) : rapid_json_{new RapidJson(json)} {
+}
+
+Error JsonStringParser::GetUInt64(const std::string& name, uint64_t* val) const noexcept {
+    return rapid_json_->GetUInt64(name, val);
+}
+
+Error JsonStringParser::GetString(const std::string& name, std::string* val) const noexcept {
+    return rapid_json_->GetString(name, val);
+}
+
+}
+
+
+
diff --git a/common/cpp/src/json_parser/rapid_json.cpp b/common/cpp/src/json_parser/rapid_json.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..42974c6eb3a414383889088abb71c1a3e385a352
--- /dev/null
+++ b/common/cpp/src/json_parser/rapid_json.cpp
@@ -0,0 +1,78 @@
+#include "rapid_json.h"
+
+using namespace rapidjson;
+
+namespace hidra2 {
+
+RapidJson::RapidJson(const std::string& json): json_{json} {
+
+}
+
+bool RapidJson::LazyInitialize()const noexcept {
+    if (initialized_)
+        return true;
+
+    if ( doc_.Parse(json_.c_str()).HasParseError()) {
+        return false;
+    }
+
+    return true;
+
+}
+
+hidra2::Error CheckValueType(const std::string& name, ValueType type, const Value& val) {
+    bool res = false;
+    switch (type) {
+    case ValueType::kString:
+        res = val.IsString();
+        break;
+    case ValueType::kUint64:
+        res = val.IsInt64();
+        break;
+    }
+    if (!res) {
+        return TextError("wrong type: " + name);
+    }
+
+    return nullptr;
+}
+
+
+hidra2::Error RapidJson::GetValue(const std::string& name, ValueType type, Value* val)const noexcept {
+    if (!LazyInitialize()) {
+        return TextError("cannot parse document");
+    }
+
+    auto iterator = doc_.FindMember(name.c_str());
+    if (iterator == doc_.MemberEnd()) {
+        return  TextError("cannot find: " + name);
+    }
+
+    *val =  iterator->value;
+    return CheckValueType(name, type, *val);
+}
+
+
+Error RapidJson::GetUInt64(const std::string& name, uint64_t* val) const noexcept {
+    Value json_val;
+    if (Error err = GetValue(name, ValueType::kUint64, &json_val)) {
+        return err;
+    }
+    *val = json_val.GetInt64();
+    return nullptr;
+}
+
+
+
+
+Error RapidJson::GetString(const std::string& name, std::string* val) const noexcept {
+    Value json_val;
+    if (Error err = GetValue(name, ValueType::kString, &json_val)) {
+        return err;
+    }
+    *val = json_val.GetString();
+    return nullptr;
+}
+
+
+}
\ No newline at end of file
diff --git a/common/cpp/src/json_parser/rapid_json.h b/common/cpp/src/json_parser/rapid_json.h
new file mode 100644
index 0000000000000000000000000000000000000000..379ccd631d68181eae864bdd1f36c024bce8204a
--- /dev/null
+++ b/common/cpp/src/json_parser/rapid_json.h
@@ -0,0 +1,30 @@
+#ifndef HIDRA2_RAPID_JSON_H
+#define HIDRA2_RAPID_JSON_H
+
+#include "rapidjson/document.h"
+#include "common/error.h"
+
+namespace hidra2 {
+
+enum class ValueType {
+    kUint64,
+    kString
+};
+
+
+class RapidJson {
+  public:
+    RapidJson(const std::string& json);
+    Error GetUInt64(const std::string& name, uint64_t* val) const noexcept;
+    Error GetString(const std::string& name, std::string* val) const noexcept;
+  private:
+    mutable rapidjson::Document doc_;
+    std::string json_;
+    mutable bool initialized_ = false;
+    bool LazyInitialize() const noexcept;
+    hidra2::Error GetValue(const std::string& name, ValueType type, rapidjson::Value* val)const noexcept;
+
+};
+
+}
+#endif //HIDRA2_RAPID_JSON_H
diff --git a/common/cpp/unittests/json_parser/test_json_string_parser.cpp b/common/cpp/unittests/json_parser/test_json_string_parser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..926043424f0211d6e737a55c09319f568fc5013c
--- /dev/null
+++ b/common/cpp/unittests/json_parser/test_json_string_parser.cpp
@@ -0,0 +1,76 @@
+
+#include <gmock/gmock.h>
+#include "gtest/gtest.h"
+#include <chrono>
+
+#include "json_parser/json_parser.h"
+
+using ::testing::AtLeast;
+using ::testing::Eq;
+using ::testing::Ne;
+using ::testing::Test;
+using ::testing::_;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::HasSubstr;
+
+using hidra2::JsonStringParser;
+
+namespace {
+
+TEST(ParseString, CorrectConvertToJson) {
+    ASSERT_THAT(1, Eq(1));
+    std::string json = R"({"_id":2,"foo":"foo","bar":1})";
+
+    JsonStringParser parser{json};
+
+    uint64_t id, bar;
+    std::string foo;
+    auto err1 = parser.GetUInt64("_id", &id);
+    auto err2 = parser.GetString("foo", &foo);
+    auto err3 = parser.GetUInt64("bar", &bar);
+
+    ASSERT_THAT(err1, Eq(nullptr));
+    ASSERT_THAT(err2, Eq(nullptr));
+    ASSERT_THAT(err3, Eq(nullptr));
+
+    ASSERT_THAT(id, Eq(2));
+    ASSERT_THAT(foo, Eq("foo"));
+    ASSERT_THAT(bar, Eq(1));
+
+}
+
+
+TEST(ParseString, ErrorOnWrongType) {
+    ASSERT_THAT(1, Eq(1));
+    std::string json = R"({"_id":"2"})";
+
+    JsonStringParser parser{json};
+
+    uint64_t id;
+    auto err = parser.GetUInt64("_id", &id);
+
+    ASSERT_THAT(err, Ne(nullptr));
+    ASSERT_THAT(err->Explain(), ::testing::HasSubstr("type"));
+
+}
+
+TEST(ParseString, ErrorOnWrongDocument) {
+    ASSERT_THAT(1, Eq(1));
+    std::string json = R"({"_id":2)";
+
+    JsonStringParser parser{json};
+
+    uint64_t id;
+    auto err = parser.GetUInt64("_id", &id);
+
+    ASSERT_THAT(err, Ne(nullptr));
+    ASSERT_THAT(err->Explain(), ::testing::HasSubstr("parse"));
+
+}
+
+
+
+}
\ No newline at end of file
diff --git a/worker/api/cpp/CMakeLists.txt b/worker/api/cpp/CMakeLists.txt
index 49bcfa605ec2c49306a339193658f96447ab47bf..914d87840dff2058631c83a683401fb10bf9f959 100644
--- a/worker/api/cpp/CMakeLists.txt
+++ b/worker/api/cpp/CMakeLists.txt
@@ -13,7 +13,7 @@ set(SOURCE_FILES
 # Library
 ################################
 add_library(${TARGET_NAME} STATIC ${SOURCE_FILES} $<TARGET_OBJECTS:system_io>
-            $<TARGET_OBJECTS:data_structs>)
+            $<TARGET_OBJECTS:json_parser> $<TARGET_OBJECTS:data_structs> )
 
 set (CMAKE_PREFIX_PATH "${LIBCURL_DIR}")
 find_package (CURL REQUIRED)