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 <nlohmann/json.hpp>
16
17#include <istream>
18#include <list>
19#include <memory>
20#include <sstream>
21
22// This is the only translation unit where the JSON parsing library is used.
23// Hence, the JSON engine backing the RooFit::Detail::JSONTree could in
24// principle be swapped out by changing only this file.
25
26namespace {
27
28class TJSONTree : public RooFit::Detail::JSONTree {
29public:
30 class Node : public RooFit::Detail::JSONNode {
31 protected:
32 TJSONTree *tree;
33 class Impl;
34 template <class Nd, class NdType, class json_it>
35 class ChildItImpl;
36 friend TJSONTree;
37 std::unique_ptr<Impl> node;
38
39 const TJSONTree *get_tree() const { return tree; }
40 TJSONTree *get_tree() { return tree; }
41 const Impl &get_node() const;
42 Impl &get_node();
43
44 public:
45 void writeJSON(std::ostream &os) const override;
46
47 Node(TJSONTree *t, std::istream &is);
48 Node(TJSONTree *t, Impl &other);
49 Node(TJSONTree *t);
50 Node(const Node &other);
51 virtual ~Node();
52 Node &operator<<(std::string const &s) override;
53 Node &operator<<(int i) override;
54 Node &operator<<(double d) override;
55 Node &operator<<(bool b) override;
56 const Node &operator>>(std::string &v) const override;
57 Node &operator[](std::string const &k) override;
58 const Node &operator[](std::string const &k) const override;
59 bool is_container() const override;
60 bool is_map() const override;
61 bool is_seq() const override;
62 bool is_null() const override;
63 bool is_number() const override;
64 Node &set_map() override;
65 Node &set_seq() override;
66 Node &set_null() override;
67 void clear() override;
68 std::string key() const override;
69 std::string val() const override;
70 int val_int() const override;
71 double val_double() const override;
72 bool val_bool() const override;
73 bool has_key() const override;
74 bool has_val() const override;
75 bool has_child(std::string const &) const override;
76 Node &append_child() override;
77 size_t num_children() const override;
78 Node &child(size_t pos) override;
79 const Node &child(size_t pos) const override;
80
81 children_view children() override;
82 const_children_view children() const override;
83 };
84
85protected:
86 Node root;
87 std::list<Node> _nodecache;
88
89public:
90 TJSONTree();
91 ~TJSONTree() override;
92 TJSONTree(std::istream &is);
93 TJSONTree::Node &incache(const TJSONTree::Node &n);
94
95 Node &rootnode() override { return root; }
96};
97
98inline nlohmann::json parseWrapper(std::istream &is)
99{
100 try {
101 return nlohmann::json::parse(is);
102 } catch (const nlohmann::json::exception &ex) {
103 throw std::runtime_error(ex.what());
104 }
105}
106
107// TJSONTree methods
108
109TJSONTree::TJSONTree() : root(this) {};
110
111TJSONTree::TJSONTree(std::istream &is) : root(this, is) {};
112
113TJSONTree::~TJSONTree()
114{
115 TJSONTree::_nodecache.clear();
116};
117
118TJSONTree::Node &TJSONTree::incache(const TJSONTree::Node &n)
119{
120 _nodecache.push_back(n);
121 return _nodecache.back();
122}
123
124const TJSONTree::Node::Impl &TJSONTree::Node::get_node() const
125{
126 return *node;
127}
128
129TJSONTree::Node::Impl &TJSONTree::Node::get_node()
130{
131 return *node;
132}
133
134// TJSONTree::Node implementation
135
136class TJSONTree::Node::Impl {
137public:
138 std::string _key;
139 std::string const &key() const { return _key; }
140 virtual nlohmann::json &get() = 0;
141 virtual const nlohmann::json &get() const = 0;
142 class BaseNode;
143 class NodeRef;
144 Impl(const std::string &k) : _key(k) {}
145 virtual ~Impl() = default;
146 static TJSONTree::Node &mkNode(TJSONTree *t, const std::string &k, nlohmann::json &n);
147 static const TJSONTree::Node &mkNode(const TJSONTree *t, const std::string &k, const nlohmann::json &n);
148};
149
150class TJSONTree::Node::Impl::BaseNode : public TJSONTree::Node::Impl {
151 nlohmann::json node;
152
153public:
154 nlohmann::json &get() override { return node; }
155 const nlohmann::json &get() const override { return node; }
156 BaseNode(std::istream &is) : Impl(""), node(parseWrapper(is)) {}
157 BaseNode() : Impl("") {}
158};
159
160class TJSONTree::Node::Impl::NodeRef : public TJSONTree::Node::Impl {
161 nlohmann::json &node;
162
163public:
164 nlohmann::json &get() override { return node; }
165 const nlohmann::json &get() const override { return node; }
166 NodeRef(const std::string &k, nlohmann::json &n) : Impl(k), node(n) {}
167 NodeRef(const NodeRef &other) : Impl(other.key()), node(other.node) {}
168};
169
170TJSONTree::Node &TJSONTree::Node::Impl::mkNode(TJSONTree *t, const std::string &k, nlohmann::json &n)
171{
172 Node::Impl::NodeRef ref(k, n);
173 return t->incache(Node(t, ref));
174}
175
176const TJSONTree::Node &TJSONTree::Node::Impl::mkNode(const TJSONTree *t, const std::string &k, const nlohmann::json &n)
177{
178 // not so nice to use const_cast here, but the non-const version will only live in the cache
179 Node::Impl::NodeRef ref(k, const_cast<nlohmann::json &>(n));
180 return const_cast<TJSONTree *>(t)->incache(Node(const_cast<TJSONTree *>(t), ref));
181}
182
183TJSONTree::Node::Node(TJSONTree *t, std::istream &is) : tree(t), node(std::make_unique<Impl::BaseNode>(is)) {}
184
185TJSONTree::Node::Node(TJSONTree *t) : tree(t), node(std::make_unique<Impl::BaseNode>()) {}
186
187TJSONTree::Node::Node(TJSONTree *t, Impl &other)
188 : tree(t), node(std::make_unique<Impl::NodeRef>(other.key(), other.get()))
189{
190}
191
192TJSONTree::Node::Node(const Node &other) : Node(other.tree, *other.node) {}
193
194TJSONTree::Node::~Node() = default;
195
196// TJSONNode interface
197
198void TJSONTree::Node::writeJSON(std::ostream &os) const
199{
200 os << node->get();
201}
202
203TJSONTree::Node &TJSONTree::Node::operator<<(std::string const &s)
204{
205 node->get() = s;
206 return *this;
207}
208
209TJSONTree::Node &TJSONTree::Node::operator<<(int i)
210{
211 node->get() = i;
212 return *this;
213}
214
215TJSONTree::Node &TJSONTree::Node::operator<<(double d)
216{
217 node->get() = d;
218 return *this;
219}
220
221TJSONTree::Node &TJSONTree::Node::operator<<(bool b)
222{
223 node->get() = b;
224 return *this;
225}
226
227const TJSONTree::Node &TJSONTree::Node::operator>>(std::string &v) const
228{
229 v = node->get().get<std::string>();
230 return *this;
231}
232
233TJSONTree::Node &TJSONTree::Node::operator[](std::string const &k)
234{
235 return Impl::mkNode(tree, k, node->get()[k]);
236}
237
238const TJSONTree::Node &TJSONTree::Node::operator[](std::string const &k) const
239{
240 return Impl::mkNode(tree, k, node->get()[k]);
241}
242
243bool TJSONTree::Node::is_container() const
244{
245 return node->get().is_array() || node->get().is_object();
246}
247
248bool TJSONTree::Node::is_map() const
249{
250 return node->get().is_object();
251}
252
253bool TJSONTree::Node::is_seq() const
254{
255 return node->get().is_array();
256}
257
258bool TJSONTree::Node::is_null() const
259{
260 return node->get().is_null();
261}
262
263bool TJSONTree::Node::is_number() const
264{
265 return node->get().is_number();
266}
267
268// To check whether it's allowed to reset the type of an object. We allow
269// this for nodes that have no type yet, or nodes with an empty string.
270bool isResettingPossible(nlohmann::json const &node)
271{
272
273 if (node.type() == nlohmann::json::value_t::null) {
274 return true;
275 }
276
277 if (node.type() == nlohmann::json::value_t::string) {
278 if (node.get<std::string>().empty()) {
279 return true;
280 }
281 }
282 return false;
283}
284
285TJSONTree::Node &TJSONTree::Node::set_map()
286{
287 if (node->get().type() == nlohmann::json::value_t::object)
288 return *this;
289
290 if (isResettingPossible(node->get())) {
291 node->get() = nlohmann::json::object();
292 } else {
293 throw std::runtime_error("cannot declare \"" + this->key() + "\" to be of map - type, already of type " +
294 node->get().type_name());
295 }
296 return *this;
297}
298
299TJSONTree::Node &TJSONTree::Node::set_seq()
300{
301 if (node->get().type() == nlohmann::json::value_t::array)
302 return *this;
303
304 if (isResettingPossible(node->get())) {
305 node->get() = nlohmann::json::array();
306 } else {
307 throw std::runtime_error("cannot declare \"" + this->key() + "\" to be of seq - type, already of type " +
308 node->get().type_name());
309 }
310 return *this;
311}
312
313TJSONTree::Node &TJSONTree::Node::set_null()
314{
315 node->get() = nullptr;
316 return *this;
317}
318
319void TJSONTree::Node::clear()
320{
321 node->get().clear();
322}
323
324std::string TJSONTree::Node::key() const
325{
326 return node->key();
327}
328
329std::string TJSONTree::Node::val() const
330{
331 switch (node->get().type()) {
332 case nlohmann::json::value_t::string: return node->get().get<std::string>();
333 case nlohmann::json::value_t::boolean: return node->get().get<bool>() ? "true" : "false";
334 case nlohmann::json::value_t::number_integer: return std::to_string(node->get().get<int>());
335 case nlohmann::json::value_t::number_unsigned: return std::to_string(node->get().get<unsigned int>());
336 case nlohmann::json::value_t::number_float: {
337 std::stringstream ss;
338 ss << node->get().get<double>();
339 return ss.str();
340 }
341 default:
342 throw std::runtime_error("node \"" + node->key() + "\": implicit string conversion for type " +
343 node->get().type_name() + " not supported!");
344 }
345}
346
347int TJSONTree::Node::val_int() const
348{
349 return node->get().get<int>();
350}
351double TJSONTree::Node::val_double() const
352{
353 return node->get().get<double>();
354}
355bool TJSONTree::Node::val_bool() const
356{
357 auto const &nd = node->get();
358
359 // Attempting to convert zeroes and ones to bools.
360 if (nd.type() == nlohmann::json::value_t::number_unsigned) {
361 auto val = nd.get<unsigned int>();
362 if (val == 0)
363 return false;
364 if (val == 1)
365 return true;
366 }
367
368 return nd.get<bool>();
369}
370
371bool TJSONTree::Node::has_key() const
372{
373 return !node->key().empty();
374}
375
376bool TJSONTree::Node::has_val() const
377{
378 return node->get().is_primitive();
379}
380
381bool TJSONTree::Node::has_child(std::string const &c) const
382{
383 return node->get().find(c) != node->get().end();
384}
385
386TJSONTree::Node &TJSONTree::Node::append_child()
387{
388 node->get().push_back("");
389 return Impl::mkNode(tree, "", node->get().back());
390}
391
392size_t TJSONTree::Node::num_children() const
393{
394 return node->get().size();
395}
396
397TJSONTree::Node &TJSONTree::Node::child(size_t pos)
398{
399 return Impl::mkNode(tree, "", node->get().at(pos));
400}
401
402const TJSONTree::Node &TJSONTree::Node::child(size_t pos) const
403{
404 return Impl::mkNode(tree, "", node->get().at(pos));
405}
406
409
410template <class Nd, class NdType, class json_it>
411class TJSONTree::Node::ChildItImpl final : public RooFit::Detail::JSONNode::child_iterator_t<Nd>::Impl {
412public:
413 enum class POS {
414 BEGIN,
415 END
416 };
417 ChildItImpl(NdType &n, POS p)
418 : node(n), iter(p == POS::BEGIN ? n.get_node().get().begin() : n.get_node().get().end()) {};
419 ChildItImpl(NdType &n, json_it it) : node(n), iter(it) {}
420 ChildItImpl(const ChildItImpl &other) : node(other.node), iter(other.iter) {}
422 std::unique_ptr<typename child_iterator::Impl> clone() const override
423 {
424 return std::make_unique<ChildItImpl>(node, iter);
425 }
426 void forward() override { ++iter; }
427 void backward() override { --iter; }
428 Nd &current() override
429 {
430 if (node.is_seq()) {
431 return TJSONTree::Node::Impl::mkNode(node.get_tree(), "", iter.value());
432 } else {
433 return TJSONTree::Node::Impl::mkNode(node.get_tree(), iter.key(), iter.value());
434 }
435 }
436 bool equal(const typename child_iterator::Impl &other) const override
437 {
438 // We can use static_cast here because we never compare Iterators for
439 // different JSON node types.
440 auto it = static_cast<const ChildItImpl<Nd, NdType, json_it> *>(&other);
441 return it && it->iter == this->iter;
442 }
443
444private:
445 NdType &node;
446 json_it iter;
447};
448
449RooFit::Detail::JSONNode::children_view TJSONTree::Node::children()
450{
451 using childIt = TJSONTree::Node::ChildItImpl<RooFit::Detail::JSONNode, TJSONTree::Node, json_iterator>;
452 return {child_iterator(std::make_unique<childIt>(*this, childIt::POS::BEGIN)),
453 child_iterator(std::make_unique<childIt>(*this, childIt::POS::END))};
454}
455RooFit::Detail::JSONNode::const_children_view TJSONTree::Node::children() const
456{
457 using childConstIt =
458 TJSONTree::Node::ChildItImpl<const RooFit::Detail::JSONNode, const TJSONTree::Node, const_json_iterator>;
459 return {const_child_iterator(std::make_unique<childConstIt>(*this, childConstIt::POS::BEGIN)),
460 const_child_iterator(std::make_unique<childConstIt>(*this, childConstIt::POS::END))};
461}
462
463// Iterator implementation that is agnostic of the JSON parsing backend, used
464// for the default implementation of JSONNode::children().
465template <class Node_t>
466class ChildItImpl final : public RooFit::Detail::JSONNode::child_iterator_t<Node_t>::Impl {
467public:
469 ChildItImpl(Node_t &n, size_t p) : node(n), pos(p) {}
470 ChildItImpl(const ChildItImpl &other) : node(other.node), pos(other.pos) {}
471 std::unique_ptr<typename child_iterator::Impl> clone() const override
472 {
473 return std::make_unique<ChildItImpl>(node, pos);
474 }
475 void forward() override { ++pos; }
476 void backward() override { --pos; }
477 Node_t &current() override { return node.child(pos); }
478 bool equal(const typename child_iterator::Impl &other) const override
479 {
480 auto it = dynamic_cast<const ChildItImpl<Node_t> *>(&other);
481 return it && &(it->node) == &(this->node) && (it->pos) == this->pos;
482 }
483
484private:
485 Node_t &node;
486 size_t pos;
487};
488
489} // namespace
490
491namespace RooFit {
492namespace Detail {
493
494template class JSONNode::child_iterator_t<JSONNode>;
495template class JSONNode::child_iterator_t<const JSONNode>;
496
497double JSONNode::val_double() const
498{
499 double out;
500 std::stringstream ss{val()};
501 ss >> out;
502 return out;
503}
504
505// Default fallback for backends that don't provide native type introspection:
506// a node is considered numeric if its textual value parses completely as a
507// floating-point number. Containers, null and non-numeric scalars (strings,
508// booleans) are rejected.
509bool JSONNode::is_number() const
510{
511 if (is_container() || is_null()) {
512 return false;
513 }
514 const std::string text = val();
515 if (text.empty()) {
516 return false;
517 }
518 try {
519 std::size_t consumed = 0;
520 std::stod(text, &consumed);
521 return consumed == text.size();
522 } catch (...) {
523 return false;
524 }
525}
526
527JSONNode::children_view JSONNode::children()
528{
529 return {child_iterator(std::make_unique<::ChildItImpl<JSONNode>>(*this, 0)),
530 child_iterator(std::make_unique<::ChildItImpl<JSONNode>>(*this, this->num_children()))};
531}
532JSONNode::const_children_view JSONNode::children() const
533{
534 return {const_child_iterator(std::make_unique<::ChildItImpl<const JSONNode>>(*this, 0)),
535 const_child_iterator(std::make_unique<::ChildItImpl<const JSONNode>>(*this, this->num_children()))};
536}
537
538std::ostream &operator<<(std::ostream &os, JSONNode const &s)
539{
540 s.writeJSON(os);
541 return os;
542}
543
544template <typename... Args>
545std::unique_ptr<JSONTree> JSONTree::createImpl(Args &&...args)
546{
547 return std::make_unique<TJSONTree>(std::forward<Args>(args)...);
548}
549
550std::unique_ptr<JSONTree> JSONTree::create()
551{
552 return createImpl();
553}
554
555std::unique_ptr<JSONTree> JSONTree::create(std::istream &is)
556{
557 return createImpl(is);
558}
559
560std::unique_ptr<JSONTree> JSONTree::create(std::string const &str)
561{
562 std::stringstream ss{str};
563 return JSONTree::create(ss);
564}
565
566} // namespace Detail
567} // namespace RooFit
#define BEGIN
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define c(i)
Definition RSha256.hxx:101
TBuffer & operator<<(TBuffer &buf, const Tmpl *obj)
Definition TBuffer.h:397
TBuffer & operator>>(TBuffer &buf, Tmpl *&obj)
Definition TBuffer.h:381
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
winID h TVirtualViewer3D TVirtualGLPainter p
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 child
Option_t Option_t TPoint TPoint const char text
virtual void writeJSON(std::ostream &os) const =0
const Int_t n
Definition legend1.C:16
Double_t ex[n]
Definition legend1.C:17
CoordSystem::Scalar get(DisplacementVector2D< CoordSystem, Tag > const &p)
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:72
void forward(const LAYERDATA &prevLayerData, LAYERDATA &currLayerData)
apply the weights (and functions) in forward direction of the DNN
void backward(LAYERDATA &prevLayerData, LAYERDATA &currLayerData)
backward application of the weights (back-propagation of the error)