teqp 0.22.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Concepts
json_tools.hpp
Go to the documentation of this file.
1#pragma once
2#include "nlohmann/json.hpp"
3#include <nlohmann/json-schema.hpp>
4
5#include <set>
6#include <filesystem>
7#include <fstream>
8#include "teqp/exceptions.hpp"
9
10#include <Eigen/Dense>
11
12using nlohmann::json;
13using nlohmann::json_schema::json_validator;
14
15namespace teqp{
16
18 inline nlohmann::json load_a_JSON_file(const std::string& path) {
19 if (!std::filesystem::is_regular_file(path)) {
20 throw std::invalid_argument("Path to be loaded does not exist: " + path);
21 }
22 auto stream = std::ifstream(path);
23 if (!stream) {
24 throw std::invalid_argument("File stream cannot be opened from: " + path);
25 }
26 try {
27 return nlohmann::json::parse(stream);
28 }
29 catch (...) {
30 throw std::invalid_argument("File at " + path + " is not valid JSON");
31 }
32 }
33
34 inline void JSON_to_file(const nlohmann::json& jsondata, const std::string& path){
35 std::ofstream file(path);
36 file << jsondata;
37 }
38
39 inline auto all_same_length(const nlohmann::json& j, const std::vector<std::string>& ks) {
40 std::set<decltype(j[0].size())> lengths;
41 for (auto k : ks) { lengths.insert(j.at(k).size()); }
42 return lengths.size() == 1;
43 }
44
45 inline auto build_square_matrix = [](const nlohmann::json& j){
46 if (j.is_null() || (j.is_array() && j.size() == 0)){
47 return Eigen::ArrayXXd(0, 0);
48 }
49 try{
50 const std::valarray<std::valarray<double>> m = j;
51 // First assume that the matrix is square, resize
52 Eigen::ArrayXXd mat(m.size(), m.size());
53 if (m.size() == 0){
54 return mat;
55 }
56 // Then copy elements over
57 for (auto i = 0U; i < m.size(); ++i){
58 auto row = m[i];
59 if (row.size() != static_cast<std::size_t>(mat.rows())){
60 throw std::invalid_argument("provided matrix is not square");
61 }
62 for (auto k = 0U; k < row.size(); ++k){
63 mat(i, k) = row[k];
64 }
65 }
66 return mat;
67 }
68 catch(const nlohmann::json::exception&){
69 throw teqp::InvalidArgument("Unable to convert this kmat to a NxN matrix of doubles:" + j.dump(2));
70 }
71 };
72
82 inline auto multilevel_JSON_load(const nlohmann::json &j, const std::optional<std::string>& default_path = std::nullopt){
83
84 auto is_valid_path = [](const std::string & s){
85 try{
86 return std::filesystem::is_regular_file(s) || true; // this will return true if the function CAN BE CALLED without exception, indicating it could be a path
87 }
88 catch(...){
89 return false;
90 }
91 };
92
93 // If not provided (NULL, empty array or empty string), load from the default path provided
94 if (j.is_null() || (j.is_array() && j.empty()) || (j.is_string() && j.get<std::string>().empty())){
95 if (default_path){
96 return load_a_JSON_file(default_path.value());
97 }
98 else{
99 throw teqp::InvalidArgument("default path was not provided, and cannot load this thing: " + j.dump(1));
100 }
101 }
102 else if (j.is_object()){
103 // Assume we are already providing the thing
104 return j;
105 }
106 else if (j.is_array() && j.size() > 0){
107 return j;
108 }
109 else if (j.is_string()){
110 // If a string, either data in JSON format, or a path-like thing
111 std::string s = j.get<std::string>();
112
113 // If path to existing file, use it
114 if (is_valid_path(s) && std::filesystem::is_regular_file(s)){
115 return load_a_JSON_file(s);
116 }
117 // Or assume it is a string in JSON format
118 else{
119 return nlohmann::json::parse(s);
120 }
121 }
122 else{
123 throw teqp::InvalidArgument("Unable to load the argument to multilevel_JSON_load");
124 }
125 }
126
131 public:
132 const nlohmann::json schema;
133
134 json_validator validator; // create validator
135
136 // Instantiate the validator object, will throw if the schema is invalid
137 JSONValidator(const nlohmann::json& schema) : schema(schema) {
138 validator.set_root_schema(schema); // insert root-schema
139 }
140
141 // Return the validation errors when trying to validate the JSON
142 std::vector<std::string> get_validation_errors(const nlohmann::json& j) const{
143
144 /* Custom error handler */
145 class custom_error_handler : public nlohmann::json_schema::basic_error_handler
146 {
147 public:
148 std::vector<std::string> errors;
149 void error(const nlohmann::json::json_pointer &ptr, const json &instance, const std::string &message) override
150 {
151 nlohmann::json_schema::basic_error_handler::error(ptr, instance, message);
152 std::stringstream ss;
153 ss << ptr << ":" << instance << "': " << message << "\n";
154 errors.push_back(ss.str());
155 }
156 } handler;
157 validator.validate(j, handler); // validate the document
158 return handler.errors;
159 }
160
161 // A quick checker for validation of the JSON
162 bool is_valid(const nlohmann::json&j ) const { return get_validation_errors(j).empty(); }
163 };
164
165}
JSONValidator(const nlohmann::json &schema)
json_validator validator
bool is_valid(const nlohmann::json &j) const
const nlohmann::json schema
std::vector< std::string > get_validation_errors(const nlohmann::json &j) const
auto build_square_matrix
nlohmann::json load_a_JSON_file(const std::string &path)
Load a JSON file from a specified file.
auto all_same_length(const nlohmann::json &j, const std::vector< std::string > &ks)
void JSON_to_file(const nlohmann::json &jsondata, const std::string &path)
auto multilevel_JSON_load(const nlohmann::json &j, const std::optional< std::string > &default_path=std::nullopt)