Json daccor

json daccor is a json-schema validation library that accepts json-c
objects as input, and seems to be designed for openwrt, a system
with very similar requirements to BMCs.

Using this library has several motivations.  First, it keeps
libcper as a C application so integrations aren't required to provide
a c++ toolchain to be able to run unit tests.
Next, it means that we avoid an "expensive" conversion in unit tests
from json-c -> nlohmann just so we can use a third library, valijson
In terms of dependency count, it drops one dependency.
(nlohmann+valijson) to (json daccor)
Finally, it means that in the future versions of the library, we
can allow json-schema verification as an option in the library
itself, which would allow us to give a CLI option for verifying
schema on any arbitrary output (helping in debugging).

Testing to see if it will work and what improvements it makes

Change-Id: I1c00bf2ef9b898b2e5decd90b749c784fb4de109
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/.clang-tidy b/.clang-tidy
index c9fa39f..56265b7 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -147,7 +147,6 @@
 misc-uniqueptr-reset-release,
 misc-unused-using-decls,
 modernize-avoid-bind,
-modernize-deprecated-headers,
 modernize-deprecated-ios-base-aliases,
 modernize-loop-convert,
 modernize-make-shared,
@@ -214,4 +213,3 @@
   - { key: readability-identifier-naming.ParameterCase, value: lower_case }
   - { key: readability-identifier-naming.NamespaceCase, value: lower_case }
   - { key: readability-identifier-naming.StructCase,    value: lower_case  }
-  - { key: cppcoreguidelines-macro-usage.AllowedRegexp, value: DEBUG*|NLOHMANN_JSON_SERIALIZE_ENUM }
diff --git a/meson.build b/meson.build
index a946d11..e669bb8 100644
--- a/meson.build
+++ b/meson.build
@@ -73,15 +73,7 @@
 
 cc = meson.get_compiler('c')
 
-json_c_dep = dependency('json-c', required: false)
-if not json_c_dep.found()
-    json_c = subproject(
-        'json-c',
-        required: true,
-        default_options: ['warning_level=0'],
-    )
-    json_c_dep = json_c.get_variable('json_c_dep')
-endif
+json_c_dep = dependency('json-c')
 
 libcper_include = ['include']
 libcper_include_dir = include_directories(libcper_include, is_system: true)
diff --git a/subprojects/json-c.wrap b/subprojects/json-c.wrap
index aca2cef..e68e6ef 100644
--- a/subprojects/json-c.wrap
+++ b/subprojects/json-c.wrap
@@ -1,14 +1,7 @@
-[wrap-file]
-directory = json-c-0.18
-source_url = https://s3.amazonaws.com/json-c_releases/releases/json-c-0.18.tar.gz
-source_filename = json-c-0.18.tar.gz
-source_hash = 876ab046479166b869afc6896d288183bbc0e5843f141200c677b3e8dfb11724
-patch_filename = json-c_0.18-1_patch.zip
-patch_url = https://wrapdb.mesonbuild.com/v2/json-c_0.18-1/get_patch
-patch_hash = c9d2c0449a9686755445cd18d7cff597f21e418e11a786441de7b8947ff43798
-source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/json-c_0.18-1/json-c-0.18.tar.gz
-wrapdb_version = 0.18-1
+[wrap-git]
+revision = json-c-0.18-20240915
+url = https://github.com/json-c/json-c.git
+patch_directory = json-c
 
 [provide]
 json-c = json_c_dep
-
diff --git a/subprojects/jsoncdac.wrap b/subprojects/jsoncdac.wrap
new file mode 100644
index 0000000..e2636a1
--- /dev/null
+++ b/subprojects/jsoncdac.wrap
@@ -0,0 +1,8 @@
+[wrap-git]
+revision = HEAD
+url = https://github.com/domoslabs/jsonc-daccord.git
+patch_directory = jsoncdac
+
+[provide]
+jsoncdac = jsoncdac_dep
+
diff --git a/subprojects/packagefiles/jsoncdac/meson.build b/subprojects/packagefiles/jsoncdac/meson.build
new file mode 100644
index 0000000..6cc3bfc
--- /dev/null
+++ b/subprojects/packagefiles/jsoncdac/meson.build
@@ -0,0 +1,59 @@
+project(
+    'libjsoncdac',
+    ['c'],
+    meson_version: '>=1.1.1',
+    default_options: ['default_library=static', 'c_std=gnu23'],
+)
+conf_data = configuration_data(
+    {
+        'PROJECT_NAME': 'jsoncdac',
+        'PROJECT_VERSION': '0.1',
+        'PROJECT_VERSION_MAJOR': '0',
+        'PROJECT_VERSION_MINOR': '1',
+        'PROJECT_VERSION_PATCH': '0',
+    },
+)
+configure_file(
+    input: 'config.h.in',
+    output: 'version_config.h',
+    configuration: conf_data,
+)
+deps = []
+
+add_project_arguments('-Wno-unused-parameter', language: 'c')
+add_project_arguments('-Wformat=0', language: 'c')
+
+jsonc = dependency('json-c')
+deps += jsonc
+
+jsoncdac_sources = files(
+    'libjsoncdac/additionalproperties.c',
+    'libjsoncdac/contains.c',
+    'libjsoncdac/dependent.c',
+    #'libjsoncdac/download.c',
+    #'libjsoncdac/jdac-cli.c',
+    'libjsoncdac/output.c',
+    'libjsoncdac/pattern.c',
+    'libjsoncdac/patternproperties.c',
+    'libjsoncdac/propertynames.c',
+    'libjsoncdac/ref.c',
+    'libjsoncdac/regex_match.c',
+    'libjsoncdac/store.c',
+    'libjsoncdac/subschemalogic.c',
+    'libjsoncdac/validate.c',
+)
+
+jsoncdac_deps = [dependency('json-c')]
+cc = meson.get_compiler('c')
+m_dep = cc.find_library('m', required: false)
+if m_dep.found()
+    deps += m_dep
+endif
+
+jsoncdac = library('jsoncdac', jsoncdac_sources, dependencies: deps)
+
+jsoncdac_dep = declare_dependency(
+    link_with: jsoncdac,
+    dependencies: deps,
+    include_directories: include_directories('include'),
+)
diff --git a/tests/fuzz_cper_buf_to_ir.cpp b/tests/fuzz_cper_buf_to_ir.cpp
index 4baef7b..b947782 100644
--- a/tests/fuzz_cper_buf_to_ir.cpp
+++ b/tests/fuzz_cper_buf_to_ir.cpp
@@ -1,3 +1,4 @@
+#include <cassert>
 #include "libcper/cper-parse.h"
 #include "test-utils.hpp"
 
@@ -7,19 +8,11 @@
 	if (ir == NULL) {
 		return 0;
 	}
-	char *str = strdup(json_object_to_json_string(ir));
 
-	nlohmann::json jsonData = nlohmann::json::parse(str, nullptr, false);
-	free(str);
-	assert(jsonData.is_discarded() == false);
-	std::string error_message;
-	static std::unique_ptr<valijson::Schema> schema =
-		load_schema(AddRequiredProps::NO, 0);
-
-	int valid = schema_validate_from_file(*schema, jsonData, error_message);
+	int valid = schema_validate_from_file(ir, 0 /* single_section */,
+					      /*all_valid_bits*/ 0);
 	if (!valid) {
-		std::cout << "JSON: " << jsonData.dump(4) << std::endl;
-		std::cout << "Error: " << error_message << std::endl;
+		printf("JSON: %s\n", json_object_to_json_string(ir));
 	}
 	assert(valid);
 	json_object_put(ir);
diff --git a/tests/ir-tests.cpp b/tests/ir-tests.cpp
index a43aced..b30e2ec 100644
--- a/tests/ir-tests.cpp
+++ b/tests/ir-tests.cpp
@@ -4,20 +4,19 @@
  * Author: Lawrence.Tang@arm.com
  **/
 
-#include <cctype>
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
 #include "test-utils.hpp"
-#include <json.h>
+#include <cctype>
 #include <charconv>
-#include <nlohmann/json.hpp>
 #include <filesystem>
-#include <fstream>
-#include <libcper/cper-parse.h>
-#include <libcper/json-schema.h>
-#include <libcper/generator/cper-generate.h>
-#include <libcper/sections/cper-section.h>
-#include <libcper/generator/sections/gen-section.h>
 #include <format>
+#include <fstream>
+#include <json.h>
+#include <libcper/cper-parse.h>
+#include <libcper/generator/cper-generate.h>
+#include <libcper/generator/sections/gen-section.h>
+#include <libcper/json-schema.h>
+#include <libcper/sections/cper-section.h>
 
 namespace fs = std::filesystem;
 
@@ -76,20 +75,13 @@
 		FAIL();
 		return;
 	}
-	char *str = strdup(json_object_to_json_string(ir));
-	nlohmann::json jsonData = nlohmann::json::parse(str, nullptr, false);
-	if (jsonData.is_discarded()) {
-		std::cerr << "cper_create_examples: JSON parse error:"
-			  << std::endl;
-	}
-	free(str);
-	fclose(record);
-	free(buf);
 
 	//Write json output to disk
-	std::ofstream jsonOutFile(json_out);
-	jsonOutFile << std::setw(4) << jsonData << std::endl;
-	jsonOutFile.close();
+	json_object_to_file_ext(json_out.c_str(), ir, JSON_C_TO_STRING_PRETTY);
+	json_object_put(ir);
+
+	fclose(record);
+	free(buf);
 }
 
 std::vector<unsigned char> string_to_binary(const std::string &source)
@@ -141,28 +133,22 @@
 		FAIL();
 		return;
 	}
-	const char *str = json_object_to_json_string(ir);
-	nlohmann::json jsonData = nlohmann::json::parse(str, nullptr, false);
-	if (jsonData.is_discarded()) {
-		std::cerr << "cper_example_section_ir_test: JSON parse error:"
-			  << std::endl;
-		FAIL() << "cper_example_section_ir_test: JSON parse error:";
-		json_object_put(ir);
 
+	json_object *expected = json_object_from_file(json.c_str());
+	ASSERT_NE(expected, nullptr);
+	if (expected == nullptr) {
+		const char *str = json_object_to_json_string(ir);
+
+		const char *expected_str = json_object_to_json_string(expected);
+
+		EXPECT_EQ(str, expected_str);
 		return;
 	}
 
-	//Open json example file
-	nlohmann::json jGolden = loadJson(json.string().c_str());
-	if (jGolden.is_discarded()) {
-		std::cerr << "Could not open JSON example file: " << json
-			  << std::endl;
-		FAIL() << "Could not open JSON example file";
-	}
+	EXPECT_TRUE(json_object_equal(ir, expected));
 
 	json_object_put(ir);
-
-	EXPECT_EQ(jGolden, jsonData);
+	json_object_put(expected);
 }
 
 //Tests a single randomly generated CPER section of the given type to ensure CPER-JSON IR validity.
@@ -183,25 +169,16 @@
 		ir = cper_to_ir(record);
 	}
 
-	char *str = strdup(json_object_to_json_string(ir));
-	nlohmann::json jsonData = nlohmann::json::parse(str, nullptr, false);
-	if (jsonData.is_discarded()) {
-		std::cerr << "Could not parse json output" << std::endl;
-	}
-	free(str);
 	fclose(record);
 	free(buf);
 
 	//Validate against schema.
-	std::string error_message;
-	std::unique_ptr<valijson::Schema> schema =
-		load_schema(AddRequiredProps::YES, single_section);
-	int valid = schema_validate_from_file(*schema, jsonData, error_message);
+	int valid = schema_validate_from_file(ir, single_section,
+					      /*all_valid_bits*/ 1);
 	json_object_put(ir);
-	ASSERT_TRUE(valid)
+	EXPECT_TRUE(valid)
 		<< "IR validation test failed (single section mode = "
-		<< single_section << ") with message: " << error_message
-		<< jsonData.dump(4) << "\n";
+		<< single_section << ")\n";
 }
 
 std::string to_hex(char *input, size_t size)
diff --git a/tests/meson.build b/tests/meson.build
index ca21b42..2f1e2b2 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -21,22 +21,7 @@
     endif
 endif
 
-nlohmann_json_dep = dependency(
-    'nlohmann_json',
-    required: false,
-    version: '>=3.11.2',
-    include_type: 'system',
-)
-if not nlohmann_json_dep.found()
-    nlohmann_proj = subproject('nlohmann_json', required: true)
-    nlohmann_json_dep = nlohmann_proj.get_variable('nlohmann_json_dep')
-endif
-
-valijson_dep = dependency('valijson', required: false)
-if not valijson_dep.found()
-    valijson_proj = cmake.subproject('valijson')
-    valijson_dep = valijson_proj.get_variable('valijson_dep')
-endif
+jsonc_daccord = dependency('jsoncdac')
 
 test_sources = ['test-utils.cpp', 'base64_test.cpp']
 
@@ -50,13 +35,12 @@
     include_directories: include_directories(test_include_dirs),
     cpp_args: '-fpermissive',
     dependencies: [
+        json_c_dep,
+        jsonc_daccord,
         libcper_parse_dep,
         libcper_generate_dep,
-        json_c_dep,
         gtest,
         gmock,
-        nlohmann_json_dep,
-        valijson_dep,
     ],
 )
 test('test-cper-tests', cper_tests)
@@ -81,13 +65,7 @@
             cpp_args: fuzz_args,
             c_args: fuzz_args,
             link_args: fuzz_args,
-            dependencies: [
-                json_c_dep,
-                gtest,
-                gmock,
-                nlohmann_json_dep,
-                valijson_dep,
-            ],
+            dependencies: [json_c_dep, jsonc_daccord, gtest, gmock],
         )
         test(
             fuzzer_test,
diff --git a/tests/test-utils.cpp b/tests/test-utils.cpp
index 11d8a02..e4d79d3 100644
--- a/tests/test-utils.cpp
+++ b/tests/test-utils.cpp
@@ -7,47 +7,54 @@
 #include <cstdio>
 #include <cstdlib>
 #include <fstream>
+#include <map>
 #include <filesystem>
+#include <vector>
+#include <algorithm>
 #include "test-utils.hpp"
 
 #include <libcper/BaseTypes.h>
 #include <libcper/generator/cper-generate.h>
 
+extern "C" {
+#include <jsoncdaccord.h>
+#include <json.h>
+#include <libcper/log.h>
+}
+
 namespace fs = std::filesystem;
 
 // Objects that have mutually exclusive fields (and thereforce can't have both
 // required at the same time) can be added to this list.
 // Truly optional properties that shouldn't be added to "required" field for
 // validating the entire schema with validationbits=1
-const static std::map<std::string, std::vector<std::string> >
-	optional_properties_map = {
-		{ "./sections/cper-cxl-protocol.json",
-		  { "capabilityStructure", "deviceSerial" } },
-		{ "./sections/cper-cxl-component.json",
-		  { "cxlComponentEventLog" } },
-		{ "./sections/cper-ia32x64-processor.json",
-		  { "addressSpace", "errorType", "participationType",
-		    "timedOut", "level", "operation", "preciseIP",
-		    "restartableIP", "overflow", "uncorrected",
-		    "transactionType" } },
-		// Several PCIe properties are dependent on the device capability
-		// version and whether the device is running in Flit-Mode.
-		// All of these properties are optional.
-		{ "./sections/cper-pcie.json",
-		  { "device_capabilities2", "device_status2", "device_control2",
-		    "link_capabilities2", "link_status2", "link_control2",
-		    "slot_capabilities2", "slot_status2", "slot_control2" } },
-	};
+// In most cases making sure examples set all valid bits is preferable to adding to this list
+const static std::vector<std::string> optional_props = {
+	{ // Some sections don't parse header correctly?
+	  "header",
 
-nlohmann::json loadJson(const char *filePath)
-{
-	std::ifstream file(filePath);
-	if (!file.is_open()) {
-		std::cerr << "Failed to open file: " << filePath << std::endl;
-	}
-	nlohmann::json out = nlohmann::json::parse(file, nullptr, false);
-	return out;
-}
+	  // Each section is optional
+	  "GenericProcessor", "Ia32x64Processor", "ArmProcessor", "Memory",
+	  "Memory2", "Pcie", "PciBus", "PciComponent", "Firmware",
+	  "GenericDmar", "VtdDmar", "IommuDmar", "CcixPer", "CxlProtocol",
+	  "CxlComponent", "Nvidia", "Ampere", "Unknown",
+
+	  // CXL?  might have a bug?
+	  "partitionID",
+
+	  // CXL protocol
+	  "capabilityStructure", "deviceSerial",
+
+	  // CXL component
+	  "cxlComponentEventLog", "addressSpace", "errorType",
+	  "participationType", "timedOut", "level", "operation", "preciseIP",
+	  "restartableIP", "overflow", "uncorrected", "transactionType",
+
+	  // PCIe AER
+	  "addressSpace", "errorType", "participationType", "timedOut", "level",
+	  "operation", "preciseIP", "restartableIP", "overflow", "uncorrected",
+	  "transactionType" }
+};
 
 //Returns a ready-for-use memory stream containing a CPER record with the given sections inside.
 FILE *generate_record_memstream(const char **types, UINT16 num_types,
@@ -72,161 +79,151 @@
 	return fmemopen(*buf, *buf_size, "r");
 }
 
-void iterate_make_required_props(nlohmann::json &jsonSchema,
-				 std::vector<std::string> &optional_props)
+int iterate_make_required_props(json_object *jsonSchema, bool all_valid_bits)
 {
-	//id
-	const auto it_id = jsonSchema.find("$id");
-	if (it_id != jsonSchema.end()) {
-		auto id_strptr = it_id->get_ptr<const std::string *>();
-		std::string id_str = *id_strptr;
-		if (id_str.find("header") != std::string::npos ||
-		    id_str.find("section-descriptor") != std::string::npos) {
-			return;
+	//properties
+	json_object *properties =
+		json_object_object_get(jsonSchema, "properties");
+
+	if (properties != nullptr) {
+		json_object *requrired_arr = json_object_new_array();
+
+		json_object_object_foreach(properties, property_name,
+					   property_value)
+		{
+			bool add_to_required = true;
+			const auto it_find_opt_prop = std::ranges::find(
+				optional_props, property_name);
+			if (it_find_opt_prop != optional_props.end()) {
+				add_to_required = false;
+			}
+
+			if (add_to_required) {
+				//Add to list if property is not optional
+				json_object_array_add(
+					requrired_arr,
+					json_object_new_string(property_name));
+			}
+		}
+
+		json_object_object_foreach(properties, property_name2,
+					   property_value2)
+		{
+			(void)property_name2;
+			if (iterate_make_required_props(property_value2,
+							all_valid_bits) < 0) {
+				return -1;
+			}
+		}
+
+		if (all_valid_bits) {
+			json_object_object_add(jsonSchema, "required",
+					       requrired_arr);
+		}
+		//json_object_put(requrired_arr);
+	}
+
+	// ref
+	json_object *ref = json_object_object_get(jsonSchema, "$ref");
+	if (ref != nullptr) {
+		const char *ref_str = json_object_get_string(ref);
+		if (ref_str != nullptr) {
+			std::string ref_path = LIBCPER_JSON_SPEC;
+			// remove the leading .
+			ref_path += std::string(ref_str).substr(1);
+			json_object *ref_obj =
+				json_object_from_file(ref_path.c_str());
+			if (ref_obj == nullptr) {
+				printf("Failed to parse file: %s\n",
+				       ref_path.c_str());
+				return -1;
+			}
+
+			if (iterate_make_required_props(ref_obj,
+							all_valid_bits) < 0) {
+				json_object_put(ref_obj);
+				return -1;
+			}
+
+			json_object_object_foreach(ref_obj, key, val)
+			{
+				json_object_object_add(jsonSchema, key,
+						       json_object_get(val));
+			}
+			json_object_object_del(jsonSchema, "$ref");
+
+			json_object_put(ref_obj);
 		}
 	}
+
 	//oneOf
-	const auto it_oneof = jsonSchema.find("oneOf");
-	if (it_oneof != jsonSchema.end()) {
-		//Iterate over oneOf properties
-		for (auto &oneOfProp : *it_oneof) {
-			iterate_make_required_props(oneOfProp, optional_props);
+	const json_object *oneOf = json_object_object_get(jsonSchema, "oneOf");
+	if (oneOf != nullptr) {
+		size_t num_elements = json_object_array_length(oneOf);
+
+		for (size_t i = 0; i < num_elements; i++) {
+			json_object *obj = json_object_array_get_idx(oneOf, i);
+			if (iterate_make_required_props(obj, all_valid_bits) <
+			    0) {
+				return -1;
+			}
 		}
 	}
 
 	//items
-	const auto it_items = jsonSchema.find("items");
-	if (it_items != jsonSchema.end()) {
-		iterate_make_required_props(*it_items, optional_props);
-	}
-	//required
-	const auto it_req = jsonSchema.find("required");
-	if (it_req == jsonSchema.end()) {
-		return;
-	}
-
-	//properties
-	const auto it_prop = jsonSchema.find("properties");
-	if (it_prop == jsonSchema.end()) {
-		return;
-	}
-	nlohmann::json &propertyFields = *it_prop;
-	nlohmann::json::array_t property_list;
-	if (propertyFields.is_object()) {
-		for (auto &[key, value] : propertyFields.items()) {
-			const auto it_find_opt_prop =
-				std::find(optional_props.begin(),
-					  optional_props.end(), key);
-			if (it_find_opt_prop == optional_props.end()) {
-				//Add to list if property is not optional
-				property_list.push_back(key);
+	const json_object *items = json_object_object_get(jsonSchema, "items");
+	if (items != nullptr) {
+		json_object_object_foreach(items, key, val)
+		{
+			(void)key;
+			if (iterate_make_required_props(val, all_valid_bits) <
+			    0) {
+				return -1;
 			}
-
-			iterate_make_required_props(value, optional_props);
 		}
 	}
 
-	*it_req = property_list;
+	return 1;
 }
 
-// Document loader callback function
-const nlohmann::json *documentLoader(const std::string &uri,
-				     AddRequiredProps add_required_props)
+int schema_validate_from_file(json_object *to_test, int single_section,
+			      int all_valid_bits)
 {
-	// Load the schema from a file
-	std::unique_ptr<nlohmann::json> ref_schema =
-		std::make_unique<nlohmann::json>();
-	*ref_schema = loadJson(uri.c_str());
-	if (ref_schema->is_discarded()) {
-		std::cerr << "Could not open schema file: " << uri << std::endl;
-	}
-	if (add_required_props == AddRequiredProps::YES) {
-		std::vector<std::string> opt = {};
-		const auto it_optional_file = optional_properties_map.find(uri);
-		if (it_optional_file != optional_properties_map.end()) {
-			opt = it_optional_file->second;
-		}
-		iterate_make_required_props(*ref_schema, opt);
-	}
-
-	return ref_schema.release();
-}
-
-// Document release callback function
-void documentRelease(const nlohmann::json *adapter)
-{
-	delete adapter; // Free the adapter memory
-}
-
-std::unique_ptr<valijson::Schema>
-load_schema(AddRequiredProps add_required_props, int single_section)
-{
-	// Load the schema
-	fs::path pathObj(LIBCPER_JSON_SPEC);
-
+	const char *schema_file;
 	if (single_section) {
-		pathObj /= "cper-json-section-log.json";
+		schema_file = "cper-json-section-log.json";
 	} else {
-		pathObj /= "cper-json-full-log.json";
-	}
-	nlohmann::json schema_root = loadJson(pathObj.c_str());
-	fs::path base_path(LIBCPER_JSON_SPEC);
-	try {
-		fs::current_path(base_path);
-		// std::cout << "Changed directory to: " << fs::current_path()
-		// 	  << std::endl;
-	} catch (const fs::filesystem_error &e) {
-		std::cerr << "Filesystem error: " << e.what() << std::endl;
+		schema_file = "cper-json-full-log.json";
 	}
 
-	// Parse the json schema into an internal schema format
-	std::unique_ptr<valijson::Schema> schema =
-		std::make_unique<valijson::Schema>();
-	valijson::SchemaParser parser;
-	valijson::adapters::NlohmannJsonAdapter schemaDocumentAdapter(
-		schema_root);
+	std::string schema_path = LIBCPER_JSON_SPEC;
+	schema_path += "/";
+	schema_path += schema_file;
 
-	// Set up callbacks for resolving external references
-	try {
-		parser.populateSchema(
-			schemaDocumentAdapter, *schema,
-			[add_required_props](const std::string &uri) {
-				return documentLoader(uri, add_required_props);
-			},
-			documentRelease);
-	} catch (std::exception &e) {
-		std::cerr << "Failed to parse schema: " << e.what()
-			  << std::endl;
-	}
-	return schema;
-}
-
-int schema_validate_from_file(const valijson::Schema &schema,
-			      nlohmann::json &jsonData,
-			      std::string &error_message)
-{
-	// Perform validation
-	valijson::Validator validator(valijson::Validator::kStrongTypes);
-	valijson::ValidationResults results;
-	valijson::adapters::NlohmannJsonAdapter targetDocumentAdapter(jsonData);
-	if (!validator.validate(schema, targetDocumentAdapter, &results)) {
-		std::cerr << "Validation failed." << std::endl;
-		valijson::ValidationResults::Error error;
-		unsigned int errorNum = 1;
-		while (results.popError(error)) {
-			std::string context;
-			for (const std::string &str : error.context) {
-				context += str;
-			}
-
-			std::cout << "Error #" << errorNum << '\n'
-				  << "  context: " << context << '\n'
-				  << "  desc:    " << error.description << '\n';
-			++errorNum;
-		}
+	json_object *schema = json_object_from_file(schema_path.c_str());
+	if (schema == nullptr) {
+		cper_print_log("Could not parse schema file: %s", schema_file);
 		return 0;
 	}
 
-	error_message = "Schema validation successful";
-	return 1;
+	if (iterate_make_required_props(schema, all_valid_bits) < 0) {
+		printf("Failed to make required props\n");
+		json_object_put(schema);
+		return -1;
+	}
+
+	int err = jdac_validate(to_test, schema);
+	if (err == JDAC_ERR_VALID) {
+		printf("validation ok\n");
+		json_object_put(schema);
+		return 1;
+	}
+	printf("validate failed %d: %s\n", err, jdac_errorstr(err));
+
+	printf("schema: \n%s\n",
+	       json_object_to_json_string_ext(schema, JSON_C_TO_STRING_PRETTY));
+	printf("to_test: \n%s\n", json_object_to_json_string_ext(
+					  to_test, JSON_C_TO_STRING_PRETTY));
+	json_object_put(schema);
+	return 0;
 }
diff --git a/tests/test-utils.hpp b/tests/test-utils.hpp
index ec5f064..3b1d061 100644
--- a/tests/test-utils.hpp
+++ b/tests/test-utils.hpp
@@ -1,17 +1,10 @@
 #ifndef CPER_IR_TEST_UTILS_H
 #define CPER_IR_TEST_UTILS_H
 
-#include <valijson/adapters/nlohmann_json_adapter.hpp>
-#include <valijson/schema.hpp>
-#include <valijson/schema_parser.hpp>
-#include <valijson/validator.hpp>
-#include <nlohmann/json.hpp>
-
-extern "C" {
 #include <stdio.h>
 #include <libcper/BaseTypes.h>
 #include <libcper/generator/sections/gen-section.h>
-}
+#include <json.h>
 
 // Controls whether required properties are added to the majority of property
 // definitions.  This is useful for unit tests that are validating JSON where
@@ -23,13 +16,7 @@
 				int single_section,
 				GEN_VALID_BITS_TEST_TYPE validBitsType);
 
-std::unique_ptr<valijson::Schema>
-load_schema(AddRequiredProps add_required_props, int single_section);
-
-int schema_validate_from_file(const valijson::Schema &schema,
-			      nlohmann::json &jsonData,
-			      std::string &error_message);
-
-nlohmann::json loadJson(const char *filePath);
+int schema_validate_from_file(json_object *to_test, int single_section,
+			      int all_valid_bits);
 
 #endif