Fix #499: Add object_from_json, map_to_object, and object_to_map functions

Add object_from_json as a non-breaking alternative to from_json that returns
a Dynamic_Object instead of a Map, enabling dot-access syntax on JSON fields
(e.g. obj.name instead of obj["name"]). Nested JSON objects become nested
Dynamic_Objects. Also add map_to_object and object_to_map for Python-style
interconversion between maps and objects.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
leftibot 2026-04-12 17:31:34 -06:00
parent 07d62aae99
commit 74e9b69029
6 changed files with 88 additions and 0 deletions

View File

@ -2,13 +2,17 @@
#define CHAISCRIPT_SIMPLEJSON_WRAP_HPP
#include "json.hpp"
#include "../dispatchkit/dynamic_object.hpp"
namespace chaiscript {
class json_wrap {
public:
static Module &library(Module &m) {
m.add(chaiscript::fun([](const std::string &t_str) { return from_json(t_str); }), "from_json");
m.add(chaiscript::fun([](const std::string &t_str) { return object_from_json(t_str); }), "object_from_json");
m.add(chaiscript::fun(&json_wrap::to_json), "to_json");
m.add(chaiscript::fun(&json_wrap::map_to_object), "map_to_object");
m.add(chaiscript::fun(&json_wrap::object_to_map), "object_to_map");
return m;
}
@ -57,6 +61,63 @@ namespace chaiscript {
}
}
static Boxed_Value object_from_json(const json::JSON &t_json) {
switch (t_json.JSONType()) {
case json::JSON::Class::Null:
return Boxed_Value();
case json::JSON::Class::Object: {
auto obj = dispatch::Dynamic_Object("JSON_Object");
for (const auto &p : t_json.object_range()) {
obj.get_attr(p.first) = object_from_json(p.second);
}
return Boxed_Value(std::move(obj));
}
case json::JSON::Class::Array: {
std::vector<Boxed_Value> vec;
for (const auto &p : t_json.array_range()) {
vec.emplace_back(object_from_json(p));
}
return Boxed_Value(vec);
}
case json::JSON::Class::String:
return Boxed_Value(t_json.to_string());
case json::JSON::Class::Floating:
return Boxed_Value(t_json.to_float());
case json::JSON::Class::Integral:
return Boxed_Value(t_json.to_int());
case json::JSON::Class::Boolean:
return Boxed_Value(t_json.to_bool());
}
throw std::runtime_error("Unknown JSON type");
}
static Boxed_Value object_from_json(const std::string &t_json) {
try {
return object_from_json(json::JSON::Load(t_json));
} catch (const std::out_of_range &) {
throw std::runtime_error("Unparsed JSON input");
}
}
static Boxed_Value map_to_object(const std::map<std::string, Boxed_Value> &t_map) {
auto obj = dispatch::Dynamic_Object("JSON_Object");
for (const auto &p : t_map) {
obj.get_attr(p.first) = p.second;
}
return Boxed_Value(std::move(obj));
}
static std::map<std::string, Boxed_Value> object_to_map(const dispatch::Dynamic_Object &t_obj) {
return t_obj.get_attrs();
}
static std::string to_json(const Boxed_Value &t_bv) { return to_json_object(t_bv).dump(); }
static json::JSON to_json_object(const Boxed_Value &t_bv) {

View File

@ -0,0 +1,6 @@
// object_from_json: returns Dynamic_Object with dot-access on JSON fields
var obj = object_from_json("{\"name\":\"ChaiScript\",\"version\":6,\"active\":true}")
assert_equal(obj.name, "ChaiScript")
assert_equal(obj.version, 6)
assert_equal(obj.active, true)
assert_equal(obj.get_type_name(), "JSON_Object")

View File

@ -0,0 +1,3 @@
// object_from_json: nested objects become nested Dynamic_Objects
var obj = object_from_json("{\"outer\":{\"inner\":42}}")
assert_equal(obj.outer.inner, 42)

View File

@ -0,0 +1,3 @@
// object_from_json: arrays remain as vectors
var obj = object_from_json("{\"items\":[1,2,3]}")
assert_equal(obj.items, [1,2,3])

View File

@ -0,0 +1,9 @@
// object_to_map and map_to_object conversions
var m = ["a": 1, "b": "hello"]
var obj = map_to_object(m)
assert_equal(obj.a, 1)
assert_equal(obj.b, "hello")
var m2 = object_to_map(obj)
assert_equal(m2["a"], 1)
assert_equal(m2["b"], "hello")

View File

@ -0,0 +1,6 @@
// object_from_json roundtrip through to_json
var json_str = "{\"key\":\"value\"}"
var obj = object_from_json(json_str)
var result = to_json(obj)
var obj2 = object_from_json(result)
assert_equal(obj2.key, "value")