NeoGraph 0.10.0
A C++17 Graph Agent Engine Library — LangGraph for C++
Loading...
Searching...
No Matches
json.h
Go to the documentation of this file.
1
21#pragma once
22
23#include <neograph/api.h>
24
25#include <yyjson.h>
26
27#include <cstdint>
28#include <initializer_list>
29#include <iosfwd>
30#include <ostream>
31#include <map>
32#include <memory>
33#include <stdexcept>
34#include <string>
35#include <string_view>
36#include <type_traits>
37#include <utility>
38#include <vector>
39
40namespace neograph {
41
42class NEOGRAPH_API json {
43public:
44 // ----- Exceptions -----
45 class exception : public std::runtime_error {
46 public:
47 using std::runtime_error::runtime_error;
48 };
49 class parse_error : public exception {
50 public:
51 using exception::exception;
52 };
53 class type_error : public exception {
54 public:
55 using exception::exception;
56 };
57 class out_of_range : public exception {
58 public:
59 using exception::exception;
60 };
61
62 // ----- Construction -----
63 json(); // null
64 json(std::nullptr_t);
65 json(bool b);
66 json(int i);
67 json(unsigned i);
68 json(long i);
69 json(unsigned long i);
70 json(long long i);
71 json(unsigned long long i);
72 json(double d);
73 json(float f);
74 json(const char* s);
75 json(const std::string& s);
76 json(std::string_view s);
77 json(const std::vector<std::string>& v);
78
79 json(std::initializer_list<json> il);
80
81 // ----- Copy / move -----
82 json(const json& other);
83 json(json&& other) noexcept;
84 json& operator=(const json& other) &;
85 json& operator=(json&& other) & noexcept;
86
87 // rvalue assignment: write-through to parent if this handle came from operator[]
88 json& operator=(const json& other) &&;
89 json& operator=(json&& other) && noexcept;
90
91 ~json();
92
93 // ----- Factories -----
94 static json object();
95 static json array();
96 static json array(std::initializer_list<json> il);
97
98 static json parse(std::string_view s);
99 static json parse(std::istream& in);
100
101 // ----- Type queries -----
102 bool is_null() const noexcept;
103 bool is_boolean() const noexcept;
104 bool is_bool() const noexcept { return is_boolean(); }
105 bool is_number() const noexcept;
106 bool is_number_integer() const noexcept;
107 bool is_number_unsigned() const noexcept;
108 bool is_number_float() const noexcept;
109 bool is_string() const noexcept;
110 bool is_array() const noexcept;
111 bool is_object() const noexcept;
112 bool is_primitive() const noexcept; // not null and not container
113
114 // ----- Size -----
115 size_t size() const noexcept;
116 bool empty() const noexcept;
117
118 // ----- Access (mutable — get-or-create) -----
119 json operator[](const char* key);
120 json operator[](const std::string& key);
121 json operator[](size_t idx);
122 json operator[](int idx) { return (*this)[static_cast<size_t>(idx)]; }
123
124 // ----- Access (const — read-only) -----
125 json operator[](const char* key) const;
126 json operator[](const std::string& key) const;
127 json operator[](size_t idx) const;
128 json operator[](int idx) const { return (*this)[static_cast<size_t>(idx)]; }
129
130 // ----- at (throws on missing) -----
131 json at(const char* key);
132 json at(const std::string& key);
133 json at(size_t idx);
134 json at(int idx) { return at(static_cast<size_t>(idx)); }
135 json at(const char* key) const;
136 json at(const std::string& key) const;
137 json at(size_t idx) const;
138 json at(int idx) const { return at(static_cast<size_t>(idx)); }
139
140 // ----- Array endpoints (nlohmann-compat shorthand for arr[0] / arr[size-1]) -----
141 // Throws std::out_of_range if !is_array() or empty(); matches at()'s contract.
142 json front();
143 json back();
144 json front() const;
145 json back() const;
146
147 // ----- Membership -----
148 bool contains(const char* key) const;
149 bool contains(const std::string& key) const;
150
151 // ----- Extraction -----
152 template <typename T> T get() const;
153
154 // ----- Safe get with default (object-scoped) -----
155 template <typename T> T value(const std::string& key, const T& default_val) const;
156 std::string value(const std::string& key, const char* default_val) const;
157
158 // ----- Mutation -----
159 void push_back(const json& v);
160 void push_back(json&& v);
161
162 // ----- Serialization -----
163 std::string dump(int indent = -1) const;
164
165 // ----- Equality -----
166 bool operator==(const json& other) const;
167 bool operator!=(const json& other) const { return !(*this == other); }
168
169 // ----- Iteration -----
170 class NEOGRAPH_API iterator {
171 public:
172 iterator() = default;
173 json operator*() const;
174 iterator& operator++();
175 bool operator!=(const iterator& o) const { return !(*this == o); }
176 bool operator==(const iterator& o) const {
177 // Two iterators at end() compare equal regardless of idx_.
178 if (done_ && o.done_) return parent_ == o.parent_;
179 return parent_ == o.parent_ && idx_ == o.idx_ && done_ == o.done_;
180 }
181
182 // For .items() structured binding support
183 std::string key() const;
184 json value() const;
185
186 private:
187 friend class json;
188 std::shared_ptr<yyjson_mut_doc> doc_;
189 yyjson_mut_val* parent_ = nullptr;
190 std::vector<yyjson_mut_val*> keys_; // populated for objects
191 std::vector<yyjson_mut_val*> vals_; // populated for both
192 size_t idx_ = 0;
193 bool is_object_ = false;
194 bool done_ = true;
195 };
196
197 iterator begin() const;
198 iterator end() const;
199
200 // find (for .find(key) != .end() idiom)
201 iterator find(const std::string& key) const;
202 iterator find(const char* key) const;
203
204 // items() for structured binding iteration — defined after class body.
205 class items_proxy;
206 items_proxy items() const;
207
208 // ----- Internal helpers (public for ADL callers) -----
209 yyjson_mut_doc* raw_doc() const { return doc_.get(); }
210 yyjson_mut_val* raw_val() const { return val_; }
211
212private:
213 // Doc shared via custom deleter; val_ points into doc_.
214 std::shared_ptr<yyjson_mut_doc> doc_;
215 yyjson_mut_val* val_ = nullptr;
216
217 // Write-through target (set only for handles returned by operator[]).
218 // When operator= is called on an rvalue with a parent set, the assignment
219 // replaces the corresponding key/index in the parent container.
220 yyjson_mut_val* parent_ = nullptr;
221 bool parent_is_array_ = false;
222 std::string parent_key_;
223 size_t parent_idx_ = 0;
224
225 // ----- Private constructors -----
226 json(std::shared_ptr<yyjson_mut_doc> doc, yyjson_mut_val* val);
227
228 // Private ctor used by operator[] to create a "reference" handle.
229 struct ref_tag {};
230 json(ref_tag,
231 std::shared_ptr<yyjson_mut_doc> doc,
232 yyjson_mut_val* val,
233 yyjson_mut_val* parent,
234 std::string key);
235 json(ref_tag,
236 std::shared_ptr<yyjson_mut_doc> doc,
237 yyjson_mut_val* val,
238 yyjson_mut_val* parent,
239 size_t idx);
240
241 // Helper: ensure this handle has its own doc and root val.
242 void ensure_own_doc();
243
244 // Helper: write a value into parent at the stored key/idx.
245 // src_val is a mut_val owned by some doc; we deep-copy into parent's doc.
246 void write_through(yyjson_mut_val* src_val, yyjson_mut_doc* src_doc);
247
248 // Helper: take a plain mut_val and install it as this json's own root.
249 void reset_to(yyjson_mut_val* v);
250
251 // Factory helpers
252 static std::shared_ptr<yyjson_mut_doc> make_doc();
253
254 friend void to_json_forwarding(json& j, const json& v);
255};
256
257// ----- items() proxy (defined outside json so it can hold a json member) -----
258class NEOGRAPH_API json::items_proxy {
259public:
260 explicit items_proxy(const json& j) : j_(j) {}
261 class NEOGRAPH_API iterator {
262 public:
263 iterator() = default;
264 std::pair<std::string, json> operator*() const {
265 return {it_.key(), it_.value()};
266 }
267 iterator& operator++() { ++it_; return *this; }
268 bool operator!=(const iterator& o) const { return it_ != o.it_; }
269 bool operator==(const iterator& o) const { return it_ == o.it_; }
270 private:
271 friend class items_proxy;
272 json::iterator it_;
273 };
274 iterator begin() const { iterator it; it.it_ = j_.begin(); return it; }
275 iterator end() const { iterator it; it.it_ = j_.end(); return it; }
276private:
277 json j_;
278};
279
280inline json::items_proxy json::items() const { return items_proxy{*this}; }
281
282// ----- ADL-style helpers used in types.h — extensible via argument-dependent lookup -----
283inline void to_json(json& j, const json& v) { j = v; }
284inline void from_json(const json& j, json& v) { v = j; }
285
286// ----- Stream output (via dump()) -----
287inline std::ostream& operator<<(std::ostream& os, const json& j) {
288 return os << j.dump();
289}
290
291// ===========================================================================
292// Explicit specializations declared here so template `value<T>()` sees them.
293//
294// NEOGRAPH_API on each one is required for MSVC: explicit specializations
295// declared at namespace scope (outside the class body) don't inherit the
296// class's dllexport. The implementations live in src/core/json.cpp;
297// without per-specialization annotation, neograph_llm and other downstream
298// libraries hit `LNK2019: unresolved external symbol json::get<T>` even
299// though the symbols are present in neograph_core.dll.
300// ===========================================================================
301
302template <> NEOGRAPH_API json json::get<json>() const;
303template <> NEOGRAPH_API std::string json::get<std::string>() const;
304template <> NEOGRAPH_API bool json::get<bool>() const;
305template <> NEOGRAPH_API int json::get<int>() const;
306template <> NEOGRAPH_API unsigned json::get<unsigned>() const;
307template <> NEOGRAPH_API long json::get<long>() const;
308template <> NEOGRAPH_API unsigned long json::get<unsigned long>() const;
309template <> NEOGRAPH_API long long json::get<long long>() const;
310template <> NEOGRAPH_API unsigned long long json::get<unsigned long long>() const;
311template <> NEOGRAPH_API double json::get<double>() const;
312template <> NEOGRAPH_API float json::get<float>() const;
313template <> NEOGRAPH_API std::vector<std::string> json::get<std::vector<std::string>>() const;
314
315template <typename T>
316T json::value(const std::string& key, const T& default_val) const {
317 if (!is_object()) return default_val;
318 auto child = (*this)[key];
319 if (child.is_null()) return default_val;
320 try {
321 return child.get<T>();
322 } catch (...) {
323 return default_val;
324 }
325}
326
327inline std::string json::value(const std::string& key, const char* default_val) const {
328 return value<std::string>(key, std::string(default_val));
329}
330
331} // namespace neograph
NEOGRAPH_API export/import macro for shared-library builds.