Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
JSONInterface.cxx
Go to the documentation of this file.
1/*
2 * Project: RooFit
3 * Authors:
4 * Carsten D. Burgard, DESY/ATLAS, Dec 2021
5 *
6 * Copyright (c) 2022, CERN
7 *
8 * Redistribution and use in source and binary forms,
9 * with or without modification, are permitted according to the terms
10 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
11 */
12
14
15#include "JSONReadRecorder.h"
16
17#include <nlohmann/json.hpp>
18
19#include <algorithm>
20#include <deque>
21#include <istream>
22#include <memory>
23#include <sstream>
24
25// This is the only translation unit where the JSON parsing library is used.
26// Hence, the JSON engine backing the RooFit::Detail::JSONTree could in
27// principle be swapped out by changing only this file.
28
29namespace RooFit {
30namespace Detail {
31
32namespace {
33
34nlohmann::json &json(void *value)
35{
36 return *static_cast<nlohmann::json *>(value);
37}
38
39inline nlohmann::json parseWrapper(std::istream &is)
40{
41 try {
42 return nlohmann::json::parse(is);
43 } catch (const nlohmann::json::exception &ex) {
44 throw std::runtime_error(ex.what());
45 }
46}
47
48// To check whether it's allowed to reset the type of an object. We allow
49// this for nodes that have no type yet, or nodes with an empty string.
50bool isResettingPossible(nlohmann::json const &node)
51{
52
53 if (node.type() == nlohmann::json::value_t::null) {
54 return true;
55 }
56
57 if (node.type() == nlohmann::json::value_t::string) {
58 if (node.get<std::string>().empty()) {
59 return true;
60 }
61 }
62 return false;
63}
64
65} // namespace
66
67// The nodes live as long as the tree, because the interface hands out references to them.
69public:
70 // Not root{value}, which would be an array that contains the value.
71 explicit Storage(nlohmann::json value) : root(std::move(value)), rootNode{*this, &root, ""} {}
72
73 JSONNode &makeNode(nlohmann::json &value, std::string key)
74 {
75 nodes.push_back(std::unique_ptr<JSONNode>{new JSONNode{*this, &value, std::move(key)}});
76 return *nodes.back();
77 }
78
79 // The children are created up front, such that the iterators can be pointers into this list.
80 std::vector<JSONNode *> const &makeChildren(nlohmann::json &value)
81 {
82 childLists.emplace_back();
83 std::vector<JSONNode *> &list = childLists.back();
84 if (!value.is_structured()) {
85 return list;
86 }
87 list.reserve(value.size());
88 for (auto it = value.begin(); it != value.end(); ++it) {
89 list.push_back(&makeNode(it.value(), value.is_array() ? "" : it.key()));
90 }
91 return list;
92 }
93
94 // Tells the recorders that the component `key` of `value` is read, where nullptr means all components.
95 void recordRead(void const *value, std::string const *key)
96 {
97 for (JSONReadRecorder *recorder : recorders) {
98 recorder->record(value, key);
99 }
100 }
101
102 nlohmann::json root;
104 std::vector<std::unique_ptr<JSONNode>> nodes;
105 std::deque<std::vector<JSONNode *>> childLists;
106 std::vector<JSONReadRecorder *> recorders;
107};
108
109// JSONNode
110
111JSONNode::JSONNode(Storage &storage, void *value, std::string key)
112 : _storage{&storage}, _value{value}, _key{std::move(key)}
113{
114}
115
116void JSONNode::writeJSON(std::ostream &os) const
117{
118 _storage->recordRead(_value, nullptr);
119 os << json(_value);
120}
121
122JSONNode &JSONNode::operator<<(std::string const &s)
123{
124 json(_value) = s;
125 return *this;
126}
127
129{
130 json(_value) = i;
131 return *this;
132}
133
135{
136 json(_value) = d;
137 return *this;
138}
139
141{
142 json(_value) = b;
143 return *this;
144}
145
146const JSONNode &JSONNode::operator>>(std::string &v) const
147{
148 v = json(_value).get<std::string>();
149 return *this;
150}
151
152JSONNode &JSONNode::operator[](std::string const &k)
153{
154 return _storage->makeNode(json(_value)[k], k);
155}
156
157const JSONNode &JSONNode::operator[](std::string const &k) const
158{
160 return _storage->makeNode(json(_value)[k], k);
161}
162
164{
165 return json(_value).is_array() || json(_value).is_object();
166}
167
169{
170 return json(_value).is_object();
171}
172
174{
175 return json(_value).is_array();
176}
177
179{
180 return json(_value).is_null();
181}
182
184{
185 return json(_value).is_number();
186}
187
189{
190 if (json(_value).type() == nlohmann::json::value_t::object)
191 return *this;
192
193 if (isResettingPossible(json(_value))) {
194 json(_value) = nlohmann::json::object();
195 } else {
196 throw std::runtime_error("cannot declare \"" + this->key() + "\" to be of map - type, already of type " +
197 json(_value).type_name());
198 }
199 return *this;
200}
201
203{
204 if (json(_value).type() == nlohmann::json::value_t::array)
205 return *this;
206
207 if (isResettingPossible(json(_value))) {
208 json(_value) = nlohmann::json::array();
209 } else {
210 throw std::runtime_error("cannot declare \"" + this->key() + "\" to be of seq - type, already of type " +
211 json(_value).type_name());
212 }
213 return *this;
214}
215
217{
218 json(_value) = nullptr;
219 return *this;
220}
221
223{
224 json(_value).clear();
225}
226
227void JSONNode::erase(std::string const &key)
228{
229 json(_value).erase(key);
230}
231
232std::string JSONNode::key() const
233{
234 return _key;
235}
236
237std::string JSONNode::val() const
238{
239 nlohmann::json const &value = json(_value);
240 switch (value.type()) {
241 case nlohmann::json::value_t::string: return value.get<std::string>();
242 case nlohmann::json::value_t::boolean: return value.get<bool>() ? "true" : "false";
243 case nlohmann::json::value_t::number_integer: return std::to_string(value.get<int>());
244 case nlohmann::json::value_t::number_unsigned: return std::to_string(value.get<unsigned int>());
245 case nlohmann::json::value_t::number_float: {
246 std::stringstream ss;
247 ss << value.get<double>();
248 return ss.str();
249 }
250 default:
251 throw std::runtime_error("node \"" + _key + "\": implicit string conversion for type " + value.type_name() +
252 " not supported!");
253 }
254}
255
257{
258 return json(_value).get<int>();
259}
261{
262 return json(_value).get<double>();
263}
265{
266 auto const &nd = json(_value);
267
268 // Attempting to convert zeroes and ones to bools.
269 if (nd.type() == nlohmann::json::value_t::number_unsigned) {
270 auto val = nd.get<unsigned int>();
271 if (val == 0)
272 return false;
273 if (val == 1)
274 return true;
275 }
276
277 return nd.get<bool>();
278}
279
281{
282 return !_key.empty();
283}
284
286{
287 return json(_value).is_primitive();
288}
289
290bool JSONNode::has_child(std::string const &c) const
291{
293 return json(_value).find(c) != json(_value).end();
294}
295
297{
298 json(_value).push_back("");
299 return _storage->makeNode(json(_value).back(), "");
300}
301
303{
304 return json(_value).size();
305}
306
308{
309 return _storage->makeNode(json(_value).at(pos), "");
310}
311
312const JSONNode &JSONNode::child(size_t pos) const
313{
314 _storage->recordRead(_value, nullptr);
315 return _storage->makeNode(json(_value).at(pos), "");
316}
317
319{
320 std::vector<JSONNode *> const &list = _storage->makeChildren(json(_value));
321 return {child_iterator{list.data()}, child_iterator{list.data() + list.size()}};
322}
323
325{
326 _storage->recordRead(_value, nullptr);
327 std::vector<JSONNode *> const &list = _storage->makeChildren(json(_value));
328 return {const_child_iterator{list.data()}, const_child_iterator{list.data() + list.size()}};
329}
330
331std::ostream &operator<<(std::ostream &os, JSONNode const &s)
332{
333 s.writeJSON(os);
334 return os;
335}
336
337// JSONTree
338
339JSONTree::JSONTree(std::unique_ptr<JSONNode::Storage> storage) : _storage{std::move(storage)} {}
340
341JSONTree::~JSONTree() = default;
342
344{
345 return _storage->rootNode;
346}
347
348std::unique_ptr<JSONTree> JSONTree::create()
349{
350 return std::unique_ptr<JSONTree>{new JSONTree{std::make_unique<JSONNode::Storage>(nlohmann::json{})}};
351}
352
353std::unique_ptr<JSONTree> JSONTree::create(std::istream &is)
354{
355 return std::unique_ptr<JSONTree>{new JSONTree{std::make_unique<JSONNode::Storage>(parseWrapper(is))}};
356}
357
358std::unique_ptr<JSONTree> JSONTree::create(std::string const &str)
359{
360 std::stringstream ss{str};
361 return JSONTree::create(ss);
362}
363
364// JSONReadRecorder
365
367{
368 _node._storage->recorders.push_back(this);
369}
370
372{
373 auto &recorders = _node._storage->recorders;
374 recorders.erase(std::find(recorders.begin(), recorders.end(), this));
375}
376
377void JSONReadRecorder::record(void const *value, std::string const *key)
378{
379 if (value != _node._value) {
380 return;
381 }
382 if (key) {
383 _read.insert(*key);
384 } else {
385 _readAll = true;
386 }
387}
388
389std::vector<std::string> JSONReadRecorder::unreadComponents() const
390{
391 std::vector<std::string> out;
392 nlohmann::json const &value = json(_node._value);
393 if (!value.is_object() || _readAll) {
394 return out;
395 }
396 for (auto const &item : value.items()) {
397 if (_read.count(item.key()) == 0) {
398 out.push_back(item.key());
399 }
400 }
401 return out;
402}
403
404} // namespace Detail
405} // namespace RooFit
nlohmann::json json
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define c(i)
Definition RSha256.hxx:101
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
JSONNode & makeNode(nlohmann::json &value, std::string key)
std::vector< JSONReadRecorder * > recorders
std::vector< JSONNode * > const & makeChildren(nlohmann::json &value)
void recordRead(void const *value, std::string const *key)
std::deque< std::vector< JSONNode * > > childLists
std::vector< std::unique_ptr< JSONNode > > nodes
JSONNode & operator[](std::string const &k)
JSONNode & get(std::string const &key)
void erase(std::string const &key)
Remove a child of a map, if it exists.
void writeJSON(std::ostream &os) const
JSONNode & operator<<(std::string const &s)
std::string val() const
JSONNode(JSONNode const &)=delete
const JSONNode & operator>>(std::string &v) const
bool has_child(std::string const &) const
std::string key() const
JSONNode & child(size_t pos)
Records which components of a JSON map are read while it is alive, through any node of the tree.
std::set< std::string > _read
void record(void const *value, std::string const *key)
Called by the tree for every read of the component key of value, where nullptr means all components.
JSONReadRecorder(JSONNode const &node)
std::vector< std::string > unreadComponents() const
The components of the map that were not read.
static std::unique_ptr< JSONTree > create()
JSONTree(std::unique_ptr< JSONNode::Storage > storage)
std::unique_ptr< JSONNode::Storage > _storage
Double_t ex[n]
Definition legend1.C:17
std::ostream & operator<<(std::ostream &os, RooFit::Detail::JSONNode const &s)
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:73