Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
json_helper.h
Go to the documentation of this file.
1/*
2 * Olympe Engine V2 - 2025
3 * Nicolas Chereau
4 * nchereau@gmail.com
5 *
6 * JSON Helper - Centralized JSON operations
7 *
8 * This header-only file provides safe and convenient functions for JSON operations
9 * to eliminate code duplication across the project.
10 *
11 * Features:
12 * - File I/O (load/save JSON files)
13 * - Safe accessors with default values
14 * - Array/Object helpers
15 * - Config file helpers
16 * - Key validation
17 */
18
19#pragma once
20
21#include "third_party/nlohmann/json.hpp"
22#include <fstream>
23#include <iostream>
24#include <string>
25#include <vector>
26#include <functional>
27
29
30namespace JsonHelper {
31
32// ============================================================================
33// File I/O Functions
34// ============================================================================
35
36/**
37 * Load and parse a JSON file
38 * @param filepath Path to the JSON file
39 * @param j Output json object
40 * @return true if successful, false otherwise
41 */
42inline bool LoadJsonFromFile(const std::string& filepath, json& j)
43{
44 try
45 {
46 std::ifstream file(filepath);
47 if (!file.is_open())
48 {
49 std::cerr << "JsonHelper: Failed to open file: " << filepath << std::endl;
50 return false;
51 }
52
53 std::string jsonStr((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
54 file.close();
55
56 j = json::parse(jsonStr);
57 return true;
58 }
59 catch (const std::exception& e)
60 {
61 std::cerr << "JsonHelper: Error loading JSON from " << filepath << ": " << e.what() << std::endl;
62 return false;
63 }
64}
65
66/**
67 * Save a JSON object to a file with formatting
68 * @param filepath Path to save the file
69 * @param j JSON object to save
70 * @param indent Number of spaces for indentation (default: 4)
71 * @return true if successful, false otherwise
72 */
73inline bool SaveJsonToFile(const std::string& filepath, const json& j, int indent = 4)
74{
75 try
76 {
77 std::ofstream file(filepath);
78 if (!file.is_open())
79 {
80 std::cerr << "JsonHelper: Failed to open file for writing: " << filepath << std::endl;
81 return false;
82 }
83
84 file << j.dump(indent);
85 file.close();
86 return true;
87 }
88 catch (const std::exception& e)
89 {
90 std::cerr << "JsonHelper: Error saving JSON to " << filepath << ": " << e.what() << std::endl;
91 return false;
92 }
93}
94
95// ============================================================================
96// Safe Accessor Functions
97// ============================================================================
98
99/**
100 * Safely get a string value from JSON
101 * @param j JSON object
102 * @param key Key to access
103 * @param defaultValue Default value if key doesn't exist or type mismatch
104 * @return The string value or default
105 */
106inline std::string GetString(const json& j, const std::string& key, const std::string& defaultValue = "")
107{
108 if (j.contains(key) && j[key].is_string())
109 return j[key].get<std::string>();
110 return defaultValue;
111}
112
113/**
114 * Safely get an integer value from JSON
115 * @param j JSON object
116 * @param key Key to access
117 * @param defaultValue Default value if key doesn't exist or type mismatch
118 * @return The int value or default
119 */
120inline int GetInt(const json& j, const std::string& key, int defaultValue = 0)
121{
122 if (j.contains(key) && j[key].is_number())
123 return j[key].get<int>();
124 return defaultValue;
125}
126
127/**
128 * Safely get an unsigned integer value from JSON
129 * @param j JSON object
130 * @param key Key to access
131 * @param defaultValue Default value if key doesn't exist or type mismatch
132 * @return The uint32_t value or default
133 */
134inline uint32_t GetUInt(const json& j, const std::string& key, uint32_t defaultValue = 0)
135{
136 if (j.contains(key) && j[key].is_number())
137 {
138 int value = j[key].get<int>();
139 // Protect against negative values
140 if (value < 0)
141 return defaultValue;
142 return static_cast<uint32_t>(value);
143 }
144 return defaultValue;
145}
146
147/**
148 * Safely get a float value from JSON
149 * @param j JSON object
150 * @param key Key to access
151 * @param defaultValue Default value if key doesn't exist or type mismatch
152 * @return The float value or default
153 */
154inline float GetFloat(const json& j, const std::string& key, float defaultValue = 0.0f)
155{
156 if (j.contains(key) && j[key].is_number())
157 return static_cast<float>(j[key].get<double>());
158 return defaultValue;
159}
160
161/**
162 * Safely get a double value from JSON
163 * @param j JSON object
164 * @param key Key to access
165 * @param defaultValue Default value if key doesn't exist or type mismatch
166 * @return The double value or default
167 */
168inline double GetDouble(const json& j, const std::string& key, double defaultValue = 0.0)
169{
170 if (j.contains(key) && j[key].is_number())
171 return j[key].get<double>();
172 return defaultValue;
173}
174
175/**
176 * Safely get a boolean value from JSON
177 * @param j JSON object
178 * @param key Key to access
179 * @param defaultValue Default value if key doesn't exist or type mismatch
180 * @return The bool value or default
181 */
182inline bool GetBool(const json& j, const std::string& key, bool defaultValue = false)
183{
184 if (j.contains(key) && j[key].is_boolean())
185 return j[key].get<bool>();
186 return defaultValue;
187}
188
189/**
190 * Generic template function to safely get any type from JSON
191 * @tparam T Type to extract
192 * @param j JSON object
193 * @param key Key to access
194 * @param defaultValue Default value if key doesn't exist or type mismatch
195 * @return The value of type T or default
196 *
197 * @note Use specialized functions (GetInt, GetFloat, etc.) when available
198 * for better type safety. This template is useful for custom types or
199 * when working with generic code.
200 */
201template<typename T>
202inline T json_get(const json& j, const std::string& key, const T& defaultValue)
203{
204 try
205 {
206 if (j.contains(key) && !j[key].is_null())
207 {
208 return j[key].get<T>();
209 }
210 }
211 catch (const std::exception&)
212 {
213 // Type mismatch or conversion error - return default
214 }
215 return defaultValue;
216}
217
218// ============================================================================
219// Array/Object Helper Functions
220// ============================================================================
221
222/**
223 * Check if a key contains an array
224 * @param j JSON object
225 * @param key Key to check
226 * @return true if the key exists and is an array
227 */
228inline bool IsArray(const json& j, const std::string& key)
229{
230 return j.contains(key) && j[key].is_array();
231}
232
233/**
234 * Check if a key contains an object
235 * @param j JSON object
236 * @param key Key to check
237 * @return true if the key exists and is an object
238 */
239inline bool IsObject(const json& j, const std::string& key)
240{
241 return j.contains(key) && j[key].is_object();
242}
243
244/**
245 * Get the size of an array
246 * @param j JSON object
247 * @param key Key to the array
248 * @return Size of the array, or 0 if not an array
249 */
250inline size_t GetArraySize(const json& j, const std::string& key)
251{
252 if (IsArray(j, key))
253 return j[key].size();
254 return 0;
255}
256
257/**
258 * Iterate over an array with a callback function
259 * @param j JSON object
260 * @param key Key to the array
261 * @param callback Function to call for each element (takes const json& and size_t index)
262 */
263inline void ForEachInArray(const json& j, const std::string& key, std::function<void(const json&, size_t)> callback)
264{
265 if (IsArray(j, key))
266 {
267 const auto& arr = j[key];
268 for (size_t i = 0; i < arr.size(); ++i)
269 {
270 callback(arr[i], i);
271 }
272 }
273}
274
275/**
276 * Get a nested float value from a parent object
277 * @param j JSON object
278 * @param parentKey Key to the parent object
279 * @param childKey Key within the parent object
280 * @param defaultValue Default value if not found
281 * @return The float value or default
282 */
283inline float GetNestedFloat(const json& j, const std::string& parentKey, const std::string& childKey, float defaultValue = 0.0f)
284{
285 if (IsObject(j, parentKey))
286 {
287 return GetFloat(j[parentKey], childKey, defaultValue);
288 }
289 return defaultValue;
290}
291
292/**
293 * Get a nested string value from a parent object
294 * @param j JSON object
295 * @param parentKey Key to the parent object
296 * @param childKey Key within the parent object
297 * @param defaultValue Default value if not found
298 * @return The string value or default
299 */
300inline std::string GetNestedString(const json& j, const std::string& parentKey, const std::string& childKey, const std::string& defaultValue = "")
301{
302 if (IsObject(j, parentKey))
303 {
304 return GetString(j[parentKey], childKey, defaultValue);
305 }
306 return defaultValue;
307}
308
309/**
310 * Get a nested int value from a parent object
311 * @param j JSON object
312 * @param parentKey Key to the parent object
313 * @param childKey Key within the parent object
314 * @param defaultValue Default value if not found
315 * @return The int value or default
316 */
317inline int GetNestedInt(const json& j, const std::string& parentKey, const std::string& childKey, int defaultValue = 0)
318{
319 if (IsObject(j, parentKey))
320 {
321 return GetInt(j[parentKey], childKey, defaultValue);
322 }
323 return defaultValue;
324}
325
326// ============================================================================
327// Config Helper Functions
328// ============================================================================
329
330/**
331 * Load a configuration file (alias for LoadJsonFromFile)
332 * @param filepath Path to the config file
333 * @param j Output json object
334 * @return true if successful, false otherwise
335 */
336inline bool LoadConfig(const std::string& filepath, json& j)
337{
338 return LoadJsonFromFile(filepath, j);
339}
340
341/**
342 * Save a configuration file (alias for SaveJsonToFile with indent=2)
343 * @param filepath Path to save the config file
344 * @param j JSON object to save
345 * @return true if successful, false otherwise
346 */
347inline bool SaveConfig(const std::string& filepath, const json& j)
348{
349 return SaveJsonToFile(filepath, j, 2);
350}
351
352// ============================================================================
353// Validation Functions
354// ============================================================================
355
356/**
357 * Validate that all required keys exist in a JSON object
358 * @param j JSON object to validate
359 * @param requiredKeys Vector of required key names
360 * @return true if all keys exist, false otherwise
361 */
362inline bool ValidateKeys(const json& j, const std::vector<std::string>& requiredKeys)
363{
364 for (const auto& key : requiredKeys)
365 {
366 if (!j.contains(key))
367 {
368 std::cerr << "JsonHelper: Missing required key: " << key << std::endl;
369 return false;
370 }
371 }
372 return true;
373}
374
375/**
376 * Validate that all required keys exist and log which ones are missing
377 * @param j JSON object to validate
378 * @param requiredKeys Vector of required key names
379 * @param context Context string for error messages
380 * @return true if all keys exist, false otherwise
381 */
382inline bool ValidateKeysVerbose(const json& j, const std::vector<std::string>& requiredKeys, const std::string& context = "")
383{
384 bool allValid = true;
385 for (const auto& key : requiredKeys)
386 {
387 if (!j.contains(key))
388 {
389 std::cerr << "JsonHelper: Missing required key '" << key << "'";
390 if (!context.empty())
391 std::cerr << " in " << context;
392 std::cerr << std::endl;
393 allValid = false;
394 }
395 }
396 return allValid;
397}
398
399} // namespace JsonHelper
nlohmann::json json
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
std::string GetString(const json &j, const std::string &key, const std::string &defaultValue="")
Safely get a string value from JSON.
size_t GetArraySize(const json &j, const std::string &key)
Get the size of an array.
bool ValidateKeysVerbose(const json &j, const std::vector< std::string > &requiredKeys, const std::string &context="")
Validate that all required keys exist and log which ones are missing.
std::string GetNestedString(const json &j, const std::string &parentKey, const std::string &childKey, const std::string &defaultValue="")
Get a nested string value from a parent object.
uint32_t GetUInt(const json &j, const std::string &key, uint32_t defaultValue=0)
Safely get an unsigned integer value from JSON.
T json_get(const json &j, const std::string &key, const T &defaultValue)
Generic template function to safely get any type from JSON.
bool SaveConfig(const std::string &filepath, const json &j)
Save a configuration file (alias for SaveJsonToFile with indent=2)
float GetNestedFloat(const json &j, const std::string &parentKey, const std::string &childKey, float defaultValue=0.0f)
Get a nested float value from a parent object.
double GetDouble(const json &j, const std::string &key, double defaultValue=0.0)
Safely get a double value from JSON.
int GetNestedInt(const json &j, const std::string &parentKey, const std::string &childKey, int defaultValue=0)
Get a nested int value from a parent object.
bool LoadJsonFromFile(const std::string &filepath, json &j)
Load and parse a JSON file.
Definition json_helper.h:42
bool ValidateKeys(const json &j, const std::vector< std::string > &requiredKeys)
Validate that all required keys exist in a JSON object.
int GetInt(const json &j, const std::string &key, int defaultValue=0)
Safely get an integer value from JSON.
bool LoadConfig(const std::string &filepath, json &j)
Load a configuration file (alias for LoadJsonFromFile)
void ForEachInArray(const json &j, const std::string &key, std::function< void(const json &, size_t)> callback)
Iterate over an array with a callback function.
float GetFloat(const json &j, const std::string &key, float defaultValue=0.0f)
Safely get a float value from JSON.
bool SaveJsonToFile(const std::string &filepath, const json &j, int indent=4)
Save a JSON object to a file with formatting.
Definition json_helper.h:73
bool IsArray(const json &j, const std::string &key)
Check if a key contains an array.
bool IsObject(const json &j, const std::string &key)
Check if a key contains an object.
bool GetBool(const json &j, const std::string &key, bool defaultValue=false)
Safely get a boolean value from JSON.
nlohmann::json json