CoolProp 8.0.0
An open-source fluid property and humid air property database
json.h
Go to the documentation of this file.
1#ifndef COOLPROP_DETAIL_JSON_H
2#define COOLPROP_DETAIL_JSON_H
3
4// Internal, NON-INSTALLED wrapper around nlohmann/json.
5//
6// Symbol-leak strategy: nlohmann and valijson symbols are hidden at LINK time
7// per shared product via cmake/CoolPropJSONVisibility.cmake, which applies an
8// ELF --version-script (Linux/ELF) or a Mach-O -unexported_symbols_list
9// (macOS) hide-list to every shared product. This replaces the former
10// compile-time GCC visibility pragma (which poisoned __assert_fail in
11// non-NDEBUG loader builds). Combined with nlohmann's own versioned inline
12// namespace (nlohmann::json_abi_v3_x_x), this prevents cross-library
13// ODR/symbol clashes. Do NOT rename nlohmann's namespace here: Valijson's
14// nlohmann adapter references nlohmann::json literally and a rename would
15// break it.
16
17#include "CoolProp/Exceptions.h"
18#include "CoolProp/detail/tools.h" // for CoolProp::format / CoolPropDbl
19
20#include <cstddef>
21#include <cstdint>
22#include <exception>
23#include <limits>
24#include <stdexcept>
25#include <string>
26#include <string_view>
27#include <vector>
28
29#include <nlohmann/json.hpp>
30#include <valijson/adapters/nlohmann_json_adapter.hpp>
31#include <valijson/schema.hpp>
32#include <valijson/schema_parser.hpp>
33#include <valijson/validator.hpp>
34
35namespace cpjson {
36
39inline nlohmann::json parse(std::string_view text) {
40 try {
41 return nlohmann::json::parse(text);
42 } catch (const std::exception& e) {
43 throw CoolProp::ValueError(std::string("Unable to parse JSON: ") + e.what());
44 }
45}
46
49inline nlohmann::json from_cbor(const std::uint8_t* data, std::size_t size) {
50 try {
51 return nlohmann::json::from_cbor(data, data + size);
52 } catch (const std::exception& e) {
53 throw CoolProp::ValueError(std::string("Unable to decode CBOR: ") + e.what());
54 }
55}
56
58inline std::string json2string(const nlohmann::json& v) {
59 return v.dump(4);
60}
61
62inline int get_integer(const nlohmann::json& v, const std::string& m) {
63 auto it = v.find(m);
64 if (it == v.end()) throw CoolProp::ValueError(format("Does not have member [%s]", m.c_str()));
65 if (!it->is_number_integer()) throw CoolProp::ValueError(format("Member [%s] is not an integer", m.c_str()));
66 // is_number_integer() is true for unsigned values too; a uint64 above
67 // INT64_MAX would wrap to a negative int64 and slip past the range check,
68 // so handle the unsigned case explicitly before the signed one.
69 if (it->is_number_unsigned()) {
70 const std::uint64_t uval = it->get<std::uint64_t>();
71 if (uval > static_cast<std::uint64_t>(std::numeric_limits<int>::max()))
72 throw CoolProp::ValueError(format("Member [%s] is out of int range", m.c_str()));
73 return static_cast<int>(uval);
74 }
75 const std::int64_t val = it->get<std::int64_t>();
76 if (val < std::numeric_limits<int>::min() || val > std::numeric_limits<int>::max())
77 throw CoolProp::ValueError(format("Member [%s] is out of int range", m.c_str()));
78 return static_cast<int>(val);
79}
80
81inline double get_double(const nlohmann::json& v, const std::string& m) {
82 auto it = v.find(m);
83 if (it == v.end()) throw CoolProp::ValueError(format("Does not have member [%s]", m.c_str()));
84 if (!it->is_number()) throw CoolProp::ValueError(format("Member [%s] is not a number", m.c_str()));
85 return it->get<double>();
86}
87
88inline bool get_bool(const nlohmann::json& v, const std::string& m) {
89 auto it = v.find(m);
90 if (it == v.end()) throw CoolProp::ValueError(format("Does not have member [%s]", m.c_str()));
91 if (!it->is_boolean()) throw CoolProp::ValueError(format("Member [%s] is not a boolean", m.c_str()));
92 return it->get<bool>();
93}
94
95inline std::string get_string(const nlohmann::json& v, const std::string& m) {
96 auto it = v.find(m);
97 if (it == v.end()) throw CoolProp::ValueError(format("Does not have member [%s]", m.c_str()));
98 if (!it->is_string()) throw CoolProp::ValueError(format("Member [%s] is not a string", m.c_str()));
99 return it->get<std::string>();
100}
101
102inline std::vector<double> get_double_array(const nlohmann::json& v) {
103 if (!v.is_array()) throw CoolProp::ValueError("input is not an array");
104 std::vector<double> out;
105 out.reserve(v.size());
106 for (const auto& el : v) {
107 if (!el.is_number()) throw CoolProp::ValueError("input is not a number");
108 out.push_back(el.get<double>());
109 }
110 return out;
111}
112
113inline std::vector<double> get_double_array(const nlohmann::json& v, const std::string& m) {
114 auto it = v.find(m);
115 if (it == v.end()) throw CoolProp::ValueError(format("Does not have member [%s]", m.c_str()));
116 return get_double_array(*it);
117}
118
119inline std::vector<CoolPropDbl> get_long_double_array(const nlohmann::json& v) {
120 if (!v.is_array()) throw CoolProp::ValueError("input is not an array");
121 std::vector<CoolPropDbl> out;
122 out.reserve(v.size());
123 for (const auto& el : v) {
124 if (!el.is_number()) throw CoolProp::ValueError("input is not a number");
125 out.push_back(static_cast<CoolPropDbl>(el.get<double>()));
126 }
127 return out;
128}
129
130inline std::vector<CoolPropDbl> get_long_double_array(const nlohmann::json& v, const std::string& name) {
131 auto it = v.find(name);
132 if (it == v.end()) throw CoolProp::ValueError(format("Does not have member [%s]", name.c_str()));
133 return get_long_double_array(*it);
134}
135
136inline std::vector<std::vector<double>> get_double_array2D(const nlohmann::json& v) {
137 if (!v.is_array()) throw CoolProp::ValueError("input is not an array");
138 std::vector<std::vector<double>> out;
139 for (const auto& row : v) {
140 if (!row.is_array()) throw CoolProp::ValueError(format("input \"%s\" is not a 2D array", json2string(v).c_str()));
141 out.push_back(get_double_array(row));
142 }
143 return out;
144}
145
146inline std::vector<std::vector<CoolPropDbl>> get_long_double_array2D(const nlohmann::json& v) {
147 if (!v.is_array()) throw CoolProp::ValueError("input is not a 2D array");
148 std::vector<std::vector<CoolPropDbl>> out;
149 for (const auto& row : v) {
150 if (!row.is_array()) throw CoolProp::ValueError("input is not a 2D array");
151 out.push_back(get_long_double_array(row));
152 }
153 return out;
154}
155
156inline std::vector<std::string> get_string_array(const nlohmann::json& v) {
157 if (!v.is_array()) throw CoolProp::ValueError("input is not an array");
158 std::vector<std::string> out;
159 out.reserve(v.size());
160 for (const auto& el : v) {
161 if (!el.is_string()) throw CoolProp::ValueError("input is not a string");
162 out.push_back(el.get<std::string>());
163 }
164 return out;
165}
166
167inline std::vector<std::string> get_string_array(const nlohmann::json& v, const std::string& m) {
168 auto it = v.find(m);
169 if (it == v.end()) throw CoolProp::ValueError(format("Does not have member [%s]", m.c_str()));
170 return get_string_array(*it);
171}
172
175{
181
186inline schema_validation_code validate_schema(std::string_view schemaJson, std::string_view inputJson, std::string& errstr) {
187 nlohmann::json schemaDoc;
188 try {
189 schemaDoc = nlohmann::json::parse(schemaJson);
190 } catch (const std::exception& e) {
191 errstr = std::string("Invalid schema: ") + e.what();
192 return SCHEMA_INVALID_JSON;
193 }
194
195 nlohmann::json inputDoc;
196 try {
197 inputDoc = nlohmann::json::parse(inputJson);
198 } catch (const std::exception& e) {
199 errstr = std::string("Invalid input json: ") + e.what();
200 return INPUT_INVALID_JSON;
201 }
202
203 valijson::Schema schema;
204 valijson::SchemaParser parser;
205 valijson::adapters::NlohmannJsonAdapter schemaAdapter(schemaDoc);
206 try {
207 parser.populateSchema(schemaAdapter, schema);
208 } catch (const std::exception& e) {
209 errstr = std::string("Invalid schema: ") + e.what();
210 return SCHEMA_INVALID_JSON;
211 }
212
213 valijson::Validator validator;
214 valijson::ValidationResults results;
215 valijson::adapters::NlohmannJsonAdapter inputAdapter(inputDoc);
216 try {
217 if (!validator.validate(schema, inputAdapter, &results)) {
218 std::string msg;
219 valijson::ValidationResults::Error error;
220 while (results.popError(error)) {
221 for (const std::string& ctx : error.context)
222 msg += ctx;
223 msg += ": " + error.description + "\n";
224 }
225 errstr = msg;
227 }
228 } catch (const std::exception& e) {
229 errstr = std::string("Schema validation internal error: ") + e.what();
231 }
233}
234
235} // namespace cpjson
236
237#endif // COOLPROP_DETAIL_JSON_H