1+ // JsonConverters.cpp
2+ #include " JsonConverters.hpp"
3+ #include " ../../json.hpp"
4+ #include < sstream>
5+ #include < iomanip>
6+ #include < cmath>
7+ #include < limits>
8+
9+ namespace Modules {
10+ namespace JsonConverters {
11+
12+ namespace {
13+
14+ /* *
15+ * @brief Helper function to create detailed error messages
16+ *
17+ * @param operation The operation being performed (e.g., "ValuePtr to JSON")
18+ * @param valueType The type of the ValuePtr
19+ * @param context Additional context information
20+ * @return std::string Detailed error message
21+ */
22+ std::string createErrorMessage (const std::string& operation,
23+ Symbols::Variables::Type valueType,
24+ const std::string& context = " " ) {
25+ std::string typeStr = Symbols::Variables::TypeToString (valueType);
26+ std::string msg = " Conversion error in " + operation +
27+ " : unsupported type '" + typeStr + " '" ;
28+
29+ if (!context.empty ()) {
30+ msg += " in context: " + context;
31+ }
32+
33+ return msg;
34+ }
35+
36+ /* *
37+ * @brief Helper function to create detailed error messages for JSON conversion
38+ *
39+ * @param operation The operation being performed (e.g., "JSON to ValuePtr")
40+ * @param jsonType The type of the JSON value
41+ * @param context Additional context information
42+ * @return std::string Detailed error message
43+ */
44+ std::string createJsonErrorMessage (const std::string& operation,
45+ const std::string& jsonType,
46+ const std::string& context = " " ) {
47+ std::string msg = " Conversion error in " + operation +
48+ " : unsupported JSON type '" + jsonType + " '" ;
49+
50+ if (!context.empty ()) {
51+ msg += " in context: " + context;
52+ }
53+
54+ return msg;
55+ }
56+
57+ /* *
58+ * @brief Converts a JSON number to the appropriate VoidScript numeric type
59+ *
60+ * @param json The JSON number value
61+ * @return Symbols::ValuePtr The converted numeric ValuePtr
62+ */
63+ Symbols::ValuePtr convertJsonNumberToValue (const nlohmann::json& json) {
64+ if (json.is_number_integer ()) {
65+ return Symbols::ValuePtr (static_cast <int >(json.get <int64_t >()));
66+ } else if (json.is_number_unsigned ()) {
67+ return Symbols::ValuePtr (static_cast <int >(json.get <uint64_t >()));
68+ } else if (json.is_number_float ()) {
69+ double value = json.get <double >();
70+ // Check if it can be represented as a float without losing precision
71+ if (value >= std::numeric_limits<float >::min () &&
72+ value <= std::numeric_limits<float >::max () &&
73+ std::abs (value) <= std::numeric_limits<float >::max ()) {
74+ return Symbols::ValuePtr (static_cast <float >(value));
75+ }
76+ return Symbols::ValuePtr (value);
77+ }
78+
79+ throw std::runtime_error (" Invalid JSON number type" );
80+ }
81+
82+ /* *
83+ * @brief Recursively converts a JSON object to VoidScript ObjectMap
84+ *
85+ * @param json The JSON object to convert
86+ * @return Symbols::ObjectMap The converted ObjectMap
87+ */
88+ Symbols::ObjectMap convertJsonObjectToMap (const nlohmann::json& json) {
89+ Symbols::ObjectMap result;
90+
91+ for (auto it = json.begin (); it != json.end (); ++it) {
92+ const std::string& key = it.key ();
93+ const nlohmann::json& value = it.value ();
94+
95+ if (value.is_null ()) {
96+ result[key] = Symbols::ValuePtr::null (Symbols::Variables::Type::NULL_TYPE);
97+ } else if (value.is_boolean ()) {
98+ result[key] = Symbols::ValuePtr (value.get <bool >());
99+ } else if (value.is_number_integer ()) {
100+ result[key] = Symbols::ValuePtr (static_cast <int >(value.get <int64_t >()));
101+ } else if (value.is_number_unsigned ()) {
102+ result[key] = Symbols::ValuePtr (static_cast <int >(value.get <uint64_t >()));
103+ } else if (value.is_number_float ()) {
104+ result[key] = Symbols::ValuePtr (value.get <double >());
105+ } else if (value.is_string ()) {
106+ result[key] = Symbols::ValuePtr (value.get <std::string>());
107+ } else if (value.is_array ()) {
108+ // Convert JSON array to ObjectMap with numeric string keys
109+ Symbols::ObjectMap arrayMap;
110+ for (size_t i = 0 ; i < value.size (); ++i) {
111+ const std::string indexKey = std::to_string (i);
112+ const nlohmann::json& element = value[i];
113+
114+ if (element.is_null ()) {
115+ arrayMap[indexKey] = Symbols::ValuePtr::null (Symbols::Variables::Type::NULL_TYPE);
116+ } else if (element.is_boolean ()) {
117+ arrayMap[indexKey] = Symbols::ValuePtr (element.get <bool >());
118+ } else if (element.is_number_integer ()) {
119+ arrayMap[indexKey] = Symbols::ValuePtr (static_cast <int >(element.get <int64_t >()));
120+ } else if (element.is_number_unsigned ()) {
121+ arrayMap[indexKey] = Symbols::ValuePtr (static_cast <int >(element.get <uint64_t >()));
122+ } else if (element.is_number_float ()) {
123+ arrayMap[indexKey] = Symbols::ValuePtr (element.get <double >());
124+ } else if (element.is_string ()) {
125+ arrayMap[indexKey] = Symbols::ValuePtr (element.get <std::string>());
126+ } else if (element.is_object ()) {
127+ arrayMap[indexKey] = Symbols::ValuePtr (convertJsonObjectToMap (element));
128+ } else if (element.is_array ()) {
129+ arrayMap[indexKey] = Symbols::ValuePtr (convertJsonObjectToMap (element));
130+ }
131+ }
132+ result[key] = Symbols::ValuePtr (arrayMap);
133+ } else if (value.is_object ()) {
134+ result[key] = Symbols::ValuePtr (convertJsonObjectToMap (value));
135+ }
136+ }
137+
138+ return result;
139+ }
140+
141+ /* *
142+ * @brief Recursively converts a VoidScript ObjectMap to JSON object
143+ *
144+ * @param objMap The ObjectMap to convert
145+ * @return nlohmann::json The converted JSON object
146+ */
147+ nlohmann::json convertMapToJson (const Symbols::ObjectMap& objMap) {
148+ nlohmann::json result;
149+
150+ for (const auto & kv : objMap) {
151+ const std::string& key = kv.first ;
152+ const Symbols::ValuePtr& value = kv.second ;
153+
154+ switch (value.getType ()) {
155+ case Symbols::Variables::Type::NULL_TYPE:
156+ result[key] = nullptr ;
157+ break ;
158+
159+ case Symbols::Variables::Type::BOOLEAN:
160+ result[key] = value.get <bool >();
161+ break ;
162+
163+ case Symbols::Variables::Type::INTEGER:
164+ result[key] = value.get <int >();
165+ break ;
166+
167+ case Symbols::Variables::Type::FLOAT:
168+ result[key] = value.get <float >();
169+ break ;
170+
171+ case Symbols::Variables::Type::DOUBLE:
172+ result[key] = value.get <double >();
173+ break ;
174+
175+ case Symbols::Variables::Type::STRING:
176+ result[key] = value.get <std::string>();
177+ break ;
178+
179+ case Symbols::Variables::Type::OBJECT:
180+ case Symbols::Variables::Type::CLASS:
181+ result[key] = convertMapToJson (value.get <Symbols::ObjectMap>());
182+ break ;
183+
184+ case Symbols::Variables::Type::ENUM:
185+ // Convert enum to string representation
186+ result[key] = value.toString ();
187+ break ;
188+
189+ default :
190+ // Unsupported type, convert to null
191+ result[key] = nullptr ;
192+ break ;
193+ }
194+ }
195+
196+ return result;
197+ }
198+
199+ } // anonymous namespace
200+
201+ nlohmann::json valueToJson (const Symbols::ValuePtr& value) {
202+ if (!value) {
203+ throw std::runtime_error (" Cannot convert null ValuePtr to JSON" );
204+ }
205+
206+ return valueToJsonWithContext (value, " " );
207+ }
208+
209+ Symbols::ValuePtr jsonToValue (const nlohmann::json& json) {
210+ return jsonToValueWithContext (json, " " );
211+ }
212+
213+ nlohmann::json valueToJsonWithContext (const Symbols::ValuePtr& value, const std::string& context) {
214+ if (!value) {
215+ throw std::runtime_error (createErrorMessage (" ValuePtr to JSON conversion" ,
216+ Symbols::Variables::Type::NULL_TYPE, context));
217+ }
218+
219+ Symbols::Variables::Type type = value.getType ();
220+
221+ switch (type) {
222+ case Symbols::Variables::Type::NULL_TYPE:
223+ return nullptr ;
224+
225+ case Symbols::Variables::Type::BOOLEAN:
226+ return value.get <bool >();
227+
228+ case Symbols::Variables::Type::INTEGER:
229+ return value.get <int >();
230+
231+ case Symbols::Variables::Type::FLOAT:
232+ return value.get <float >();
233+
234+ case Symbols::Variables::Type::DOUBLE:
235+ return value.get <double >();
236+
237+ case Symbols::Variables::Type::STRING:
238+ return value.get <std::string>();
239+
240+ case Symbols::Variables::Type::OBJECT:
241+ case Symbols::Variables::Type::CLASS:
242+ return convertMapToJson (value.get <Symbols::ObjectMap>());
243+
244+ case Symbols::Variables::Type::ENUM:
245+ return value.toString ();
246+
247+ default :
248+ throw std::runtime_error (createErrorMessage (" ValuePtr to JSON conversion" , type, context));
249+ }
250+ }
251+
252+ Symbols::ValuePtr jsonToValueWithContext (const nlohmann::json& json, const std::string& context) {
253+ if (json.is_null ()) {
254+ return Symbols::ValuePtr::null (Symbols::Variables::Type::NULL_TYPE);
255+ } else if (json.is_boolean ()) {
256+ return Symbols::ValuePtr (json.get <bool >());
257+ } else if (json.is_number_integer ()) {
258+ return Symbols::ValuePtr (static_cast <int >(json.get <int64_t >()));
259+ } else if (json.is_number_unsigned ()) {
260+ return Symbols::ValuePtr (static_cast <int >(json.get <uint64_t >()));
261+ } else if (json.is_number_float ()) {
262+ return Symbols::ValuePtr (json.get <double >());
263+ } else if (json.is_string ()) {
264+ return Symbols::ValuePtr (json.get <std::string>());
265+ } else if (json.is_array ()) {
266+ // Convert JSON array to ObjectMap with numeric string keys
267+ Symbols::ObjectMap arrayMap;
268+ for (size_t i = 0 ; i < json.size (); ++i) {
269+ const std::string indexKey = std::to_string (i);
270+ const nlohmann::json& element = json[i];
271+
272+ if (element.is_null ()) {
273+ arrayMap[indexKey] = Symbols::ValuePtr::null (Symbols::Variables::Type::NULL_TYPE);
274+ } else if (element.is_boolean ()) {
275+ arrayMap[indexKey] = Symbols::ValuePtr (element.get <bool >());
276+ } else if (element.is_number_integer ()) {
277+ arrayMap[indexKey] = Symbols::ValuePtr (static_cast <int >(element.get <int64_t >()));
278+ } else if (element.is_number_unsigned ()) {
279+ arrayMap[indexKey] = Symbols::ValuePtr (static_cast <int >(element.get <uint64_t >()));
280+ } else if (element.is_number_float ()) {
281+ arrayMap[indexKey] = Symbols::ValuePtr (element.get <double >());
282+ } else if (element.is_string ()) {
283+ arrayMap[indexKey] = Symbols::ValuePtr (element.get <std::string>());
284+ } else if (element.is_object ()) {
285+ arrayMap[indexKey] = Symbols::ValuePtr (convertJsonObjectToMap (element));
286+ } else if (element.is_array ()) {
287+ arrayMap[indexKey] = Symbols::ValuePtr (convertJsonObjectToMap (element));
288+ }
289+ }
290+ return Symbols::ValuePtr (arrayMap);
291+ } else if (json.is_object ()) {
292+ return Symbols::ValuePtr (convertJsonObjectToMap (json));
293+ } else {
294+ throw std::runtime_error (createJsonErrorMessage (" JSON to ValuePtr conversion" ,
295+ " unknown" , context));
296+ }
297+ }
298+
299+ bool canConvertToJson (const Symbols::ValuePtr& value) {
300+ if (!value) {
301+ return false ;
302+ }
303+
304+ Symbols::Variables::Type type = value.getType ();
305+
306+ switch (type) {
307+ case Symbols::Variables::Type::NULL_TYPE:
308+ case Symbols::Variables::Type::BOOLEAN:
309+ case Symbols::Variables::Type::INTEGER:
310+ case Symbols::Variables::Type::FLOAT:
311+ case Symbols::Variables::Type::DOUBLE:
312+ case Symbols::Variables::Type::STRING:
313+ case Symbols::Variables::Type::OBJECT:
314+ case Symbols::Variables::Type::CLASS:
315+ case Symbols::Variables::Type::ENUM:
316+ return true ;
317+
318+ default :
319+ return false ;
320+ }
321+ }
322+
323+ bool canConvertToValue (const nlohmann::json& json) {
324+ return json.is_null () || json.is_boolean () || json.is_number () ||
325+ json.is_string () || json.is_array () || json.is_object ();
326+ }
327+
328+ } // namespace JsonConverters
329+ } // namespace Modules
0 commit comments