Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TBufferJSON.cxx
Go to the documentation of this file.
1//
2// Author: Sergey Linev 4.03.2014
3
4/*************************************************************************
5 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12/**
13\class TBufferJSON
14\ingroup IO
15
16Class for serializing object to and from JavaScript Object Notation (JSON) format.
17It creates such object representation, which can be directly
18used in JavaScript ROOT (JSROOT) for drawing.
19
20TBufferJSON implements TBuffer interface, therefore most of
21ROOT and user classes can be converted into JSON.
22There are certain limitations for classes with custom streamers,
23which should be equipped specially for this purposes (see TCanvas::Streamer()
24as example).
25
26To perform conversion into JSON, one should use TBufferJSON::ToJSON method:
27~~~{.cpp}
28 TH1 *h1 = new TH1I("h1", "title", 100, 0, 10);
29 h1->FillRandom("gaus",10000);
30 TString json = TBufferJSON::ToJSON(h1);
31~~~
32
33To reconstruct object from the JSON string, one should do:
34~~~{.cpp}
35 TH1 *hnew = nullptr;
36 TBufferJSON::FromJSON(hnew, json);
37 if (hnew) hnew->Draw("hist");
38~~~
39JSON does not include stored class version, therefore schema evolution
40(reading of older class versions) is not supported. JSON should not be used as
41persistent storage for object data - only for live applications.
42
43All STL containers by default converted into JSON Array. Vector of integers:
44~~~{.cpp}
45 std::vector<int> vect = {1,4,7};
46 auto json = TBufferJSON::ToJSON(&vect);
47~~~
48Will produce JSON code "[1, 4, 7]".
49
50IMPORTANT: Before using any of `map` classes in I/O, one should create dictionary
51for it with the command like:
52```
53gInterpreter->GenerateDictionary("std::map<int,std::string>", "map;string")
54```
55
56There are special handling for map classes like `map` and `multimap`.
57They will create Array of pair objects with "first" and "second" as data members. Code:
58~~~{.cpp}
59 std::map<int,string> m;
60 m[1] = "number 1";
61 m[2] = "number 2";
62 auto json = TBufferJSON::ToJSON(&m);
63~~~
64Will generate json string:
65~~~{.json}
66[
67 {"$pair" : "pair<int,string>", "first" : 1, "second" : "number 1"},
68 {"$pair" : "pair<int,string>", "first" : 2, "second" : "number 2"}
69]
70~~~
71In special cases map container can be converted into JSON object. For that key parameter
72must be `std::string` and compact parameter should be 5. Like in example:
73~~~{.cpp}
74gInterpreter->GenerateDictionary("std::map<std::string,int>", "map;string")
75
76std::map<std::string,int> data;
77data["name1"] = 11;
78data["name2"] = 22;
79
80auto json = TBufferJSON::ToJSON(&data, TBufferJSON::kMapAsObject);
81~~~
82Will produce JSON output:
83~~~
84{
85 "_typename": "map<string,int>",
86 "name1": 11,
87 "name2": 22
88}
89~~~
90Another possibility to enforce such conversion - add "JSON_object" into comment line of correspondent
91data member like:
92~~~{.cpp}
93class Container {
94 std::map<std::string,int> data; ///< JSON_object
95};
96~~~
97
98*/
99
100#include "TBufferJSON.h"
101
102#include <typeinfo>
103#include <string>
104#include <cstring>
105#include <clocale>
106#include <cmath>
107#include <memory>
108#include <cstdlib>
109#include <fstream>
110
111#include "Compression.h"
112
113#include "TArrayI.h"
114#include "TError.h"
115#include "TBase64.h"
116#include "TROOT.h"
117#include "TList.h"
118#include "TClass.h"
119#include "TClassTable.h"
120#include "TClassEdit.h"
121#include "TDataType.h"
122#include "TRealData.h"
123#include "TDataMember.h"
124#include "TMap.h"
125#include "TRef.h"
126#include "TStreamerInfo.h"
127#include "TStreamerElement.h"
128#include "TMemberStreamer.h"
129#include "TStreamer.h"
130#include "RZip.h"
131#include "TClonesArray.h"
132#include "TVirtualMutex.h"
133#include "TInterpreter.h"
135#include "snprintf.h"
136
137#include <nlohmann/json.hpp>
138
139
140enum { json_TArray = 100, json_TCollection = -130, json_TString = 110, json_stdstring = 120 };
141
142///////////////////////////////////////////////////////////////
143// TArrayIndexProducer is used to correctly create
144/// JSON array separators for multi-dimensional JSON arrays
145/// It fully reproduces array dimensions as in original ROOT classes
146/// Contrary to binary I/O, which always writes flat arrays
147
149protected:
152 const char *fSepar{nullptr};
157
158public:
160 {
161 Bool_t usearrayindx = elem && (elem->GetArrayDim() > 0);
162 Bool_t isloop = elem && ((elem->GetType() == TStreamerInfo::kStreamLoop) ||
164 Bool_t usearraylen = (arraylen > (isloop ? 0 : 1));
165
166 if (usearrayindx && (arraylen > 0)) {
167 if (isloop) {
170 } else if (arraylen != elem->GetArrayLength()) {
171 ::Error("TArrayIndexProducer", "Problem with JSON coding of element %s type %d", elem->GetName(),
172 elem->GetType());
173 }
174 }
175
176 if (usearrayindx) {
177 fTotalLen = elem->GetArrayLength();
178 fMaxIndex.Set(elem->GetArrayDim());
179 for (int dim = 0; dim < elem->GetArrayDim(); dim++)
180 fMaxIndex[dim] = elem->GetMaxIndex(dim);
181 fIsArray = fTotalLen > 1;
182 } else if (usearraylen) {
184 fMaxIndex.Set(1);
185 fMaxIndex[0] = arraylen;
186 fIsArray = kTRUE;
187 }
188
189 if (fMaxIndex.GetSize() > 0) {
191 fIndicies.Reset(0);
192 }
193 }
194
196 {
197 Int_t ndim = member->GetArrayDim();
198 if (extradim > 0)
199 ndim++;
200
201 if (ndim > 0) {
202 fIndicies.Set(ndim);
203 fIndicies.Reset(0);
204 fMaxIndex.Set(ndim);
205 fTotalLen = 1;
206 for (int dim = 0; dim < member->GetArrayDim(); dim++) {
207 fMaxIndex[dim] = member->GetMaxIndex(dim);
208 fTotalLen *= member->GetMaxIndex(dim);
209 }
210
211 if (extradim > 0) {
212 fMaxIndex[ndim - 1] = extradim;
214 }
215 }
216 fIsArray = fTotalLen > 1;
217 }
218
219 /// returns number of array dimensions
220 Int_t NumDimensions() const { return fIndicies.GetSize(); }
221
222 /// return array with current index
224
225 /// returns total number of elements in array
226 Int_t TotalLength() const { return fTotalLen; }
227
229 {
230 // reduce one dimension of the array
231 // return size of reduced dimension
232 if (fMaxIndex.GetSize() == 0)
233 return 0;
234 Int_t ndim = fMaxIndex.GetSize() - 1;
235 Int_t len = fMaxIndex[ndim];
236 fMaxIndex.Set(ndim);
237 fIndicies.Set(ndim);
239 fIsArray = fTotalLen > 1;
240 return len;
241 }
242
243 Bool_t IsArray() const { return fIsArray; }
244
246 {
247 // return true when iteration over all arrays indexes are done
248 return !IsArray() || (fCnt >= fTotalLen);
249 }
250
251 const char *GetBegin()
252 {
253 ++fCnt;
254 // return starting separator
255 fRes.Clear();
256 for (Int_t n = 0; n < fIndicies.GetSize(); ++n)
257 fRes.Append("[");
258 return fRes.Data();
259 }
260
261 const char *GetEnd()
262 {
263 // return ending separator
264 fRes.Clear();
265 for (Int_t n = 0; n < fIndicies.GetSize(); ++n)
266 fRes.Append("]");
267 return fRes.Data();
268 }
269
270 /// increment indexes and returns intermediate or last separator
271 const char *NextSeparator()
272 {
273 if (++fCnt >= fTotalLen)
274 return GetEnd();
275
276 Int_t cnt = fIndicies.GetSize() - 1;
277 fIndicies[cnt]++;
278
279 fRes.Clear();
280
281 while ((cnt >= 0) && (cnt < fIndicies.GetSize())) {
282 if (fIndicies[cnt] >= fMaxIndex[cnt]) {
283 fRes.Append("]");
284 fIndicies[cnt--] = 0;
285 if (cnt >= 0)
286 fIndicies[cnt]++;
287 continue;
288 }
289 fRes.Append(fIndicies[cnt] == 0 ? "[" : fSepar);
290 cnt++;
291 }
292 return fRes.Data();
293 }
294
295 nlohmann::json *ExtractNode(nlohmann::json *topnode, bool next = true)
296 {
297 if (!IsArray())
298 return topnode;
299 nlohmann::json *subnode = &((*((nlohmann::json *)topnode))[fIndicies[0]]);
300 for (int k = 1; k < fIndicies.GetSize(); ++k)
301 subnode = &((*subnode)[fIndicies[k]]);
302 if (next)
304 return subnode;
305 }
306};
307
308// TJSONStackObj is used to keep stack of object hierarchy,
309// stored in TBuffer. For instance, data for parent class(es)
310// stored in subnodes, but initial object node will be kept.
311
312class TJSONStackObj : public TObject {
313 struct StlRead {
314 Int_t fIndx{0}; //! index of object in STL container
315 Int_t fMap{0}; //! special iterator over STL map::key members
316 Bool_t fFirst{kTRUE}; //! is first or second element is used in the pair
317 nlohmann::json::iterator fIter; //! iterator for std::map stored as JSON object
318 const char *fTypeTag{nullptr}; //! type tag used for std::map stored as JSON object
319 nlohmann::json fValue; //! temporary value reading std::map as JSON
320 nlohmann::json *GetStlNode(nlohmann::json *prnt)
321 {
322 if (fMap <= 0)
323 return &(prnt->at(fIndx++));
324
325 if (fMap == 1) {
326 nlohmann::json *json = &(prnt->at(fIndx));
327 if (!fFirst) fIndx++;
328 json = &(json->at(fFirst ? "first" : "second"));
329 fFirst = !fFirst;
330 return json;
331 }
332
333 if (fIndx == 0) {
334 // skip _typename if appears
335 if (fTypeTag && (fIter.key().compare(fTypeTag) == 0))
336 ++fIter;
337 fValue = fIter.key();
338 fIndx++;
339 } else {
340 fValue = fIter.value();
341 ++fIter;
342 fIndx = 0;
343 }
344 return &fValue;
345 }
346 };
347
348public:
349 TStreamerInfo *fInfo{nullptr}; //!
350 TStreamerElement *fElem{nullptr}; //! element in streamer info
353 Bool_t fIsPostProcessed{kFALSE}; //! indicate that value is written
354 Bool_t fIsObjStarted{kFALSE}; //! indicate that object writing started, should be closed in postprocess
355 Bool_t fAccObjects{kFALSE}; //! if true, accumulate whole objects in values
356 Bool_t fBase64{kFALSE}; //! enable base64 coding when writing array
357 std::vector<std::string> fValues; //! raw values
358 int fMemberCnt{1}; //! count number of object members, normally _typename is first member
359 int *fMemberPtr{nullptr}; //! pointer on members counter, can be inherit from parent stack objects
360 Int_t fLevel{0}; //! indent level
361 std::unique_ptr<TArrayIndexProducer> fIndx; //! producer of ndim indexes
362 nlohmann::json *fNode{nullptr}; //! JSON node, used for reading
363 std::unique_ptr<StlRead> fStlRead; //! custom structure for stl container reading
364 Version_t fClVersion{0}; //! keep actual class version, workaround for ReadVersion in custom streamer
365
366 TJSONStackObj() = default;
367
368 ~TJSONStackObj() override
369 {
370 if (fIsElemOwner)
371 delete fElem;
372 }
373
375
377
379 {
380 fValues.emplace_back(v.Data());
381 v.Clear();
382 }
383
384 void PushIntValue(Int_t v) { fValues.emplace_back(std::to_string(v)); }
385
386 ////////////////////////////////////////////////////////////////////////
387 /// returns separator for data members
389 {
390 return (!fMemberPtr || ((*fMemberPtr)++ > 0)) ? "," : "";
391 }
392
393 Bool_t IsJsonString() { return fNode && fNode->is_string(); }
394
395 ////////////////////////////////////////////////////////////////////////
396 /// checks if specified JSON node is array (compressed or not compressed)
397 /// returns length of array (or -1 if failure)
398 Int_t IsJsonArray(nlohmann::json *json = nullptr, const char *map_convert_type = nullptr)
399 {
400 if (!json)
401 json = fNode;
402
403 if (map_convert_type) {
404 if (!json->is_object()) return -1;
405 int sz = 0;
406 // count size of object, excluding _typename tag
407 for (auto it = json->begin(); it != json->end(); ++it) {
408 if ((strlen(map_convert_type)==0) || (it.key().compare(map_convert_type) != 0)) sz++;
409 }
410 return sz;
411 }
412
413 // normal uncompressed array
414 if (json->is_array())
415 return json->size();
416
417 // compressed array, full array length in "len" attribute, only ReadFastArray
418 if (json->is_object() && (json->count("$arr") == 1))
419 return json->at("len").get<int>();
420
421 return -1;
422 }
423
425 {
426 auto res = std::stoi(fValues.back());
427 fValues.pop_back();
428 return res;
429 }
430
431 std::unique_ptr<TArrayIndexProducer> MakeReadIndexes()
432 {
433 if (!fElem || (fElem->GetType() <= TStreamerInfo::kOffsetL) ||
434 (fElem->GetType() >= TStreamerInfo::kOffsetL + 20) || (fElem->GetArrayDim() < 2))
435 return nullptr;
436
437 auto indx = std::make_unique<TArrayIndexProducer>(fElem, -1, "");
438
439 // no need for single dimension - it can be handled directly
440 if (!indx->IsArray() || (indx->NumDimensions() < 2))
441 return nullptr;
442
443 return indx;
444 }
445
446 Bool_t IsStl() const { return fStlRead.get() != nullptr; }
447
449 {
450 fStlRead = std::make_unique<StlRead>();
451 fStlRead->fMap = map_convert;
452 if (map_convert == 2) {
453 if (!fNode->is_object()) {
454 ::Error("TJSONStackObj::AssignStl", "when reading %s expecting JSON object", cl->GetName());
455 return kFALSE;
456 }
457 fStlRead->fIter = fNode->begin();
458 fStlRead->fTypeTag = typename_tag && (strlen(typename_tag) > 0) ? typename_tag : nullptr;
459 } else {
460 if (!fNode->is_array() && !(fNode->is_object() && (fNode->count("$arr") == 1))) {
461 ::Error("TJSONStackObj::AssignStl", "when reading %s expecting JSON array", cl->GetName());
462 return kFALSE;
463 }
464 }
465 return kTRUE;
466 }
467
468 nlohmann::json *GetStlNode()
469 {
470 return fStlRead ? fStlRead->GetStlNode(fNode) : fNode;
471 }
472
473 void ClearStl()
474 {
475 fStlRead.reset(nullptr);
476 }
477};
478
479////////////////////////////////////////////////////////////////////////////////
480/// Creates buffer object to serialize data into json.
481
483 : TBufferText(mode), fOutBuffer(), fOutput(nullptr), fValue(), fStack(), fSemicolon(" : "), fArraySepar(", "),
484 fNumericLocale(), fTypeNameTag("_typename")
485{
486 fOutBuffer.Capacity(10000);
487 fValue.Capacity(1000);
489
490 // checks if setlocale(LC_NUMERIC) returns others than "C"
491 // in this case locale will be changed and restored at the end of object conversion
492
493 char *loc = setlocale(LC_NUMERIC, nullptr);
494 if (loc && (strcmp(loc, "C") != 0)) {
496 setlocale(LC_NUMERIC, "C");
497 }
498}
499
500////////////////////////////////////////////////////////////////////////////////
501/// destroy buffer
502
504{
505 while (fStack.size() > 0)
506 PopStack();
507
508 if (fNumericLocale.Length() > 0)
510}
511
512////////////////////////////////////////////////////////////////////////////////
513/// Converts object, inherited from TObject class, to JSON string
514/// Lower digit of compact parameter define formatting rules
515/// - 0 - no any compression, human-readable form
516/// - 1 - exclude spaces in the begin
517/// - 2 - remove newlines
518/// - 3 - exclude spaces as much as possible
519///
520/// Second digit of compact parameter defines algorithm for arrays compression
521/// - 0 - no compression, standard JSON array
522/// - 1 - exclude leading and trailing zeros
523/// - 2 - check values repetition and empty gaps
524///
525/// Third digit of compact parameter defines typeinfo storage:
526/// - TBufferJSON::kSkipTypeInfo (100) - "_typename" will be skipped, not always can be read back
527///
528/// Fourth digit: (1 or 0) defines whether to set kStoreInfNaN (1000) - inf and nan to be stored as string
529///
530/// Maximal compression achieved when compact parameter equal to 23
531/// When member_name specified, converts only this data member
532
534{
535 TClass *clActual = nullptr;
536 void *ptr = (void *)obj;
537
538 if (obj) {
539 clActual = TObject::Class()->GetActualClass(obj);
540 if (!clActual)
542 else if (clActual != TObject::Class())
543 ptr = (void *)((Longptr_t)obj - clActual->GetBaseClassOffset(TObject::Class()));
544 }
545
547}
548
549////////////////////////////////////////////////////////////////////////////////
550/// zip JSON string and convert into base64 string
551/// to be used with JSROOT unzipJSON() function
552/// Main application - embed large JSON code into jupyter notebooks
553
555{
556 std::string buf;
557
558 int srcsize = (int) strlen(json);
559
560 buf.resize(srcsize + 500);
561
562 int tgtsize = buf.length();
563
564 int nout = 0;
565
567
568 return TBase64::Encode(buf.data(), nout);
569}
570
571////////////////////////////////////////////////////////////////////////////////
572/// Set level of space/newline/array compression
573/// Lower digit of compact parameter define formatting rules
574/// - kNoCompress = 0 - no any compression, human-readable form
575/// - kNoIndent = 1 - remove indentation spaces in the begin of each line
576/// - kNoNewLine = 2 - remove also newlines
577/// - kNoSpaces = 3 - exclude all spaces and new lines
578///
579/// Second digit of compact parameter defines algorithm for arrays compression
580/// - 0 - no compression, standard JSON array
581/// - kZeroSuppression = 10 - exclude leading and trailing zeros
582/// - kSameSuppression = 20 - check values repetition and empty gaps
583///
584/// Third digit defines usage of typeinfo
585/// - kSkipTypeInfo = 100 - "_typename" field will be skipped, reading by ROOT or JSROOT may be impossible
586///
587/// Fourth digit (1 or 0) defines whether to set kStoreInfNaN
588
590{
591 if (level < 0)
592 level = 0;
593 fCompact = level % 10;
594 if (fCompact >= kMapAsObject) {
597 }
598 fSemicolon = (fCompact >= kNoSpaces) ? ":" : " : ";
599 fArraySepar = (fCompact >= kNoSpaces) ? "," : ", ";
600 fArrayCompact = ((level / 10) % 10) * 10;
601 if ((((level / 100) % 10) * 100) == kSkipTypeInfo)
603 else if (fTypeNameTag.Length() == 0)
604 fTypeNameTag = "_typename";
605 fStoreInfNaN = ((((level / 1000) % 10) * 1000) == kStoreInfNaN);
606}
607
608////////////////////////////////////////////////////////////////////////////////
609/// Configures _typename tag in JSON structures
610/// By default "_typename" field in JSON structures used to store class information
611/// One can specify alternative tag like "$typename" or "xy", but such JSON can not be correctly used in JSROOT
612/// If empty string is provided, class information will not be stored
613
614void TBufferJSON::SetTypenameTag(const char *tag)
615{
616 if (!tag)
618 else
619 fTypeNameTag = tag;
620}
621
622////////////////////////////////////////////////////////////////////////////////
623/// Configures _typeversion tag in JSON
624/// One can specify name of the JSON tag like "_typeversion" or "$tv" which will be used to store class version
625/// Such tag can be used to correctly recover objects from JSON
626/// If empty string is provided (default), class version will not be stored
627
629{
630 if (!tag)
632 else
633 fTypeVersionTag = tag;
634}
635
636////////////////////////////////////////////////////////////////////////////////
637/// Specify class which typename will not be stored in JSON
638/// Several classes can be configured
639/// To exclude typeinfo for all classes, call TBufferJSON::SetTypenameTag("")
640
642{
643 if (cl && (std::find(fSkipClasses.begin(), fSkipClasses.end(), cl) == fSkipClasses.end()))
644 fSkipClasses.emplace_back(cl);
645}
646
647////////////////////////////////////////////////////////////////////////////////
648/// Returns true if class info will be skipped from JSON
649
651{
652 return cl && (std::find(fSkipClasses.begin(), fSkipClasses.end(), cl) != fSkipClasses.end());
653}
654
655////////////////////////////////////////////////////////////////////////////////
656/// Converts any type of object to JSON string
657/// One should provide pointer on object and its class name
658/// Lower digit of compact parameter define formatting rules
659/// - TBufferJSON::kNoCompress (0) - no any compression, human-readable form
660/// - TBufferJSON::kNoIndent (1) - exclude spaces in the begin
661/// - TBufferJSON::kNoNewLine (2) - no indent and no newlines
662/// - TBufferJSON::kNoSpaces (3) - exclude spaces as much as possible
663/// Second digit of compact parameter defines algorithm for arrays compression
664/// - 0 - no compression, standard JSON array
665/// - TBufferJSON::kZeroSuppression (10) - exclude leading and trailing zeros
666/// - TBufferJSON::kSameSuppression (20) - check values repetition and empty gaps
667/// - TBufferJSON::kBase64 (30) - arrays will be coded with base64 coding
668/// Third digit of compact parameter defines typeinfo storage:
669/// - TBufferJSON::kSkipTypeInfo (100) - "_typename" will be skipped, not always can be read back
670/// Fourth digit: (1 or 0) defines whether to set kStoreInfNaN (1000) - inf and nan to be stored as string
671/// Maximal none-destructive compression can be achieved when
672/// compact parameter equal to TBufferJSON::kNoSpaces + TBufferJSON::kSameSuppression
673/// When member_name specified, converts only this data member
674
675TString TBufferJSON::ConvertToJSON(const void *obj, const TClass *cl, Int_t compact, const char *member_name)
676{
677 if (!cl) {
678 ::Error("TBufferJSON::ConvertToJSON", "Unknown class (probably missing dictionary).");
679 return TString();
680 }
681 TClass *clActual = obj ? cl->GetActualClass(obj) : nullptr;
682 const void *actualStart = obj;
683 if (clActual && (clActual != cl)) {
684 actualStart = (char *)obj - clActual->GetBaseClassOffset(cl);
685 } else {
686 // We could not determine the real type of this object,
687 // let's assume it is the one given by the caller.
688 clActual = const_cast<TClass *>(cl);
689 }
690
691 if (member_name && actualStart) {
692 TRealData *rdata = clActual->GetRealData(member_name);
693 TDataMember *member = rdata ? rdata->GetDataMember() : nullptr;
694 if (!member) {
695 TIter iter(clActual->GetListOfRealData());
696 while ((rdata = dynamic_cast<TRealData *>(iter())) != nullptr) {
697 member = rdata->GetDataMember();
698 if (member && strcmp(member->GetName(), member_name) == 0)
699 break;
700 }
701 }
702 if (!member)
703 return TString();
704
705 Int_t arraylen = -1;
706 if (member->GetArrayIndex() != 0) {
707 TRealData *idata = clActual->GetRealData(member->GetArrayIndex());
708 TDataMember *imember = idata ? idata->GetDataMember() : nullptr;
709 if (imember && (strcmp(imember->GetTrueTypeName(), "int") == 0)) {
710 arraylen = *((int *)((char *)actualStart + idata->GetThisOffset()));
711 }
712 }
713
714 void *ptr = (char *)actualStart + rdata->GetThisOffset();
715 if (member->IsaPointer())
716 ptr = *((char **)ptr);
717
719 }
720
721 TBufferJSON buf;
722
723 buf.SetCompact(compact);
724
725 return buf.StoreObject(actualStart, clActual);
726}
727
728////////////////////////////////////////////////////////////////////////////////
729/// Store provided object as JSON structure
730/// Allows to configure different TBufferJSON properties before converting object into JSON
731/// Actual object class must be specified here
732/// Method can be safely called once - after that TBufferJSON instance must be destroyed
733/// Code should look like:
734///
735/// auto obj = new UserClass();
736/// TBufferJSON buf;
737/// buf.SetCompact(TBufferJSON::kNoSpaces); // change any other settings in TBufferJSON
738/// auto json = buf.StoreObject(obj, TClass::GetClass<UserClass>());
739///
740
741TString TBufferJSON::StoreObject(const void *obj, const TClass *cl)
742{
743 if (IsWriting()) {
744
745 InitMap();
746
747 PushStack(); // dummy stack entry to avoid extra checks in the beginning
748
749 JsonWriteObject(obj, cl);
750
751 PopStack();
752 } else {
753 Error("StoreObject", "Can not store object into TBuffer for reading");
754 }
755
756 return fOutBuffer.Length() ? fOutBuffer : fValue;
757}
758
759////////////////////////////////////////////////////////////////////////////////
760/// Converts selected data member into json
761/// \param ptr specifies address in memory, where data member is located.
762/// \note if data member described by `member` is pointer, `ptr` should be the
763/// value of the pointer, not the address of the pointer.
764/// \param compact defines compactness of produced JSON. See
765/// TBufferJSON::SetCompact for more details
766/// \param arraylen (when specified) is array length for this data member, //[fN] case
767
769{
770 if (!ptr || !member)
771 return TString("null");
772
773 Bool_t stlstring = !strcmp(member->GetTrueTypeName(), "string");
774
775 Int_t isstl = member->IsSTLContainer();
776
777 TClass *mcl = member->IsBasic() ? nullptr : gROOT->GetClass(member->GetTypeName());
778
779 if (mcl && (mcl != TString::Class()) && !stlstring && !isstl && (mcl->GetBaseClassOffset(TArray::Class()) != 0) &&
780 (arraylen <= 0) && (member->GetArrayDim() == 0))
782
783 TBufferJSON buf;
784
785 buf.SetCompact(compact);
786
787 return buf.JsonWriteMember(ptr, member, mcl, arraylen);
788}
789
790////////////////////////////////////////////////////////////////////////////////
791/// Convert object into JSON and store in text file
792/// Returns size of the produce file
793/// Used in TObject::SaveAs()
794
795Int_t TBufferJSON::ExportToFile(const char *filename, const TObject *obj, const char *option)
796{
797 if (!obj || !filename || (*filename == 0))
798 return 0;
799
800 Int_t compact = strstr(filename, ".json.gz") ? 3 : 0;
801 if (option && (*option >= '0') && (*option <= '3'))
803
805
806 std::ofstream ofs(filename);
807
808 if (strstr(filename, ".json.gz")) {
809 const char *objbuf = json.Data();
810 Long_t objlen = json.Length();
811
812 unsigned long objcrc = R__crc32(0, NULL, 0);
813 objcrc = R__crc32(objcrc, (const unsigned char *)objbuf, objlen);
814
815 // 10 bytes (ZIP header), compressed data, 8 bytes (CRC and original length)
816 Int_t buflen = 10 + objlen + 8;
817 if (buflen < 512)
818 buflen = 512;
819
820 char *buffer = (char *)malloc(buflen);
821 if (!buffer)
822 return 0; // failure
823
824 char *bufcur = buffer;
825
826 *bufcur++ = 0x1f; // first byte of ZIP identifier
827 *bufcur++ = 0x8b; // second byte of ZIP identifier
828 *bufcur++ = 0x08; // compression method
829 *bufcur++ = 0x00; // FLAG - empty, no any file names
830 *bufcur++ = 0; // empty timestamp
831 *bufcur++ = 0; //
832 *bufcur++ = 0; //
833 *bufcur++ = 0; //
834 *bufcur++ = 0; // XFL (eXtra FLags)
835 *bufcur++ = 3; // OS 3 means Unix
836 // strcpy(bufcur, "item.json");
837 // bufcur += strlen("item.json")+1;
838
839 char dummy[8];
840 memcpy(dummy, bufcur - 6, 6);
841
842 // R__memcompress fills first 6 bytes with own header, therefore just overwrite them
843 unsigned long ziplen = R__memcompress(bufcur - 6, objlen + 6, (char *)objbuf, objlen);
844
845 memcpy(bufcur - 6, dummy, 6);
846
847 bufcur += (ziplen - 6); // jump over compressed data (6 byte is extra ROOT header)
848
849 *bufcur++ = objcrc & 0xff; // CRC32
850 *bufcur++ = (objcrc >> 8) & 0xff;
851 *bufcur++ = (objcrc >> 16) & 0xff;
852 *bufcur++ = (objcrc >> 24) & 0xff;
853
854 *bufcur++ = objlen & 0xff; // original data length
855 *bufcur++ = (objlen >> 8) & 0xff; // original data length
856 *bufcur++ = (objlen >> 16) & 0xff; // original data length
857 *bufcur++ = (objlen >> 24) & 0xff; // original data length
858
859 ofs.write(buffer, bufcur - buffer);
860
861 free(buffer);
862 } else {
863 ofs << json.Data();
864 }
865
866 ofs.close();
867
868 return json.Length();
869}
870
871////////////////////////////////////////////////////////////////////////////////
872/// Convert object into JSON and store in text file
873/// Returns size of the produce file
874
875Int_t TBufferJSON::ExportToFile(const char *filename, const void *obj, const TClass *cl, const char *option)
876{
877 if (!obj || !cl || !filename || (*filename == 0))
878 return 0;
879
880 Int_t compact = strstr(filename, ".json.gz") ? 3 : 0;
881 if (option && (*option >= '0') && (*option <= '3'))
883
885
886 std::ofstream ofs(filename);
887
888 if (strstr(filename, ".json.gz")) {
889 const char *objbuf = json.Data();
890 Long_t objlen = json.Length();
891
892 unsigned long objcrc = R__crc32(0, NULL, 0);
893 objcrc = R__crc32(objcrc, (const unsigned char *)objbuf, objlen);
894
895 // 10 bytes (ZIP header), compressed data, 8 bytes (CRC and original length)
896 Int_t buflen = 10 + objlen + 8;
897 if (buflen < 512)
898 buflen = 512;
899
900 char *buffer = (char *)malloc(buflen);
901 if (!buffer)
902 return 0; // failure
903
904 char *bufcur = buffer;
905
906 *bufcur++ = 0x1f; // first byte of ZIP identifier
907 *bufcur++ = 0x8b; // second byte of ZIP identifier
908 *bufcur++ = 0x08; // compression method
909 *bufcur++ = 0x00; // FLAG - empty, no any file names
910 *bufcur++ = 0; // empty timestamp
911 *bufcur++ = 0; //
912 *bufcur++ = 0; //
913 *bufcur++ = 0; //
914 *bufcur++ = 0; // XFL (eXtra FLags)
915 *bufcur++ = 3; // OS 3 means Unix
916 // strcpy(bufcur, "item.json");
917 // bufcur += strlen("item.json")+1;
918
919 char dummy[8];
920 memcpy(dummy, bufcur - 6, 6);
921
922 // R__memcompress fills first 6 bytes with own header, therefore just overwrite them
923 unsigned long ziplen = R__memcompress(bufcur - 6, objlen + 6, (char *)objbuf, objlen);
924
925 memcpy(bufcur - 6, dummy, 6);
926
927 bufcur += (ziplen - 6); // jump over compressed data (6 byte is extra ROOT header)
928
929 *bufcur++ = objcrc & 0xff; // CRC32
930 *bufcur++ = (objcrc >> 8) & 0xff;
931 *bufcur++ = (objcrc >> 16) & 0xff;
932 *bufcur++ = (objcrc >> 24) & 0xff;
933
934 *bufcur++ = objlen & 0xff; // original data length
935 *bufcur++ = (objlen >> 8) & 0xff; // original data length
936 *bufcur++ = (objlen >> 16) & 0xff; // original data length
937 *bufcur++ = (objlen >> 24) & 0xff; // original data length
938
939 ofs.write(buffer, bufcur - buffer);
940
941 free(buffer);
942 } else {
943 ofs << json.Data();
944 }
945
946 ofs.close();
947
948 return json.Length();
949}
950
951////////////////////////////////////////////////////////////////////////////////
952/// Read TObject-based class from JSON, produced by ConvertToJSON() method.
953/// If object does not inherit from TObject class, return 0.
954
956{
957 TClass *cl = nullptr;
958 void *obj = ConvertFromJSONAny(str, &cl);
959
960 if (!cl || !obj)
961 return nullptr;
962
964
965 if (delta < 0) {
966 cl->Destructor(obj);
967 return nullptr;
968 }
969
970 return (TObject *)(((char *)obj) + delta);
971}
972
973////////////////////////////////////////////////////////////////////////////////
974/// Read object from JSON
975/// In class pointer (if specified) read class is returned
976/// One must specify expected object class, if it is TArray or STL container
977
978void *TBufferJSON::ConvertFromJSONAny(const char *str, TClass **cl)
979{
981
982 return buf.RestoreObject(str, cl);
983}
984
985////////////////////////////////////////////////////////////////////////////////
986/// Read object from JSON
987/// In class pointer (if specified) read class is returned
988/// One must specify expected object class, if it is TArray or STL container
989
991{
992 if (!IsReading())
993 return nullptr;
994
995 nlohmann::json docu = nlohmann::json::parse(json_str);
996
997 if (docu.is_null() || (!docu.is_object() && !docu.is_array()))
998 return nullptr;
999
1000 TClass *objClass = nullptr;
1001
1002 if (cl) {
1003 objClass = *cl; // this is class which suppose to created when reading JSON
1004 *cl = nullptr;
1005 }
1006
1007 InitMap();
1008
1009 PushStack(0, &docu);
1010
1011 void *obj = JsonReadObject(nullptr, objClass, cl);
1012
1013 PopStack();
1014
1015 return obj;
1016}
1017
1018////////////////////////////////////////////////////////////////////////////////
1019/// Read objects from JSON, one can reuse existing object
1020
1022{
1023 if (!expectedClass)
1024 return nullptr;
1025
1026 TClass *resClass = const_cast<TClass *>(expectedClass);
1027
1028 void *res = ConvertFromJSONAny(str, &resClass);
1029
1030 if (!res || !resClass)
1031 return nullptr;
1032
1033 if (resClass == expectedClass)
1034 return res;
1035
1036 Int_t offset = resClass->GetBaseClassOffset(expectedClass);
1037 if (offset < 0) {
1038 ::Error("TBufferJSON::ConvertFromJSONChecked", "expected class %s is not base for read class %s",
1039 expectedClass->GetName(), resClass->GetName());
1040 resClass->Destructor(res);
1041 return nullptr;
1042 }
1043
1044 return (char *)res - offset;
1045}
1046
1047////////////////////////////////////////////////////////////////////////////////
1048/// Convert single data member to JSON structures
1049/// Note; if data member described by 'member'is pointer, `ptr` should be the
1050/// value of the pointer, not the address of the pointer.
1051/// Returns string with converted member
1052
1054{
1055 if (!member)
1056 return "null";
1057
1058 if (gDebug > 2)
1059 Info("JsonWriteMember", "Write member %s type %s ndim %d", member->GetName(), member->GetTrueTypeName(),
1060 member->GetArrayDim());
1061
1062 Int_t tid = member->GetDataType() ? member->GetDataType()->GetType() : kNoType_t;
1063 if (strcmp(member->GetTrueTypeName(), "const char*") == 0)
1064 tid = kCharStar;
1065 else if (!member->IsBasic() || (tid == kOther_t) || (tid == kVoid_t))
1066 tid = kNoType_t;
1067
1068 if (!ptr)
1069 return (tid == kCharStar) ? "\"\"" : "null";
1070
1071 PushStack(0);
1072 fValue.Clear();
1073
1074 if (tid != kNoType_t) {
1075
1077
1078 Int_t shift = 1;
1079
1080 if (indx.IsArray() && (tid == kChar_t))
1081 shift = indx.ReduceDimension();
1082
1083 auto unitSize = member->GetUnitSize();
1084 char *ppp = (char *)ptr;
1085 if (member->IsaPointer()) {
1086 // UnitSize was the sizeof(void*)
1087 assert(member->GetDataType());
1088 unitSize = member->GetDataType()->Size();
1089 }
1090
1091 if (indx.IsArray())
1092 fOutBuffer.Append(indx.GetBegin());
1093
1094 do {
1095 fValue.Clear();
1096
1097 switch (tid) {
1098 case kChar_t:
1099 if (shift > 1)
1100 JsonWriteConstChar((Char_t *)ppp, shift);
1101 else
1102 JsonWriteBasic(*((Char_t *)ppp));
1103 break;
1104 case kShort_t: JsonWriteBasic(*((Short_t *)ppp)); break;
1105 case kInt_t: JsonWriteBasic(*((Int_t *)ppp)); break;
1106 case kLong_t: JsonWriteBasic(*((Long_t *)ppp)); break;
1107 case kFloat_t: JsonWriteBasic(*((Float_t *)ppp)); break;
1108 case kCounter: JsonWriteBasic(*((Int_t *)ppp)); break;
1109 case kCharStar: JsonWriteConstChar((Char_t *)ppp); break;
1110 case kDouble_t: JsonWriteBasic(*((Double_t *)ppp)); break;
1111 case kDouble32_t: JsonWriteBasic(*((Double_t *)ppp)); break;
1112 case kchar: JsonWriteBasic(*((char *)ppp)); break;
1113 case kUChar_t: JsonWriteBasic(*((UChar_t *)ppp)); break;
1114 case kUShort_t: JsonWriteBasic(*((UShort_t *)ppp)); break;
1115 case kUInt_t: JsonWriteBasic(*((UInt_t *)ppp)); break;
1116 case kULong_t: JsonWriteBasic(*((ULong_t *)ppp)); break;
1117 case kBits: JsonWriteBasic(*((UInt_t *)ppp)); break;
1118 case kLong64_t: JsonWriteBasic(*((Long64_t *)ppp)); break;
1119 case kULong64_t: JsonWriteBasic(*((ULong64_t *)ppp)); break;
1120 case kBool_t: JsonWriteBasic(*((Bool_t *)ppp)); break;
1121 case kFloat16_t: JsonWriteBasic(*((Float_t *)ppp)); break;
1122 case kOther_t:
1123 case kVoid_t: break;
1124 }
1125
1127 if (indx.IsArray())
1128 fOutBuffer.Append(indx.NextSeparator());
1129
1130 ppp += shift * unitSize;
1131
1132 } while (!indx.IsDone());
1133
1135
1136 } else if (memberClass == TString::Class()) {
1137 TString *str = (TString *)ptr;
1138 JsonWriteConstChar(str ? str->Data() : nullptr);
1139 } else if ((member->IsSTLContainer() == ROOT::kSTLvector) || (member->IsSTLContainer() == ROOT::kSTLlist) ||
1140 (member->IsSTLContainer() == ROOT::kSTLforwardlist)) {
1141
1142 if (memberClass)
1143 memberClass->Streamer((void *)ptr, *this);
1144 else
1145 fValue = "[]";
1146
1147 if (fValue == "0")
1148 fValue = "[]";
1149
1150 } else if (memberClass && memberClass->GetBaseClassOffset(TArray::Class()) == 0) {
1151 TArray *arr = (TArray *)ptr;
1152 if (arr && (arr->GetSize() > 0)) {
1153 arr->Streamer(*this);
1154 // WriteFastArray(arr->GetArray(), arr->GetSize());
1155 if (Stack()->fValues.size() > 1) {
1156 Warning("TBufferJSON", "When streaming TArray, more than 1 object in the stack, use second item");
1157 fValue = Stack()->fValues[1].c_str();
1158 }
1159 } else
1160 fValue = "[]";
1161 } else if (memberClass && !strcmp(memberClass->GetName(), "string")) {
1162 // here value contains quotes, stack can be ignored
1163 memberClass->Streamer((void *)ptr, *this);
1164 }
1165 PopStack();
1166
1167 if (fValue.Length())
1168 return fValue;
1169
1170 if (!memberClass || (member->GetArrayDim() > 0) || (arraylen > 0))
1171 return "<not supported>";
1172
1174}
1175
1176////////////////////////////////////////////////////////////////////////////////
1177/// add new level to the structures stack
1178
1180{
1181 auto next = new TJSONStackObj();
1182 next->fLevel = inclevel;
1183 if (IsReading()) {
1184 next->fNode = (nlohmann::json *)readnode;
1185 } else if (fStack.size() > 0) {
1186 auto prev = Stack();
1187 next->fLevel += prev->fLevel;
1188 next->fMemberPtr = prev->fMemberPtr;
1189 }
1190 fStack.emplace_back(next);
1191 return next;
1192}
1193
1194////////////////////////////////////////////////////////////////////////////////
1195/// remove one level from stack
1196
1198{
1199 if (fStack.size() > 0)
1200 fStack.pop_back();
1201
1202 return fStack.size() > 0 ? fStack.back().get() : nullptr;
1203}
1204
1205////////////////////////////////////////////////////////////////////////////////
1206/// Append two string to the output JSON, normally separate by line break
1207
1208void TBufferJSON::AppendOutput(const char *line0, const char *line1)
1209{
1210 if (line0)
1212
1213 if (line1) {
1214 if (fCompact < 2)
1215 fOutput->Append("\n");
1216
1217 if (strlen(line1) > 0) {
1218 if (fCompact < 1) {
1219 if (Stack()->fLevel > 0)
1220 fOutput->Append(' ', Stack()->fLevel);
1221 }
1222 fOutput->Append(line1);
1223 }
1224 }
1225}
1226
1227////////////////////////////////////////////////////////////////////////////////
1228/// Start object element with typeinfo
1229
1231{
1232 auto stack = PushStack(2);
1233
1234 // new object started - assign own member counter
1235 stack->fMemberPtr = &stack->fMemberCnt;
1236
1237 if ((fTypeNameTag.Length() > 0) && !IsSkipClassInfo(obj_class)) {
1238 // stack->fMemberCnt = 1; // default value, comment out here
1239 AppendOutput("{", "\"");
1241 AppendOutput("\"");
1243 AppendOutput("\"");
1244 AppendOutput(obj_class->GetName());
1245 AppendOutput("\"");
1246 if (fTypeVersionTag.Length() > 0) {
1247 AppendOutput(stack->NextMemberSeparator(), "\"");
1249 AppendOutput("\"");
1251 AppendOutput(TString::Format("%d", (int)(info ? info->GetClassVersion() : obj_class->GetClassVersion())));
1252 }
1253 } else {
1254 stack->fMemberCnt = 0; // exclude typename
1255 AppendOutput("{");
1256 }
1257
1258 return stack;
1259}
1260
1261////////////////////////////////////////////////////////////////////////////////
1262/// Start new class member in JSON structures
1263
1265{
1266 const char *elem_name = nullptr;
1268
1269 switch (special_kind) {
1270 case 0:
1271 if (base_class) return;
1272 elem_name = elem->GetName();
1273 if (strcmp(elem_name,"fLineStyle") == 0)
1274 if ((strcmp(elem->GetTypeName(),"TString") == 0) && (strcmp(elem->GetFullName(),"fLineStyle[30]") == 0)) {
1275 auto st1 = fStack.at(fStack.size() - 2).get();
1276 if (st1->IsStreamerInfo() && st1->fInfo && (strcmp(st1->fInfo->GetName(),"TStyle") == 0))
1277 elem_name = "fLineStyles";
1278 }
1279 break;
1280 case TClassEdit::kVector: elem_name = "fVector"; break;
1281 case TClassEdit::kList: elem_name = "fList"; break;
1282 case TClassEdit::kForwardlist: elem_name = "fForwardlist"; break;
1283 case TClassEdit::kDeque: elem_name = "fDeque"; break;
1284 case TClassEdit::kMap: elem_name = "fMap"; break;
1285 case TClassEdit::kMultiMap: elem_name = "fMultiMap"; break;
1286 case TClassEdit::kSet: elem_name = "fSet"; break;
1287 case TClassEdit::kMultiSet: elem_name = "fMultiSet"; break;
1288 case TClassEdit::kUnorderedSet: elem_name = "fUnorderedSet"; break;
1289 case TClassEdit::kUnorderedMultiSet: elem_name = "fUnorderedMultiSet"; break;
1290 case TClassEdit::kUnorderedMap: elem_name = "fUnorderedMap"; break;
1291 case TClassEdit::kUnorderedMultiMap: elem_name = "fUnorderedMultiMap"; break;
1292 case TClassEdit::kBitSet: elem_name = "fBitSet"; break;
1293 case json_TArray: elem_name = "fArray"; break;
1294 case json_TString:
1295 case json_stdstring: elem_name = "fString"; break;
1296 }
1297
1298 if (!elem_name)
1299 return;
1300
1301 if (IsReading()) {
1302 nlohmann::json *json = Stack()->fNode;
1303
1304 if (json->count(elem_name) != 1) {
1305 Error("JsonStartElement", "Missing JSON structure for element %s", elem_name);
1306 } else {
1307 Stack()->fNode = &((*json)[elem_name]);
1308 if (special_kind == json_TArray) {
1309 Int_t len = Stack()->IsJsonArray();
1310 Stack()->PushIntValue(len > 0 ? len : 0);
1311 if (len < 0)
1312 Error("JsonStartElement", "Missing array when reading TArray class for element %s", elem->GetName());
1313 }
1314 if ((gDebug > 1) && base_class)
1315 Info("JsonStartElement", "Reading baseclass %s from element %s", base_class->GetName(), elem_name);
1316 }
1317
1318 } else {
1319 AppendOutput(Stack()->NextMemberSeparator(), "\"");
1321 AppendOutput("\"");
1323 }
1324}
1325
1326////////////////////////////////////////////////////////////////////////////////
1327/// disable post-processing of the code
1332
1333////////////////////////////////////////////////////////////////////////////////
1334/// return non-zero value when class has special handling in JSON
1335/// it is TCollection (-130), TArray (100), TString (110), std::string (120) and STL containers (1..6)
1336
1338{
1339 if (!cl)
1340 return 0;
1341
1342 Bool_t isarray = strncmp("TArray", cl->GetName(), 6) == 0;
1343 if (isarray)
1344 isarray = (const_cast<TClass *>(cl))->GetBaseClassOffset(TArray::Class()) == 0;
1345 if (isarray)
1346 return json_TArray;
1347
1348 // negative value used to indicate that collection stored as object
1349 if ((const_cast<TClass *>(cl))->GetBaseClassOffset(TCollection::Class()) == 0)
1350 return json_TCollection;
1351
1352 // special case for TString - it is saved as string in JSON
1353 if (cl == TString::Class())
1354 return json_TString;
1355
1356 bool isstd = TClassEdit::IsStdClass(cl->GetName());
1358 if (isstd)
1360 if (isstlcont > 0)
1361 return isstlcont;
1362
1363 // also special handling for STL string, which handled similar to TString
1364 if (isstd && !strcmp(cl->GetName(), "string"))
1365 return json_stdstring;
1366
1367 return 0;
1368}
1369
1370////////////////////////////////////////////////////////////////////////////////
1371/// Write object to buffer
1372/// If object was written before, only pointer will be stored
1373/// If check_map==kFALSE, object will be stored in any case and pointer will not be registered in the map
1374
1375void TBufferJSON::JsonWriteObject(const void *obj, const TClass *cl, Bool_t check_map)
1376{
1377 if (!cl)
1378 obj = nullptr;
1379
1380 if (gDebug > 0)
1381 Info("JsonWriteObject", "Object %p class %s check_map %s", obj, cl ? cl->GetName() : "null",
1382 check_map ? "true" : "false");
1383
1385
1387
1388 TJSONStackObj *stack = Stack();
1389
1390 if (stack && stack->fAccObjects && ((fValue.Length() > 0) || (stack->fValues.size() > 0))) {
1391 // accumulate data of super-object in stack
1392
1393 if (fValue.Length() > 0)
1394 stack->PushValue(fValue);
1395
1396 // redirect output to local buffer, use it later as value
1399 } else if ((special_kind <= 0) || (special_kind > json_TArray)) {
1400 // FIXME: later post processing should be active for all special classes, while they all keep output in the value
1404
1405 if ((fMapAsObject && (fStack.size()==1)) || (stack && stack->fElem && strstr(stack->fElem->GetTitle(), "JSON_object")))
1406 map_convert = 2; // mapped into normal object
1407 else
1408 map_convert = 1;
1409
1410 if (!cl->HasDictionary()) {
1411 Error("JsonWriteObject", "Cannot stream class %s without dictionary", cl->GetName());
1412 AppendOutput(map_convert == 1 ? "[]" : "null");
1413 goto post_process;
1414 }
1415 }
1416
1417 if (!obj) {
1418 AppendOutput("null");
1419 goto post_process;
1420 }
1421
1422 if (special_kind <= 0) {
1423 // add element name which should correspond to the object
1424 if (check_map) {
1426 if (refid > 0) {
1427 // old-style refs, coded into string like "$ref12"
1428 // AppendOutput(TString::Format("\"$ref:%u\"", iter->second));
1429 // new-style refs, coded into extra object {"$ref":12}, auto-detected by JSROOT 4.8 and higher
1430 AppendOutput(TString::Format("{\"$ref\":%u}", (unsigned)(refid - 1)));
1431 goto post_process;
1432 }
1433 MapObject(obj, cl, fJsonrCnt + 1); // +1 used
1434 }
1435
1436 fJsonrCnt++; // object counts required in dereferencing part
1437
1438 stack = JsonStartObjectWrite(cl);
1439
1440 } else if (map_convert == 2) {
1441 // special handling of map - it is object, but stored in the fValue
1442
1443 if (check_map) {
1445 if (refid > 0) {
1446 fValue.Form("{\"$ref\":%u}", (unsigned)(refid - 1));
1447 goto post_process;
1448 }
1449 MapObject(obj, cl, fJsonrCnt + 1); // +1 used
1450 }
1451
1452 fJsonrCnt++; // object counts required in dereferencing part
1453 stack = PushStack(0);
1454
1455 } else {
1456
1457 bool base64 = ((special_kind == TClassEdit::kVector) && stack && stack->fElem && strstr(stack->fElem->GetTitle(), "JSON_base64"));
1458
1459 // for array, string and STL collections different handling -
1460 // they not recognized at the end as objects in JSON
1461 stack = PushStack(0);
1462
1463 stack->fBase64 = base64;
1464 }
1465
1466 if (gDebug > 3)
1467 Info("JsonWriteObject", "Starting object %p write for class: %s", obj, cl->GetName());
1468
1470
1472 JsonWriteCollection((TCollection *)obj, cl);
1473 else
1474 (const_cast<TClass *>(cl))->Streamer((void *)obj, *this);
1475
1476 if (gDebug > 3)
1477 Info("JsonWriteObject", "Done object %p write for class: %s", obj, cl->GetName());
1478
1479 if (special_kind == json_TArray) {
1480 if (stack->fValues.size() != 1)
1481 Error("JsonWriteObject", "Problem when writing array");
1482 stack->fValues.clear();
1483 } else if ((special_kind == json_TString) || (special_kind == json_stdstring)) {
1484 if (stack->fValues.size() > 2)
1485 Error("JsonWriteObject", "Problem when writing TString or std::string");
1486 stack->fValues.clear();
1488 fValue.Clear();
1489 } else if ((special_kind > 0) && (special_kind < ROOT::kSTLend)) {
1490 // here make STL container processing
1491
1492 if (map_convert == 2) {
1493 // converting map into object
1494
1495 if (!stack->fValues.empty() && (fValue.Length() > 0))
1496 stack->PushValue(fValue);
1497
1498 const char *separ = (fCompact < 2) ? ", " : ",";
1499 const char *semi = (fCompact < 2) ? ": " : ":";
1500 bool first = true;
1501
1502 fValue = "{";
1503 if ((fTypeNameTag.Length() > 0) && !IsSkipClassInfo(cl)) {
1504 fValue.Append("\"");
1506 fValue.Append("\"");
1508 fValue.Append("\"");
1509 fValue.Append(cl->GetName());
1510 fValue.Append("\"");
1511 first = false;
1512 }
1513 for (Int_t k = 1; k < (int)stack->fValues.size() - 1; k += 2) {
1514 if (!first)
1516 first = false;
1517 fValue.Append(stack->fValues[k].c_str());
1519 fValue.Append(stack->fValues[k + 1].c_str());
1520 }
1521 fValue.Append("}");
1522 stack->fValues.clear();
1523 } else if (stack->fValues.empty()) {
1524 // empty container
1525 if (fValue != "0")
1526 Error("JsonWriteObject", "With empty stack fValue!=0");
1527 fValue = "[]";
1528 } else {
1529
1530 auto size = std::stoi(stack->fValues[0]);
1531
1532 bool trivial_format = false;
1533
1534 if ((stack->fValues.size() == 1) && ((size > 1) || ((fValue.Length() > 1) && (fValue[0]=='[')))) {
1535 // prevent case of vector<vector<value_class>>
1536 const auto proxy = cl->GetCollectionProxy();
1537 TClass *value_class = proxy ? proxy->GetValueClass() : nullptr;
1538 if (value_class && TClassEdit::IsStdClass(value_class->GetName()) && (value_class->GetCollectionType() != ROOT::kNotSTL))
1539 trivial_format = false;
1540 else
1541 trivial_format = true;
1542 }
1543
1544 if (trivial_format) {
1545 // case of simple vector, array already in the value
1546 stack->fValues.clear();
1547 if (fValue.Length() == 0) {
1548 Error("JsonWriteObject", "Empty value when it should contain something");
1549 fValue = "[]";
1550 }
1551
1552 } else {
1553 const char *separ = "[";
1554
1555 if (fValue.Length() > 0)
1556 stack->PushValue(fValue);
1557
1558 if ((size * 2 == (int) stack->fValues.size() - 1) && (map_convert > 0)) {
1559 // special handling for std::map.
1560 // Create entries like { '$pair': 'typename' , 'first' : key, 'second' : value }
1561 TString pairtype = cl->GetName();
1562 if (pairtype.Index("unordered_map<") == 0)
1563 pairtype.Replace(0, 14, "pair<");
1564 else if (pairtype.Index("unordered_multimap<") == 0)
1565 pairtype.Replace(0, 19, "pair<");
1566 else if (pairtype.Index("multimap<") == 0)
1567 pairtype.Replace(0, 9, "pair<");
1568 else if (pairtype.Index("map<") == 0)
1569 pairtype.Replace(0, 4, "pair<");
1570 else
1571 pairtype = "TPair";
1572 if (fTypeNameTag.Length() == 0)
1573 pairtype = "1";
1574 else
1575 pairtype = TString("\"") + pairtype + TString("\"");
1576 for (Int_t k = 1; k < (int) stack->fValues.size() - 1; k += 2) {
1579 // fJsonrCnt++; // do not add entry in the map, can conflict with objects inside values
1580 fValue.Append("{");
1581 fValue.Append("\"$pair\"");
1583 fValue.Append(pairtype.Data());
1585 fValue.Append("\"first\"");
1587 fValue.Append(stack->fValues[k].c_str());
1589 fValue.Append("\"second\"");
1591 fValue.Append(stack->fValues[k + 1].c_str());
1592 fValue.Append("}");
1593 }
1594 } else {
1595 // for most stl containers write just like blob, but skipping first element with size
1596 for (Int_t k = 1; k < (int) stack->fValues.size(); k++) {
1599 fValue.Append(stack->fValues[k].c_str());
1600 }
1601 }
1602
1603 fValue.Append("]");
1604 stack->fValues.clear();
1605 }
1606 }
1607 }
1608
1609 // reuse post-processing code for TObject or TRef
1610 PerformPostProcessing(stack, cl);
1611
1612 if ((special_kind == 0) && (!stack->fValues.empty() || (fValue.Length() > 0))) {
1613 if (gDebug > 0)
1614 Info("JsonWriteObject", "Create blob value for class %s", cl->GetName());
1615
1616 AppendOutput(fArraySepar.Data(), "\"_blob\"");
1618
1619 const char *separ = "[";
1620
1621 for (auto &elem: stack->fValues) {
1624 AppendOutput(elem.c_str());
1625 }
1626
1627 if (fValue.Length() > 0) {
1630 }
1631
1632 AppendOutput("]");
1633
1634 fValue.Clear();
1635 stack->fValues.clear();
1636 }
1637
1638 PopStack();
1639
1640 if ((special_kind <= 0))
1641 AppendOutput(nullptr, "}");
1642
1644
1645 if (fPrevOutput) {
1647 // for STL containers and TArray object in fValue itself
1648 if ((special_kind <= 0) || (special_kind > json_TArray))
1650 else if (fObjectOutput.Length() != 0)
1651 Error("JsonWriteObject", "Non-empty object output for special class %s", cl->GetName());
1652 }
1653}
1654
1655////////////////////////////////////////////////////////////////////////////////
1656/// store content of ROOT collection
1657
1659{
1660 AppendOutput(Stack()->NextMemberSeparator(), "\"name\"");
1662 AppendOutput("\"");
1663 AppendOutput(col->GetName());
1664 AppendOutput("\"");
1665 AppendOutput(Stack()->NextMemberSeparator(), "\"arr\"");
1667
1668 // collection treated as JS Array
1669 AppendOutput("[");
1670
1671 auto map = dynamic_cast<TMap *>(col);
1672 auto lst = dynamic_cast<TList *>(col);
1673
1674 TString sopt;
1675 Bool_t first = kTRUE;
1676
1677 if (lst) {
1678 // handle TList with extra options
1679 sopt.Capacity(500);
1680 sopt = "[";
1681
1682 auto lnk = lst->FirstLink();
1683 while (lnk) {
1684 if (!first) {
1686 sopt.Append(fArraySepar.Data());
1687 }
1688
1689 WriteObjectAny(lnk->GetObject(), TObject::Class());
1690
1691 if (dynamic_cast<TObjOptLink *>(lnk)) {
1692 sopt.Append("\"");
1693 sopt.Append(lnk->GetAddOption());
1694 sopt.Append("\"");
1695 } else
1696 sopt.Append("null");
1697
1698 lnk = lnk->Next();
1699 first = kFALSE;
1700 }
1701 } else if (map) {
1702 // handle TMap with artificial TPair object
1703 TIter iter(col);
1704 while (auto obj = iter()) {
1705 if (!first)
1707
1708 // fJsonrCnt++; // do not account map pair as JSON object
1709 AppendOutput("{", "\"$pair\"");
1711 AppendOutput("\"TPair\"");
1712 AppendOutput(fArraySepar.Data(), "\"first\"");
1714
1716
1717 AppendOutput(fArraySepar.Data(), "\"second\"");
1719 WriteObjectAny(map->GetValue(obj), TObject::Class());
1720 AppendOutput("", "}");
1721 first = kFALSE;
1722 }
1723 } else {
1724 TIter iter(col);
1725 while (auto obj = iter()) {
1726 if (!first)
1728
1730 first = kFALSE;
1731 }
1732 }
1733
1734 AppendOutput("]");
1735
1736 if (lst) {
1737 sopt.Append("]");
1738 AppendOutput(Stack()->NextMemberSeparator(), "\"opt\"");
1740 AppendOutput(sopt.Data());
1741 }
1742
1743 fValue.Clear();
1744}
1745
1746////////////////////////////////////////////////////////////////////////////////
1747/// read content of ROOT collection
1748
1750{
1751 if (!col)
1752 return;
1753
1754 TList *lst = nullptr;
1755 TMap *map = nullptr;
1756 TClonesArray *clones = nullptr;
1757 if (col->InheritsFrom(TList::Class()))
1758 lst = dynamic_cast<TList *>(col);
1759 else if (col->InheritsFrom(TMap::Class()))
1760 map = dynamic_cast<TMap *>(col);
1761 else if (col->InheritsFrom(TClonesArray::Class()))
1762 clones = dynamic_cast<TClonesArray *>(col);
1763
1764 nlohmann::json *json = Stack()->fNode;
1765
1766 std::string name = json->at("name");
1767 col->SetName(name.c_str());
1768
1769 nlohmann::json &arr = json->at("arr");
1770 int size = arr.size();
1771
1772 for (int n = 0; n < size; ++n) {
1773 nlohmann::json *subelem = &arr.at(n);
1774
1775 if (map)
1776 subelem = &subelem->at("first");
1777
1778 PushStack(0, subelem);
1779
1780 TClass *readClass = nullptr, *objClass = nullptr;
1781 void *subobj = nullptr;
1782
1783 if (clones) {
1784 if (n == 0) {
1785 if (!clones->GetClass() || (clones->GetSize() == 0)) {
1786 if (fTypeNameTag.Length() > 0) {
1787 clones->SetClass(subelem->at(fTypeNameTag.Data()).get<std::string>().c_str(), size);
1788 } else {
1789 Error("JsonReadCollection",
1790 "Cannot detect class name for TClonesArray - typename tag not configured");
1791 return;
1792 }
1793 } else if (size > clones->GetSize()) {
1794 Error("JsonReadCollection", "TClonesArray size %d smaller than required %d", clones->GetSize(), size);
1795 return;
1796 }
1797 }
1798 objClass = clones->GetClass();
1799 subobj = clones->ConstructedAt(n);
1800 }
1801
1803
1804 PopStack();
1805
1806 if (clones)
1807 continue;
1808
1809 if (!subobj || !readClass) {
1810 subobj = nullptr;
1811 } else if (readClass->GetBaseClassOffset(TObject::Class()) != 0) {
1812 Error("JsonReadCollection", "Try to add object %s not derived from TObject", readClass->GetName());
1813 subobj = nullptr;
1814 }
1815
1816 TObject *tobj = static_cast<TObject *>(subobj);
1817
1818 if (map) {
1819 PushStack(0, &arr.at(n).at("second"));
1820
1821 readClass = nullptr;
1822 void *subobj2 = JsonReadObject(nullptr, nullptr, &readClass);
1823
1824 PopStack();
1825
1826 if (!subobj2 || !readClass) {
1827 subobj2 = nullptr;
1828 } else if (readClass->GetBaseClassOffset(TObject::Class()) != 0) {
1829 Error("JsonReadCollection", "Try to add object %s not derived from TObject", readClass->GetName());
1830 subobj2 = nullptr;
1831 }
1832
1833 map->Add(tobj, static_cast<TObject *>(subobj2));
1834 } else if (lst) {
1835 auto &elem = json->at("opt").at(n);
1836 if (elem.is_null())
1837 lst->Add(tobj);
1838 else
1839 lst->Add(tobj, elem.get<std::string>().c_str());
1840 } else {
1841 // generic method, all kinds of TCollection should work
1842 col->Add(tobj);
1843 }
1844 }
1845}
1846
1847////////////////////////////////////////////////////////////////////////////////
1848/// Read object from current JSON node
1849
1851{
1852 if (readClass)
1853 *readClass = nullptr;
1854
1855 TJSONStackObj *stack = Stack();
1856
1857 Bool_t process_stl = stack->IsStl();
1858 nlohmann::json *json = stack->GetStlNode();
1859
1860 // check if null pointer
1861 if (json->is_null())
1862 return nullptr;
1863
1865
1866 // Extract pointer
1867 if (json->is_object() && (json->size() == 1) && (json->find("$ref") != json->end())) {
1868 unsigned refid = json->at("$ref").get<unsigned>();
1869
1870 void *ref_obj = nullptr;
1871 TClass *ref_cl = nullptr;
1872
1874
1875 if (!ref_obj || !ref_cl) {
1876 Error("JsonReadObject", "Fail to find object for reference %u", refid);
1877 return nullptr;
1878 }
1879
1880 if (readClass)
1881 *readClass = ref_cl;
1882
1883 if (gDebug > 2)
1884 Info("JsonReadObject", "Extract object reference %u %p cl:%s expects:%s", refid, ref_obj, ref_cl->GetName(),
1885 (objClass ? objClass->GetName() : "---"));
1886
1887 return ref_obj;
1888 }
1889
1890 // special case of strings - they do not create JSON object, but just string
1892 if (!obj)
1893 obj = objClass->New();
1894
1895 if (gDebug > 2)
1896 Info("JsonReadObject", "Read string from %s", json->dump().c_str());
1897
1899 *((std::string *)obj) = json->get<std::string>();
1900 else
1901 *((TString *)obj) = json->get<std::string>().c_str();
1902
1903 if (readClass)
1904 *readClass = const_cast<TClass *>(objClass);
1905
1906 return obj;
1907 }
1908
1909 Bool_t isBase = (stack->fElem && objClass) ? stack->fElem->IsBase() : kFALSE; // base class
1910
1911 if (isBase && (!obj || !objClass)) {
1912 Error("JsonReadObject", "No object when reading base class");
1913 return obj;
1914 }
1915
1916 Int_t map_convert = 0;
1919 map_convert = json->is_object() ? 2 : 1; // check if map was written as array or as object
1920
1921 if (objClass && !objClass->HasDictionary()) {
1922 Error("JsonReadObject", "Cannot stream class %s without dictionary", objClass->GetName());
1923 return obj;
1924 }
1925 }
1926
1927 // from now all operations performed with sub-element,
1928 // stack should be repaired at the end
1929 if (process_stl)
1930 stack = PushStack(0, json);
1931
1932 TClass *jsonClass = nullptr;
1934
1935 if ((special_kind == json_TArray) || ((special_kind > 0) && (special_kind < ROOT::kSTLend))) {
1936
1937 jsonClass = const_cast<TClass *>(objClass);
1938
1939 if (!obj)
1940 obj = jsonClass->New();
1941
1942 Int_t len = stack->IsJsonArray(json, map_convert == 2 ? fTypeNameTag.Data() : nullptr);
1943
1944 stack->PushIntValue(len > 0 ? len : 0);
1945
1946 if (len < 0) // should never happens
1947 Error("JsonReadObject", "Not array when expecting such %s", json->dump().c_str());
1948
1949 if (gDebug > 1)
1950 Info("JsonReadObject", "Reading special kind %d %s ptr %p", special_kind, objClass->GetName(), obj);
1951
1952 } else if (isBase) {
1953 // base class has special handling - no additional level and no extra refid
1954
1955 jsonClass = const_cast<TClass *>(objClass);
1956
1957 if (gDebug > 1)
1958 Info("JsonReadObject", "Reading baseclass %s ptr %p", objClass->GetName(), obj);
1959 } else {
1960
1961 if ((fTypeNameTag.Length() > 0) && (json->count(fTypeNameTag.Data()) > 0)) {
1962 std::string clname = json->at(fTypeNameTag.Data()).get<std::string>();
1964 if (!jsonClass)
1965 Error("JsonReadObject", "Cannot find class %s", clname.c_str());
1966 } else {
1967 // try to use class which is assigned by streamers - better than nothing
1968 jsonClass = const_cast<TClass *>(objClass);
1969 }
1970
1971 if (!jsonClass) {
1972 if (process_stl)
1973 PopStack();
1974 return obj;
1975 }
1976
1977 if ((fTypeVersionTag.Length() > 0) && (json->count(fTypeVersionTag.Data()) > 0))
1978 jsonClassVersion = json->at(fTypeVersionTag.Data()).get<int>();
1979
1980 if (objClass && (jsonClass != objClass)) {
1981 if (obj || (jsonClass->GetBaseClassOffset(objClass) != 0)) {
1982 if (jsonClass->GetBaseClassOffset(objClass) < 0)
1983 Error("JsonReadObject", "Not possible to read %s and casting to %s pointer as the two classes are unrelated",
1984 jsonClass->GetName(), objClass->GetName());
1985 else
1986 Error("JsonReadObject", "Reading %s and casting to %s pointer is currently not supported",
1987 jsonClass->GetName(), objClass->GetName());
1988 if (process_stl)
1989 PopStack();
1990 return obj;
1991 }
1992 }
1993
1994 if (!obj)
1995 obj = jsonClass->New();
1996
1997 if (gDebug > 1)
1998 Info("JsonReadObject", "Reading object of class %s refid %u ptr %p", jsonClass->GetName(), fJsonrCnt, obj);
1999
2000 if (!special_kind)
2002
2003 // add new element to the reading map
2004 MapObject(obj, jsonClass, ++fJsonrCnt);
2005 }
2006
2007 // there are two ways to handle custom streamers
2008 // either prepare data before streamer and tweak basic function which are reading values like UInt32_t
2009 // or try re-implement custom streamer here
2010
2011 if ((jsonClass == TObject::Class()) || (jsonClass == TRef::Class())) {
2012 // for TObject we re-implement custom streamer - it is much easier
2013
2015
2016 } else if (special_kind == json_TCollection) {
2017
2019
2020 } else {
2021
2023
2024 // special handling of STL which coded into arrays
2025 if ((special_kind > 0) && (special_kind < ROOT::kSTLend))
2027
2028 // if provided - use class version from JSON
2029 stack->fClVersion = jsonClassVersion ? jsonClassVersion : jsonClass->GetClassVersion();
2030
2031 if (gDebug > 3)
2032 Info("JsonReadObject", "Calling streamer of class %s", jsonClass->GetName());
2033
2034 if (isBase && (special_kind == 0))
2035 Error("JsonReadObject", "Should not be used for reading of base class %s", jsonClass->GetName());
2036
2037 if (do_read)
2038 jsonClass->Streamer((void *)obj, *this);
2039
2040 stack->fClVersion = 0;
2041
2042 stack->ClearStl(); // reset STL index for itself to prevent looping
2043 }
2044
2045 // return back stack position
2046 if (process_stl)
2047 PopStack();
2048
2049 if (gDebug > 1)
2050 Info("JsonReadObject", "Reading object of class %s done", jsonClass->GetName());
2051
2052 if (readClass)
2054
2055 return obj;
2056}
2057
2058////////////////////////////////////////////////////////////////////////////////
2059/// Read TObject data members from JSON.
2060/// Do not call TObject::Streamer() to avoid special tweaking of TBufferJSON interface
2061
2063{
2064 nlohmann::json *json = node ? (nlohmann::json *)node : Stack()->fNode;
2065
2066 UInt_t uid = json->at("fUniqueID").get<unsigned>();
2067 UInt_t bits = json->at("fBits").get<unsigned>();
2068 // UInt32_t pid = json->at("fPID").get<unsigned>(); // ignore PID for the moment
2069
2070 tobj->SetUniqueID(uid);
2071
2072 static auto tobj_fbits_offset = TObject::Class()->GetDataMemberOffset("fBits");
2073
2074 // there is no method to set all bits directly - do it differently
2075 if (tobj_fbits_offset > 0) {
2076 UInt_t *fbits = (UInt_t *) ((char* ) tobj + tobj_fbits_offset);
2078 }
2079}
2080
2081////////////////////////////////////////////////////////////////////////////////
2082/// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2083/// and indent new level in json structure.
2084/// This call indicates, that TStreamerInfo functions starts streaming
2085/// object data of correspondent class
2086
2088{
2089 if (gDebug > 2)
2090 Info("IncrementLevel", "Class: %s", (info ? info->GetClass()->GetName() : "custom"));
2091
2093}
2094
2095////////////////////////////////////////////////////////////////////////////////
2096/// Prepares buffer to stream data of specified class
2097
2099{
2100 if (sinfo)
2101 cl = sinfo->GetClass();
2102
2103 if (!cl)
2104 return;
2105
2106 if (gDebug > 3)
2107 Info("WorkWithClass", "Class: %s", cl->GetName());
2108
2109 TJSONStackObj *stack = Stack();
2110
2111 if (IsReading()) {
2112 stack = PushStack(0, stack->fNode);
2113 } else if (stack && stack->IsStreamerElement() && !stack->fIsObjStarted &&
2114 ((stack->fElem->GetType() == TStreamerInfo::kObject) ||
2115 (stack->fElem->GetType() == TStreamerInfo::kAny))) {
2116
2117 stack->fIsObjStarted = kTRUE;
2118
2119 fJsonrCnt++; // count object, but do not keep reference
2120
2121 stack = JsonStartObjectWrite(cl, sinfo);
2122 } else {
2123 stack = PushStack(0);
2124 }
2125
2126 stack->fInfo = sinfo;
2127 stack->fIsStreamerInfo = kTRUE;
2128}
2129
2130////////////////////////////////////////////////////////////////////////////////
2131/// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2132/// and decrease level in json structure.
2133
2135{
2136 if (gDebug > 2)
2137 Info("DecrementLevel", "Class: %s", (info ? info->GetClass()->GetName() : "custom"));
2138
2139 TJSONStackObj *stack = Stack();
2140
2141 if (stack->IsStreamerElement()) {
2142
2143 if (IsWriting()) {
2144 if (gDebug > 3)
2145 Info("DecrementLevel", " Perform post-processing elem: %s", stack->fElem->GetName());
2146
2147 PerformPostProcessing(stack);
2148 }
2149
2150 stack = PopStack(); // remove stack of last element
2151 }
2152
2153 if (stack->fInfo != (TStreamerInfo *)info)
2154 Error("DecrementLevel", " Mismatch of streamer info");
2155
2156 PopStack(); // back from data of stack info
2157
2158 if (gDebug > 3)
2159 Info("DecrementLevel", "Class: %s done", (info ? info->GetClass()->GetName() : "custom"));
2160}
2161
2162////////////////////////////////////////////////////////////////////////////////
2163/// Return current streamer info element
2164
2169
2170////////////////////////////////////////////////////////////////////////////////
2171/// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2172/// and add/verify next element of json structure
2173/// This calls allows separate data, correspondent to one class member, from another
2174
2176{
2177 if (gDebug > 3)
2178 Info("SetStreamerElementNumber", "Element name %s", elem->GetName());
2179
2181}
2182
2183////////////////////////////////////////////////////////////////////////////////
2184/// This is call-back from streamer which indicates
2185/// that class member will be streamed
2186/// Name of element used in JSON
2187
2189{
2190 TJSONStackObj *stack = Stack();
2191 if (!stack) {
2192 Error("WorkWithElement", "stack is empty");
2193 return;
2194 }
2195
2196 if (gDebug > 0)
2197 Info("WorkWithElement", " Start element %s type %d typename %s", elem ? elem->GetName() : "---",
2198 elem ? elem->GetType() : -1, elem ? elem->GetTypeName() : "---");
2199
2200 if (stack->IsStreamerElement()) {
2201 // this is post processing
2202
2203 if (IsWriting()) {
2204 if (gDebug > 3)
2205 Info("WorkWithElement", " Perform post-processing elem: %s", stack->fElem->GetName());
2206 PerformPostProcessing(stack);
2207 }
2208
2209 stack = PopStack(); // go level back
2210 }
2211
2212 fValue.Clear();
2213
2214 if (!stack) {
2215 Error("WorkWithElement", "Lost of stack");
2216 return;
2217 }
2218
2219 TStreamerInfo *info = stack->fInfo;
2220 if (!stack->IsStreamerInfo()) {
2221 Error("WorkWithElement", "Problem in Inc/Dec level");
2222 return;
2223 }
2224
2225 Int_t number = info ? info->GetElements()->IndexOf(elem) : -1;
2226
2227 if (!elem) {
2228 Error("WorkWithElement", "streamer info returns elem = nullptr");
2229 return;
2230 }
2231
2232 TClass *base_class = elem->IsBase() ? elem->GetClassPointer() : nullptr;
2233
2234 stack = PushStack(0, stack->fNode);
2235 stack->fElem = elem;
2236 stack->fIsElemOwner = (number < 0);
2237
2239
2240 if (base_class && IsReading())
2241 stack->fClVersion = base_class->GetClassVersion();
2242
2243 if ((elem->GetType() == TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop) && (elem->GetArrayDim() > 0)) {
2244 // array of array, start handling here
2245 stack->fIndx = std::make_unique<TArrayIndexProducer>(elem, -1, fArraySepar.Data());
2246 if (IsWriting())
2247 AppendOutput(stack->fIndx->GetBegin());
2248 }
2249
2250 if (IsReading() && (elem->GetType() > TStreamerInfo::kOffsetP) && (elem->GetType() < TStreamerInfo::kOffsetP + 20)) {
2251 // reading of such array begins with reading of single Char_t value
2252 // it indicates if array should be read or not
2253 stack->PushIntValue(stack->IsJsonString() || (stack->IsJsonArray() > 0) ? 1 : 0);
2254 }
2255}
2256
2257////////////////////////////////////////////////////////////////////////////////
2258/// Should be called in the beginning of custom class streamer.
2259/// Informs buffer data about class which will be streamed now.
2260///
2261/// ClassBegin(), ClassEnd() and ClassMember() should be used in
2262/// custom class streamers to specify which kind of data are
2263/// now streamed. Such information is used to correctly
2264/// convert class data to JSON. Without that functions calls
2265/// classes with custom streamers cannot be used with TBufferJSON
2266
2268{
2269 WorkWithClass(nullptr, cl);
2270}
2271
2272////////////////////////////////////////////////////////////////////////////////
2273/// Should be called at the end of custom streamer
2274/// See TBufferJSON::ClassBegin for more details
2275
2277{
2278 DecrementLevel(0);
2279}
2280
2281////////////////////////////////////////////////////////////////////////////////
2282/// Method indicates name and typename of class member,
2283/// which should be now streamed in custom streamer
2284/// Following combinations are supported:
2285/// 1. name = "ClassName", typeName = 0 or typename==ClassName
2286/// This is a case, when data of parent class "ClassName" should be streamed.
2287/// For instance, if class directly inherited from TObject, custom
2288/// streamer should include following code:
2289/// ~~~{.cpp}
2290/// b.ClassMember("TObject");
2291/// TObject::Streamer(b);
2292/// ~~~
2293/// 2. Basic data type
2294/// ~~~{.cpp}
2295/// b.ClassMember("fInt","Int_t");
2296/// b >> fInt;
2297/// ~~~
2298/// 3. Array of basic data types
2299/// ~~~{.cpp}
2300/// b.ClassMember("fArr","Int_t", 5);
2301/// b.ReadFastArray(fArr, 5);
2302/// ~~~
2303/// 4. Object as data member
2304/// ~~~{.cpp}
2305/// b.ClassMember("fName","TString");
2306/// fName.Streamer(b);
2307/// ~~~
2308/// 5. Pointer on object as data member
2309/// ~~~{.cpp}
2310/// b.ClassMember("fObj","TObject*");
2311/// b.StreamObject(fObj);
2312/// ~~~
2313///
2314/// arrsize1 and arrsize2 arguments (when specified) indicate first and
2315/// second dimension of array. Can be used for array of basic types.
2316/// See ClassBegin() method for more details.
2317
2318void TBufferJSON::ClassMember(const char *name, const char *typeName, Int_t arrsize1, Int_t arrsize2)
2319{
2320 if (!typeName)
2321 typeName = name;
2322
2323 if (!name || (strlen(name) == 0)) {
2324 Error("ClassMember", "Invalid member name");
2325 return;
2326 }
2327
2328 TString tname = typeName;
2329
2330 Int_t typ_id = -1;
2331
2332 if (strcmp(typeName, "raw:data") == 0)
2334
2335 if (typ_id < 0) {
2336 TDataType *dt = gROOT->GetType(typeName);
2337 if (dt && (dt->GetType() > 0) && (dt->GetType() < 20))
2338 typ_id = dt->GetType();
2339 }
2340
2341 if (typ_id < 0)
2342 if (strcmp(name, typeName) == 0) {
2343 TClass *cl = TClass::GetClass(tname.Data());
2344 if (cl)
2346 }
2347
2348 if (typ_id < 0) {
2350 if (tname[tname.Length() - 1] == '*') {
2351 tname.Resize(tname.Length() - 1);
2352 isptr = kTRUE;
2353 }
2354 TClass *cl = TClass::GetClass(tname.Data());
2355 if (!cl) {
2356 Error("ClassMember", "Invalid class specifier %s", typeName);
2357 return;
2358 }
2359
2360 if (cl->IsTObject())
2362 else
2364
2365 if ((cl == TString::Class()) && !isptr)
2367 }
2368
2369 TStreamerElement *elem = nullptr;
2370
2372 elem = new TStreamerElement(name, "title", 0, typ_id, "raw:data");
2373 } else if (typ_id == TStreamerInfo::kBase) {
2374 TClass *cl = TClass::GetClass(tname.Data());
2375 if (cl) {
2376 TStreamerBase *b = new TStreamerBase(tname.Data(), "title", 0);
2377 b->SetBaseVersion(cl->GetClassVersion());
2378 elem = b;
2379 }
2380 } else if ((typ_id > 0) && (typ_id < 20)) {
2381 elem = new TStreamerBasicType(name, "title", 0, typ_id, typeName);
2384 elem = new TStreamerObject(name, "title", 0, tname.Data());
2385 } else if (typ_id == TStreamerInfo::kObjectp) {
2386 elem = new TStreamerObjectPointer(name, "title", 0, tname.Data());
2387 } else if (typ_id == TStreamerInfo::kAny) {
2388 elem = new TStreamerObjectAny(name, "title", 0, tname.Data());
2389 } else if (typ_id == TStreamerInfo::kAnyp) {
2390 elem = new TStreamerObjectAnyPointer(name, "title", 0, tname.Data());
2391 } else if (typ_id == TStreamerInfo::kTString) {
2392 elem = new TStreamerString(name, "title", 0);
2393 }
2394
2395 if (!elem) {
2396 Error("ClassMember", "Invalid combination name = %s type = %s", name, typeName);
2397 return;
2398 }
2399
2400 if (arrsize1 > 0) {
2401 elem->SetArrayDim(arrsize2 > 0 ? 2 : 1);
2402 elem->SetMaxIndex(0, arrsize1);
2403 if (arrsize2 > 0)
2404 elem->SetMaxIndex(1, arrsize2);
2405 }
2406
2407 // we indicate that there is no streamerinfo
2408 WorkWithElement(elem, -1);
2409}
2410
2411////////////////////////////////////////////////////////////////////////////////
2412/// Function is converts TObject and TString structures to more compact representation
2413
2415{
2416 if (stack->fIsPostProcessed)
2417 return;
2418
2419 const TStreamerElement *elem = stack->fElem;
2420
2421 if (!elem && !obj_cl)
2422 return;
2423
2424 stack->fIsPostProcessed = kTRUE;
2425
2426 // when element was written as separate object, close only braces and exit
2427 if (stack->fIsObjStarted) {
2428 AppendOutput("", "}");
2429 return;
2430 }
2431
2434
2435 if (obj_cl) {
2436 if (obj_cl == TObject::Class())
2437 isTObject = kTRUE;
2438 else if (obj_cl == TRef::Class())
2439 isTRef = kTRUE;
2440 else
2441 return;
2442 } else {
2443 const char *typname = elem->IsBase() ? elem->GetName() : elem->GetTypeName();
2444 isTObject = (elem->GetType() == TStreamerInfo::kTObject) || (strcmp("TObject", typname) == 0);
2445 isTString = elem->GetType() == TStreamerInfo::kTString;
2447 isOffsetPArray = (elem->GetType() > TStreamerInfo::kOffsetP) && (elem->GetType() < TStreamerInfo::kOffsetP + 20);
2448 isTArray = (strncmp("TArray", typname, 6) == 0);
2449 }
2450
2451 if (isTString || isSTLstring) {
2452 // just remove all kind of string length information
2453
2454 if (gDebug > 3)
2455 Info("PerformPostProcessing", "reformat string value = '%s'", fValue.Data());
2456
2457 stack->fValues.clear();
2458 } else if (isOffsetPArray) {
2459 // basic array with [fN] comment
2460
2461 if (stack->fValues.empty() && (fValue == "0")) {
2462 fValue = "[]";
2463 } else if ((stack->fValues.size() == 1) && (stack->fValues[0] == "1")) {
2464 stack->fValues.clear();
2465 } else {
2466 Error("PerformPostProcessing", "Wrong values for kOffsetP element %s", (elem ? elem->GetName() : "---"));
2467 stack->fValues.clear();
2468 fValue = "[]";
2469 }
2470 } else if (isTObject || isTRef) {
2471 // complex workaround for TObject/TRef streamer
2472 // would be nice if other solution can be found
2473 // Here is not supported TRef on TRef (double reference)
2474
2475 Int_t cnt = stack->fValues.size();
2476 if (fValue.Length() > 0)
2477 cnt++;
2478
2479 if (cnt < 2 || cnt > 3) {
2480 if (gDebug > 0)
2481 Error("PerformPostProcessing", "When storing TObject/TRef, strange number of items %d", cnt);
2482 AppendOutput(stack->NextMemberSeparator(), "\"dummy\"");
2484 } else {
2485 AppendOutput(stack->NextMemberSeparator(), "\"fUniqueID\"");
2487 AppendOutput(stack->fValues[0].c_str());
2488 AppendOutput(stack->NextMemberSeparator(), "\"fBits\"");
2490 auto tbits = std::atol((stack->fValues.size() > 1) ? stack->fValues[1].c_str() : fValue.Data());
2491 AppendOutput(std::to_string(tbits & ~TObject::kNotDeleted & ~TObject::kIsOnHeap).c_str());
2492 if (cnt == 3) {
2493 AppendOutput(stack->NextMemberSeparator(), "\"fPID\"");
2495 AppendOutput((stack->fValues.size() > 2) ? stack->fValues[2].c_str() : fValue.Data());
2496 }
2497
2498 stack->fValues.clear();
2499 fValue.Clear();
2500 return;
2501 }
2502
2503 } else if (isTArray) {
2504 // for TArray one deletes complete stack
2505 stack->fValues.clear();
2506 }
2507
2508 if (elem && elem->IsBase() && (fValue.Length() == 0)) {
2509 // here base class data already completely stored
2510 return;
2511 }
2512
2513 if (!stack->fValues.empty()) {
2514 // append element blob data just as abstract array, user is responsible to decode it
2515 AppendOutput("[");
2516 for (auto &blob: stack->fValues) {
2517 AppendOutput(blob.c_str());
2519 }
2520 }
2521
2522 if (fValue.Length() == 0) {
2523 AppendOutput("null");
2524 } else {
2526 fValue.Clear();
2527 }
2528
2529 if (!stack->fValues.empty())
2530 AppendOutput("]");
2531}
2532
2533////////////////////////////////////////////////////////////////////////////////
2534/// suppressed function of TBuffer
2535
2537{
2538 return nullptr;
2539}
2540
2541////////////////////////////////////////////////////////////////////////////////
2542/// suppressed function of TBuffer
2543
2545
2546////////////////////////////////////////////////////////////////////////////////
2547/// read version value from buffer
2548
2550{
2551 Version_t res = cl ? cl->GetClassVersion() : 0;
2552
2553 if (start)
2554 *start = 0;
2555 if (bcnt)
2556 *bcnt = 0;
2557
2558 if (!cl && Stack()->fClVersion) {
2559 res = Stack()->fClVersion;
2560 Stack()->fClVersion = 0;
2561 }
2562
2563 if (gDebug > 3)
2564 Info("ReadVersion", "Result: %d Class: %s", res, (cl ? cl->GetName() : "---"));
2565
2566 return res;
2567}
2568
2569////////////////////////////////////////////////////////////////////////////////
2570/// Ignored in TBufferJSON
2571
2572UInt_t TBufferJSON::WriteVersion(const TClass * /*cl*/, Bool_t /* useBcnt */)
2573{
2574 return 0;
2575}
2576
2577////////////////////////////////////////////////////////////////////////////////
2578/// Read object from buffer. Only used from TBuffer
2579
2581{
2582 if (gDebug > 2)
2583 Info("ReadObjectAny", "From current JSON node");
2584 void *res = JsonReadObject(nullptr, expectedClass);
2585 return res;
2586}
2587
2588////////////////////////////////////////////////////////////////////////////////
2589/// Skip any kind of object from buffer
2590
2592
2593////////////////////////////////////////////////////////////////////////////////
2594/// Write object to buffer. Only used from TBuffer
2595
2597{
2598 if (gDebug > 3)
2599 Info("WriteObjectClass", "Class %s", (actualClass ? actualClass->GetName() : " null"));
2600
2602}
2603
2604////////////////////////////////////////////////////////////////////////////////
2605/// If value exists, push in the current stack for post-processing
2606
2608{
2609 if (fValue.Length() > 0)
2611}
2612
2613////////////////////////////////////////////////////////////////////////////////
2614/// Read array of Bool_t from buffer
2615
2620
2621////////////////////////////////////////////////////////////////////////////////
2622/// Read array of Char_t from buffer
2623
2628
2629////////////////////////////////////////////////////////////////////////////////
2630/// Read array of UChar_t from buffer
2631
2636
2637////////////////////////////////////////////////////////////////////////////////
2638/// Read array of Short_t from buffer
2639
2644
2645////////////////////////////////////////////////////////////////////////////////
2646/// Read array of UShort_t from buffer
2647
2652
2653////////////////////////////////////////////////////////////////////////////////
2654/// Read array of Int_t from buffer
2655
2657{
2658 return JsonReadArray(i);
2659}
2660
2661////////////////////////////////////////////////////////////////////////////////
2662/// Read array of UInt_t from buffer
2663
2665{
2666 return JsonReadArray(i);
2667}
2668
2669////////////////////////////////////////////////////////////////////////////////
2670/// Read array of Long_t from buffer
2671
2676
2677////////////////////////////////////////////////////////////////////////////////
2678/// Read array of ULong_t from buffer
2679
2684
2685////////////////////////////////////////////////////////////////////////////////
2686/// Read array of Long64_t from buffer
2687
2692
2693////////////////////////////////////////////////////////////////////////////////
2694/// Read array of ULong64_t from buffer
2695
2700
2701////////////////////////////////////////////////////////////////////////////////
2702/// Read array of Float_t from buffer
2703
2708
2709////////////////////////////////////////////////////////////////////////////////
2710/// Read array of Double_t from buffer
2711
2716
2717////////////////////////////////////////////////////////////////////////////////
2718/// Read static array from JSON - not used
2719
2720template <typename T>
2722{
2723 Info("ReadArray", "Not implemented");
2724 return value ? 1 : 0;
2725}
2726
2727////////////////////////////////////////////////////////////////////////////////
2728/// Read array of Bool_t from buffer
2729
2734
2735////////////////////////////////////////////////////////////////////////////////
2736/// Read array of Char_t from buffer
2737
2742
2743////////////////////////////////////////////////////////////////////////////////
2744/// Read array of UChar_t from buffer
2745
2750
2751////////////////////////////////////////////////////////////////////////////////
2752/// Read array of Short_t from buffer
2753
2758
2759////////////////////////////////////////////////////////////////////////////////
2760/// Read array of UShort_t from buffer
2761
2766
2767////////////////////////////////////////////////////////////////////////////////
2768/// Read array of Int_t from buffer
2769
2774
2775////////////////////////////////////////////////////////////////////////////////
2776/// Read array of UInt_t from buffer
2777
2782
2783////////////////////////////////////////////////////////////////////////////////
2784/// Read array of Long_t from buffer
2785
2790
2791////////////////////////////////////////////////////////////////////////////////
2792/// Read array of ULong_t from buffer
2793
2798
2799////////////////////////////////////////////////////////////////////////////////
2800/// Read array of Long64_t from buffer
2801
2806
2807////////////////////////////////////////////////////////////////////////////////
2808/// Read array of ULong64_t from buffer
2809
2814
2815////////////////////////////////////////////////////////////////////////////////
2816/// Read array of Float_t from buffer
2817
2822
2823////////////////////////////////////////////////////////////////////////////////
2824/// Read array of Double_t from buffer
2825
2830
2831////////////////////////////////////////////////////////////////////////////////
2832/// Template method to read array from the JSON
2833
2834template <typename T>
2836{
2837 if (!arr || (arrsize <= 0))
2838 return;
2839 nlohmann::json *json = Stack()->fNode;
2840 if (gDebug > 2)
2841 Info("ReadFastArray", "Reading array sz %d from JSON %s", arrsize, json->dump().substr(0, 30).c_str());
2842 auto indexes = Stack()->MakeReadIndexes();
2843 if (indexes) { /* at least two dims */
2844 TArrayI &indx = indexes->GetIndices();
2845 Int_t lastdim = indx.GetSize() - 1;
2846 if (indexes->TotalLength() != arrsize)
2847 Error("ReadFastArray", "Mismatch %d-dim array sizes %d %d", lastdim + 1, arrsize, (int)indexes->TotalLength());
2848 for (int cnt = 0; cnt < arrsize; ++cnt) {
2849 nlohmann::json *elem = &(json->at(indx[0]));
2850 for (int k = 1; k < lastdim; ++k)
2851 elem = &((*elem)[indx[k]]);
2852 arr[cnt] = (asstring && elem->is_string()) ? elem->get<std::string>()[indx[lastdim]] : (*elem)[indx[lastdim]].get<T>();
2853 indexes->NextSeparator();
2854 }
2855 } else if (asstring && json->is_string()) {
2856 std::string str = json->get<std::string>();
2857 for (int cnt = 0; cnt < arrsize; ++cnt)
2858 arr[cnt] = (cnt < (int)str.length()) ? str[cnt] : 0;
2859 } else if (json->is_object() && (json->count("$arr") == 1)) {
2860 if (json->at("len").get<int>() != arrsize)
2861 Error("ReadFastArray", "Mismatch compressed array size %d %d", arrsize, json->at("len").get<int>());
2862
2863 for (int cnt = 0; cnt < arrsize; ++cnt)
2864 arr[cnt] = 0;
2865
2866 if (json->count("b") == 1) {
2867 auto base64 = json->at("b").get<std::string>();
2868
2869 int offset = (json->count("o") == 1) ? json->at("o").get<int>() : 0;
2870
2871 // TODO: provide TBase64::Decode with direct write into target buffer
2872 auto decode = TBase64::Decode(base64.c_str());
2873
2874 if (arrsize * (long) sizeof(T) < (offset + decode.Length())) {
2875 Error("ReadFastArray", "Base64 data %ld larger than target array size %ld", (long) decode.Length() + offset, (long) (arrsize*sizeof(T)));
2876 } else if ((sizeof(T) > 1) && (decode.Length() % sizeof(T) != 0)) {
2877 Error("ReadFastArray", "Base64 data size %ld not matches with element size %ld", (long) decode.Length(), (long) sizeof(T));
2878 } else {
2879 memcpy((char *) arr + offset, decode.Data(), decode.Length());
2880 }
2881 return;
2882 }
2883
2884 int p = 0, id = 0;
2885 std::string idname = "", pname, vname, nname;
2886 while (p < arrsize) {
2887 pname = std::string("p") + idname;
2888 if (json->count(pname) == 1)
2889 p = json->at(pname).get<int>();
2890 vname = std::string("v") + idname;
2891 if (json->count(vname) != 1)
2892 break;
2893 nlohmann::json &v = json->at(vname);
2894 if (v.is_array()) {
2895 for (unsigned sub = 0; sub < v.size(); ++sub)
2896 arr[p++] = v[sub].get<T>();
2897 } else {
2898 nname = std::string("n") + idname;
2899 unsigned ncopy = (json->count(nname) == 1) ? json->at(nname).get<unsigned>() : 1;
2900 for (unsigned sub = 0; sub < ncopy; ++sub)
2901 arr[p++] = v.get<T>();
2902 }
2903 idname = std::to_string(++id);
2904 }
2905 } else {
2906 if ((int)json->size() != arrsize)
2907 Error("ReadFastArray", "Mismatch array sizes %d %d", arrsize, (int)json->size());
2908 for (int cnt = 0; cnt < arrsize; ++cnt)
2909 arr[cnt] = json->at(cnt).get<T>();
2910 }
2911}
2912
2913////////////////////////////////////////////////////////////////////////////////
2914/// read array of Bool_t from buffer
2915
2920
2921////////////////////////////////////////////////////////////////////////////////
2922/// read array of Char_t from buffer
2923
2925{
2926 JsonReadFastArray(c, n, true);
2927}
2928
2929////////////////////////////////////////////////////////////////////////////////
2930/// read array of Char_t from buffer
2931
2936
2937////////////////////////////////////////////////////////////////////////////////
2938/// read array of UChar_t from buffer
2939
2944
2945////////////////////////////////////////////////////////////////////////////////
2946/// read array of Short_t from buffer
2947
2952
2953////////////////////////////////////////////////////////////////////////////////
2954/// read array of UShort_t from buffer
2955
2960
2961////////////////////////////////////////////////////////////////////////////////
2962/// read array of Int_t from buffer
2963
2968
2969////////////////////////////////////////////////////////////////////////////////
2970/// read array of UInt_t from buffer
2971
2976
2977////////////////////////////////////////////////////////////////////////////////
2978/// read array of Long_t from buffer
2979
2984
2985////////////////////////////////////////////////////////////////////////////////
2986/// read array of ULong_t from buffer
2987
2992
2993////////////////////////////////////////////////////////////////////////////////
2994/// read array of Long64_t from buffer
2995
3000
3001////////////////////////////////////////////////////////////////////////////////
3002/// read array of ULong64_t from buffer
3003
3008
3009////////////////////////////////////////////////////////////////////////////////
3010/// read array of Float_t from buffer
3011
3016
3017////////////////////////////////////////////////////////////////////////////////
3018/// read array of Double_t from buffer
3019
3024
3025////////////////////////////////////////////////////////////////////////////////
3026/// Read an array of 'n' objects from the I/O buffer.
3027/// Stores the objects read starting at the address 'start'.
3028/// The objects in the array are assume to be of class 'cl'.
3029/// Copied code from TBufferFile
3030
3031void TBufferJSON::ReadFastArray(void *start, const TClass *cl, Int_t n, TMemberStreamer * /* streamer */,
3032 const TClass * /* onFileClass */)
3033{
3034 if (gDebug > 1)
3035 Info("ReadFastArray", "void* n:%d cl:%s", n, cl->GetName());
3036
3037 // if (streamer) {
3038 // Info("ReadFastArray", "(void*) Calling streamer - not handled correctly");
3039 // streamer->SetOnFileClass(onFileClass);
3040 // (*streamer)(*this, start, 0);
3041 // return;
3042 // }
3043
3044 int objectSize = cl->Size();
3045 char *obj = (char *)start;
3046
3047 TJSONStackObj *stack = Stack();
3048 nlohmann::json *topnode = stack->fNode, *subnode = topnode;
3049 if (stack->fIndx)
3050 subnode = stack->fIndx->ExtractNode(topnode);
3051
3052 TArrayIndexProducer indexes(stack->fElem, n, "");
3053
3054 if (gDebug > 1)
3055 Info("ReadFastArray", "Indexes ndim:%d totallen:%d", indexes.NumDimensions(), indexes.TotalLength());
3056
3057 for (Int_t j = 0; j < n; j++, obj += objectSize) {
3058
3059 stack->fNode = indexes.ExtractNode(subnode);
3060
3061 JsonReadObject(obj, cl);
3062 }
3063
3064 // restore top node - show we use stack here?
3065 stack->fNode = topnode;
3066}
3067
3068////////////////////////////////////////////////////////////////////////////////
3069/// redefined here to avoid warning message from gcc
3070
3072 TMemberStreamer * /* streamer */, const TClass * /* onFileClass */)
3073{
3074 if (gDebug > 1)
3075 Info("ReadFastArray", "void** n:%d cl:%s prealloc:%s", n, cl->GetName(), (isPreAlloc ? "true" : "false"));
3076
3077 // if (streamer) {
3078 // Info("ReadFastArray", "(void**) Calling streamer - not handled correctly");
3079 // if (isPreAlloc) {
3080 // for (Int_t j = 0; j < n; j++) {
3081 // if (!start[j])
3082 // start[j] = cl->New();
3083 // }
3084 // }
3085 // streamer->SetOnFileClass(onFileClass);
3086 // (*streamer)(*this, (void *)start, 0);
3087 // return;
3088 // }
3089
3090 TJSONStackObj *stack = Stack();
3091 nlohmann::json *topnode = stack->fNode, *subnode = topnode;
3092 if (stack->fIndx)
3093 subnode = stack->fIndx->ExtractNode(topnode);
3094
3095 TArrayIndexProducer indexes(stack->fElem, n, "");
3096
3097 for (Int_t j = 0; j < n; j++) {
3098
3099 stack->fNode = indexes.ExtractNode(subnode);
3100
3101 if (!isPreAlloc) {
3102 void *old = start[j];
3103 start[j] = JsonReadObject(nullptr, cl);
3104 if (old && old != start[j] && TStreamerInfo::CanDelete())
3105 (const_cast<TClass *>(cl))->Destructor(old, kFALSE); // call delete and destruct
3106 } else {
3107 if (!start[j])
3108 start[j] = (const_cast<TClass *>(cl))->New();
3109 JsonReadObject(start[j], cl);
3110 }
3111 }
3112
3113 stack->fNode = topnode;
3114}
3115
3116template <typename T>
3118{
3119 bool is_base64 = Stack()->fBase64 || (fArrayCompact == kBase64);
3120
3121 if (!is_base64 && ((fArrayCompact == 0) || (arrsize < 6))) {
3122 fValue.Append("[");
3123 for (Int_t indx = 0; indx < arrsize; indx++) {
3124 if (indx > 0)
3127 }
3128 fValue.Append("]");
3129 } else if (is_base64 && !arrsize) {
3130 fValue.Append("[]");
3131 } else {
3132 fValue.Append("{");
3133 fValue.Append(TString::Format("\"$arr\":\"%s\"%s\"len\":%d", typname, fArraySepar.Data(), arrsize));
3134 Int_t aindx(0), bindx(arrsize);
3135 while ((aindx < arrsize) && (vname[aindx] == 0))
3136 aindx++;
3137 while ((aindx < bindx) && (vname[bindx - 1] == 0))
3138 bindx--;
3139
3140 if (is_base64) {
3141 // small initial offset makes no sense - JSON code is large then size gain
3142 if ((aindx * sizeof(T) < 5) && (aindx < bindx))
3143 aindx = 0;
3144
3145 if ((aindx > 0) && (aindx < bindx))
3146 fValue.Append(TString::Format("%s\"o\":%ld", fArraySepar.Data(), (long) (aindx * (int) sizeof(T))));
3147
3149 fValue.Append("\"b\":\"");
3150
3151 if (aindx < bindx)
3152 fValue.Append(TBase64::Encode((const char *) (vname + aindx), (bindx - aindx) * sizeof(T)));
3153
3154 fValue.Append("\"");
3155 } else if (aindx < bindx) {
3156 TString suffix("");
3157 Int_t p(aindx), suffixcnt(-1), lastp(0);
3158 while (p < bindx) {
3159 if (vname[p] == 0) {
3160 p++;
3161 continue;
3162 }
3163 Int_t p0(p++), pp(0), nsame(1);
3165 pp = bindx;
3166 p = bindx + 1;
3167 nsame = 0;
3168 }
3169 for (; p <= bindx; ++p) {
3170 if ((p < bindx) && (vname[p] == vname[p - 1])) {
3171 nsame++;
3172 continue;
3173 }
3174 if (vname[p - 1] == 0) {
3175 if (nsame > 9) {
3176 nsame = 0;
3177 break;
3178 }
3179 } else if (nsame > 5) {
3180 if (pp) {
3181 p = pp;
3182 nsame = 0;
3183 } else
3184 pp = p;
3185 break;
3186 }
3187 pp = p;
3188 nsame = 1;
3189 }
3190 if (pp <= p0)
3191 continue;
3192 if (++suffixcnt > 0)
3193 suffix.Form("%d", suffixcnt);
3194 if (p0 != lastp)
3195 fValue.Append(TString::Format("%s\"p%s\":%d", fArraySepar.Data(), suffix.Data(), p0));
3196 lastp = pp; /* remember cursor, it may be the same */
3197 fValue.Append(TString::Format("%s\"v%s\":", fArraySepar.Data(), suffix.Data()));
3198 if ((nsame > 1) || (pp - p0 == 1)) {
3200 if (nsame > 1)
3201 fValue.Append(TString::Format("%s\"n%s\":%d", fArraySepar.Data(), suffix.Data(), nsame));
3202 } else {
3203 fValue.Append("[");
3204 for (Int_t indx = p0; indx < pp; indx++) {
3205 if (indx > p0)
3208 }
3209 fValue.Append("]");
3210 }
3211 }
3212 }
3213 fValue.Append("}");
3214 }
3215}
3216
3217////////////////////////////////////////////////////////////////////////////////
3218/// Write array of Bool_t to buffer
3219
3221{
3222 JsonPushValue();
3223 JsonWriteArrayCompress(b, n, "Bool");
3224}
3225
3226////////////////////////////////////////////////////////////////////////////////
3227/// Write array of Char_t to buffer
3228
3230{
3231 JsonPushValue();
3232 JsonWriteArrayCompress(c, n, "Int8");
3233}
3234
3235////////////////////////////////////////////////////////////////////////////////
3236/// Write array of UChar_t to buffer
3237
3239{
3240 JsonPushValue();
3241 JsonWriteArrayCompress(c, n, "Uint8");
3242}
3243
3244////////////////////////////////////////////////////////////////////////////////
3245/// Write array of Short_t to buffer
3246
3248{
3249 JsonPushValue();
3250 JsonWriteArrayCompress(h, n, "Int16");
3251}
3252
3253////////////////////////////////////////////////////////////////////////////////
3254/// Write array of UShort_t to buffer
3255
3257{
3258 JsonPushValue();
3259 JsonWriteArrayCompress(h, n, "Uint16");
3260}
3261
3262////////////////////////////////////////////////////////////////////////////////
3263/// Write array of Int_ to buffer
3264
3266{
3267 JsonPushValue();
3268 JsonWriteArrayCompress(i, n, "Int32");
3269}
3270
3271////////////////////////////////////////////////////////////////////////////////
3272/// Write array of UInt_t to buffer
3273
3275{
3276 JsonPushValue();
3277 JsonWriteArrayCompress(i, n, "Uint32");
3278}
3279
3280////////////////////////////////////////////////////////////////////////////////
3281/// Write array of Long_t to buffer
3282
3284{
3285 JsonPushValue();
3286 JsonWriteArrayCompress(l, n, "Int64");
3287}
3288
3289////////////////////////////////////////////////////////////////////////////////
3290/// Write array of ULong_t to buffer
3291
3293{
3294 JsonPushValue();
3295 JsonWriteArrayCompress(l, n, "Uint64");
3296}
3297
3298////////////////////////////////////////////////////////////////////////////////
3299/// Write array of Long64_t to buffer
3300
3302{
3303 JsonPushValue();
3304 JsonWriteArrayCompress(l, n, "Int64");
3305}
3306
3307////////////////////////////////////////////////////////////////////////////////
3308/// Write array of ULong64_t to buffer
3309
3311{
3312 JsonPushValue();
3313 JsonWriteArrayCompress(l, n, "Uint64");
3314}
3315
3316////////////////////////////////////////////////////////////////////////////////
3317/// Write array of Float_t to buffer
3318
3320{
3321 JsonPushValue();
3322 JsonWriteArrayCompress(f, n, "Float32");
3323}
3324
3325////////////////////////////////////////////////////////////////////////////////
3326/// Write array of Double_t to buffer
3327
3329{
3330 JsonPushValue();
3331 JsonWriteArrayCompress(d, n, "Float64");
3332}
3333
3334////////////////////////////////////////////////////////////////////////////////
3335/// Template method to write array of arbitrary dimensions
3336/// Different methods can be used for store last array dimension -
3337/// either JsonWriteArrayCompress<T>() or JsonWriteConstChar()
3338/// \note Due to the current limit of the buffer size, the function aborts execution of the program in case of overflow. See https://github.com/root-project/root/issues/6734 for more details.
3339///
3340template <typename T>
3342 void (TBufferJSON::*method)(const T *, Int_t, const char *))
3343{
3344 JsonPushValue();
3345 if (arrsize <= 0) { /*fJsonrCnt++;*/
3346 fValue.Append("[]");
3347 return;
3348 }
3349 constexpr Int_t dataWidth = 1; // at least 1
3350 const Int_t maxElements = (std::numeric_limits<Int_t>::max() - Length())/dataWidth;
3351 if (arrsize > maxElements)
3352 {
3353 Fatal("JsonWriteFastArray", "Not enough space left in the buffer (1GB limit). %lld elements is greater than the max left of %d", arrsize, maxElements);
3354 return; // In case the user re-routes the error handler to not die when Fatal is called
3355 }
3356
3358 if (elem && (elem->GetArrayDim() > 1) && (elem->GetArrayLength() == arrsize)) {
3359 TArrayI indexes(elem->GetArrayDim() - 1);
3360 indexes.Reset(0);
3361 Int_t cnt = 0, shift = 0, len = elem->GetMaxIndex(indexes.GetSize());
3362 while (cnt >= 0) {
3363 if (indexes[cnt] >= elem->GetMaxIndex(cnt)) {
3364 fValue.Append("]");
3365 indexes[cnt--] = 0;
3366 if (cnt >= 0)
3367 indexes[cnt]++;
3368 continue;
3369 }
3370 fValue.Append(indexes[cnt] == 0 ? "[" : fArraySepar.Data());
3371 if (++cnt == indexes.GetSize()) {
3372 (*this.*method)((arr + shift), len, typname);
3373 indexes[--cnt]++;
3374 shift += len;
3375 }
3376 }
3377 } else {
3378 (*this.*method)(arr, arrsize, typname);
3379 }
3380}
3381
3382////////////////////////////////////////////////////////////////////////////////
3383/// Write array of Bool_t to buffer
3384
3386{
3387 JsonWriteFastArray(b, n, "Bool", &TBufferJSON::JsonWriteArrayCompress<Bool_t>);
3388}
3389
3390////////////////////////////////////////////////////////////////////////////////
3391/// Write array of Char_t to buffer
3392///
3393/// Normally written as JSON string, but if string includes \0 in the middle
3394/// or some special characters, uses regular array. From array size 1000 it
3395/// will be automatically converted into base64 coding
3396
3398{
3399 Bool_t need_blob = false;
3400 Bool_t has_zero = false;
3401 for (Long64_t i=0;i<n;++i) {
3402 if (!c[i]) {
3403 has_zero = true; // might be terminal '\0'
3404 } else if (has_zero || !isprint(c[i])) {
3405 need_blob = true;
3406 break;
3407 }
3408 }
3409
3410 if (need_blob && (n >= 1000) && (!Stack()->fElem || (Stack()->fElem->GetArrayDim() < 2)))
3411 Stack()->fBase64 = true;
3412
3413 JsonWriteFastArray(c, n, "Int8", need_blob ? &TBufferJSON::JsonWriteArrayCompress<Char_t> : &TBufferJSON::JsonWriteConstChar);
3414}
3415
3416////////////////////////////////////////////////////////////////////////////////
3417/// Write array of Char_t to buffer
3418
3423
3424////////////////////////////////////////////////////////////////////////////////
3425/// Write array of UChar_t to buffer
3426
3428{
3429 JsonWriteFastArray(c, n, "Uint8", &TBufferJSON::JsonWriteArrayCompress<UChar_t>);
3430}
3431
3432////////////////////////////////////////////////////////////////////////////////
3433/// Write array of Short_t to buffer
3434
3436{
3437 JsonWriteFastArray(h, n, "Int16", &TBufferJSON::JsonWriteArrayCompress<Short_t>);
3438}
3439
3440////////////////////////////////////////////////////////////////////////////////
3441/// Write array of UShort_t to buffer
3442
3444{
3445 JsonWriteFastArray(h, n, "Uint16", &TBufferJSON::JsonWriteArrayCompress<UShort_t>);
3446}
3447
3448////////////////////////////////////////////////////////////////////////////////
3449/// Write array of Int_t to buffer
3450
3452{
3453 JsonWriteFastArray(i, n, "Int32", &TBufferJSON::JsonWriteArrayCompress<Int_t>);
3454}
3455
3456////////////////////////////////////////////////////////////////////////////////
3457/// Write array of UInt_t to buffer
3458
3460{
3461 JsonWriteFastArray(i, n, "Uint32", &TBufferJSON::JsonWriteArrayCompress<UInt_t>);
3462}
3463
3464////////////////////////////////////////////////////////////////////////////////
3465/// Write array of Long_t to buffer
3466
3468{
3469 JsonWriteFastArray(l, n, "Int64", &TBufferJSON::JsonWriteArrayCompress<Long_t>);
3470}
3471
3472////////////////////////////////////////////////////////////////////////////////
3473/// Write array of ULong_t to buffer
3474
3476{
3477 JsonWriteFastArray(l, n, "Uint64", &TBufferJSON::JsonWriteArrayCompress<ULong_t>);
3478}
3479
3480////////////////////////////////////////////////////////////////////////////////
3481/// Write array of Long64_t to buffer
3482
3484{
3485 JsonWriteFastArray(l, n, "Int64", &TBufferJSON::JsonWriteArrayCompress<Long64_t>);
3486}
3487
3488////////////////////////////////////////////////////////////////////////////////
3489/// Write array of ULong64_t to buffer
3490
3492{
3493 JsonWriteFastArray(l, n, "Uint64", &TBufferJSON::JsonWriteArrayCompress<ULong64_t>);
3494}
3495
3496////////////////////////////////////////////////////////////////////////////////
3497/// Write array of Float_t to buffer
3498
3500{
3501 JsonWriteFastArray(f, n, "Float32", &TBufferJSON::JsonWriteArrayCompress<Float_t>);
3502}
3503
3504////////////////////////////////////////////////////////////////////////////////
3505/// Write array of Double_t to buffer
3506
3508{
3509 JsonWriteFastArray(d, n, "Float64", &TBufferJSON::JsonWriteArrayCompress<Double_t>);
3510}
3511
3512////////////////////////////////////////////////////////////////////////////////
3513/// Recall TBuffer function to avoid gcc warning message
3514
3515void TBufferJSON::WriteFastArray(void *start, const TClass *cl, Long64_t n, TMemberStreamer * /* streamer */)
3516{
3517 if (gDebug > 2)
3518 Info("WriteFastArray", "void *start cl:%s n:%lld", cl ? cl->GetName() : "---", n);
3519
3520 // if (streamer) {
3521 // JsonDisablePostprocessing();
3522 // (*streamer)(*this, start, 0);
3523 // return;
3524 // }
3525
3526 if (n < 0) {
3527 // special handling of empty StreamLoop
3528 AppendOutput("null");
3530 } else {
3531
3532 char *obj = (char *)start;
3533 if (!n)
3534 n = 1;
3535 int size = cl->Size();
3536
3538
3539 if (indexes.IsArray()) {
3541 AppendOutput(indexes.GetBegin());
3542 }
3543
3544 for (Long64_t j = 0; j < n; j++, obj += size) {
3545
3546 if (j > 0)
3547 AppendOutput(indexes.NextSeparator());
3548
3549 JsonWriteObject(obj, cl, kFALSE);
3550
3551 if (indexes.IsArray() && (fValue.Length() > 0)) {
3553 fValue.Clear();
3554 }
3555 }
3556
3557 if (indexes.IsArray())
3558 AppendOutput(indexes.GetEnd());
3559 }
3560
3561 if (Stack()->fIndx)
3562 AppendOutput(Stack()->fIndx->NextSeparator());
3563}
3564
3565////////////////////////////////////////////////////////////////////////////////
3566/// Recall TBuffer function to avoid gcc warning message
3567
3569 TMemberStreamer * /* streamer */)
3570{
3571 if (gDebug > 2)
3572 Info("WriteFastArray", "void **startp cl:%s n:%lld", cl->GetName(), n);
3573
3574 // if (streamer) {
3575 // JsonDisablePostprocessing();
3576 // (*streamer)(*this, (void *)start, 0);
3577 // return 0;
3578 // }
3579
3580 if (n <= 0)
3581 return 0;
3582
3583 Int_t res = 0;
3584
3586
3587 if (indexes.IsArray()) {
3589 AppendOutput(indexes.GetBegin());
3590 }
3591
3592 for (Long64_t j = 0; j < n; j++) {
3593
3594 if (j > 0)
3595 AppendOutput(indexes.NextSeparator());
3596
3597 if (!isPreAlloc) {
3598 res |= WriteObjectAny(start[j], cl);
3599 } else {
3600 if (!start[j])
3601 start[j] = (const_cast<TClass *>(cl))->New();
3602 // ((TClass*)cl)->Streamer(start[j],*this);
3603 JsonWriteObject(start[j], cl, kFALSE);
3604 }
3605
3606 if (indexes.IsArray() && (fValue.Length() > 0)) {
3608 fValue.Clear();
3609 }
3610 }
3611
3612 if (indexes.IsArray())
3613 AppendOutput(indexes.GetEnd());
3614
3615 if (Stack()->fIndx)
3616 AppendOutput(Stack()->fIndx->NextSeparator());
3617
3618 return res;
3619}
3620
3621////////////////////////////////////////////////////////////////////////////////
3622/// stream object to/from buffer
3623
3624void TBufferJSON::StreamObject(void *obj, const TClass *cl, const TClass * /* onfileClass */)
3625{
3626 if (gDebug > 3)
3627 Info("StreamObject", "Class: %s", (cl ? cl->GetName() : "none"));
3628
3629 if (IsWriting())
3630 JsonWriteObject(obj, cl);
3631 else
3632 JsonReadObject(obj, cl);
3633}
3634
3635////////////////////////////////////////////////////////////////////////////////
3636/// Template function to read basic value from JSON
3637
3638template <typename T>
3640{
3641 value = Stack()->GetStlNode()->get<T>();
3642}
3643
3644////////////////////////////////////////////////////////////////////////////////
3645/// Reads Bool_t value from buffer
3646
3648{
3649 JsonReadBasic(val);
3650}
3651
3652////////////////////////////////////////////////////////////////////////////////
3653/// Reads Char_t value from buffer
3654
3656{
3657 if (!Stack()->fValues.empty())
3658 val = (Char_t)Stack()->PopIntValue();
3659 else
3660 val = Stack()->GetStlNode()->get<Char_t>();
3661}
3662
3663////////////////////////////////////////////////////////////////////////////////
3664/// Reads UChar_t value from buffer
3665
3667{
3668 JsonReadBasic(val);
3669}
3670
3671////////////////////////////////////////////////////////////////////////////////
3672/// Reads Short_t value from buffer
3673
3675{
3676 JsonReadBasic(val);
3677}
3678
3679////////////////////////////////////////////////////////////////////////////////
3680/// Reads UShort_t value from buffer
3681
3683{
3684 JsonReadBasic(val);
3685}
3686
3687////////////////////////////////////////////////////////////////////////////////
3688/// Reads Int_t value from buffer
3689
3691{
3692 if (!Stack()->fValues.empty())
3693 val = Stack()->PopIntValue();
3694 else
3695 JsonReadBasic(val);
3696}
3697
3698////////////////////////////////////////////////////////////////////////////////
3699/// Reads UInt_t value from buffer
3700
3702{
3703 JsonReadBasic(val);
3704}
3705
3706////////////////////////////////////////////////////////////////////////////////
3707/// Reads Long_t value from buffer
3708
3710{
3711 JsonReadBasic(val);
3712}
3713
3714////////////////////////////////////////////////////////////////////////////////
3715/// Reads ULong_t value from buffer
3716
3718{
3719 JsonReadBasic(val);
3720}
3721
3722////////////////////////////////////////////////////////////////////////////////
3723/// Reads Long64_t value from buffer
3724
3726{
3727 JsonReadBasic(val);
3728}
3729
3730////////////////////////////////////////////////////////////////////////////////
3731/// Reads ULong64_t value from buffer
3732
3734{
3735 JsonReadBasic(val);
3736}
3737
3738////////////////////////////////////////////////////////////////////////////////
3739/// Reads Float_t value from buffer
3740
3742{
3743 nlohmann::json *json = Stack()->GetStlNode();
3744 if (json->is_null())
3745 val = std::numeric_limits<Float_t>::quiet_NaN();
3746 else
3747 try {
3748 val = json->get<Float_t>();
3749 } catch (nlohmann::detail::type_error &e) {
3750 auto aux = json->get<std::string>();
3751 if (aux == "nanf") {
3752 val = std::numeric_limits<Float_t>::quiet_NaN();
3753 } else if (aux == "inff") {
3754 val = std::numeric_limits<Float_t>::infinity();
3755 } else if (aux == "-inff") {
3756 val = -std::numeric_limits<Float_t>::infinity();
3757 } else {
3758 Error("ReadFloat", "%s '%s'", e.what(), aux.c_str());
3759 val = std::numeric_limits<Float_t>::quiet_NaN();
3760 }
3761 }
3762}
3763
3764////////////////////////////////////////////////////////////////////////////////
3765/// Reads Double_t value from buffer
3766
3768{
3769 nlohmann::json *json = Stack()->GetStlNode();
3770 if (json->is_null())
3771 val = std::numeric_limits<Double_t>::quiet_NaN();
3772 else
3773 try {
3774 val = json->get<Double_t>();
3775 } catch (nlohmann::detail::type_error &e) {
3776 auto aux = json->get<std::string>();
3777 if (aux == "nan") {
3778 val = std::numeric_limits<Double_t>::quiet_NaN();
3779 } else if (aux == "inf") {
3780 val = std::numeric_limits<Double_t>::infinity();
3781 } else if (aux == "-inf") {
3782 val = -std::numeric_limits<Double_t>::infinity();
3783 } else {
3784 Error("ReadDouble", "%s '%s'", e.what(), aux.c_str());
3785 val = std::numeric_limits<Double_t>::quiet_NaN();
3786 }
3787 }
3788}
3789
3790////////////////////////////////////////////////////////////////////////////////
3791/// Reads array of characters from buffer
3792
3794{
3795 Error("ReadCharP", "Not implemented");
3796}
3797
3798////////////////////////////////////////////////////////////////////////////////
3799/// Reads a TString
3800
3802{
3803 std::string str;
3804 JsonReadBasic(str);
3805 val = str.c_str();
3806}
3807
3808////////////////////////////////////////////////////////////////////////////////
3809/// Reads a std::string
3810
3811void TBufferJSON::ReadStdString(std::string *val)
3812{
3813 JsonReadBasic(*val);
3814}
3815
3816////////////////////////////////////////////////////////////////////////////////
3817/// Reads a char* string
3818
3820{
3821 std::string str;
3822 JsonReadBasic(str);
3823
3824 if (s) {
3825 delete[] s;
3826 s = nullptr;
3827 }
3828
3829 std::size_t nch = str.length();
3830 if (nch > 0) {
3831 s = new char[nch + 1];
3832 memcpy(s, str.c_str(), nch);
3833 s[nch] = 0;
3834 }
3835}
3836
3837////////////////////////////////////////////////////////////////////////////////
3838/// Writes Bool_t value to buffer
3839
3845
3846////////////////////////////////////////////////////////////////////////////////
3847/// Writes Char_t value to buffer
3848
3854
3855////////////////////////////////////////////////////////////////////////////////
3856/// Writes UChar_t value to buffer
3857
3863
3864////////////////////////////////////////////////////////////////////////////////
3865/// Writes Short_t value to buffer
3866
3872
3873////////////////////////////////////////////////////////////////////////////////
3874/// Writes UShort_t value to buffer
3875
3881
3882////////////////////////////////////////////////////////////////////////////////
3883/// Writes Int_t value to buffer
3884
3886{
3887 JsonPushValue();
3888 JsonWriteBasic(i);
3889}
3890
3891////////////////////////////////////////////////////////////////////////////////
3892/// Writes UInt_t value to buffer
3893
3895{
3896 JsonPushValue();
3897 JsonWriteBasic(i);
3898}
3899
3900////////////////////////////////////////////////////////////////////////////////
3901/// Writes Long_t value to buffer
3902
3908
3909////////////////////////////////////////////////////////////////////////////////
3910/// Writes ULong_t value to buffer
3911
3917
3918////////////////////////////////////////////////////////////////////////////////
3919/// Writes Long64_t value to buffer
3920
3926
3927////////////////////////////////////////////////////////////////////////////////
3928/// Writes ULong64_t value to buffer
3929
3935
3936////////////////////////////////////////////////////////////////////////////////
3937/// Writes Float_t value to buffer
3938
3944
3945////////////////////////////////////////////////////////////////////////////////
3946/// Writes Double_t value to buffer
3947
3953
3954////////////////////////////////////////////////////////////////////////////////
3955/// Writes array of characters to buffer
3956
3958{
3959 JsonPushValue();
3960
3962}
3963
3964////////////////////////////////////////////////////////////////////////////////
3965/// Writes a TString
3966
3968{
3969 JsonPushValue();
3970
3971 JsonWriteConstChar(s.Data(), s.Length());
3972}
3973
3974////////////////////////////////////////////////////////////////////////////////
3975/// Writes a std::string
3976
3977void TBufferJSON::WriteStdString(const std::string *s)
3978{
3979 JsonPushValue();
3980
3981 if (s)
3982 JsonWriteConstChar(s->c_str(), s->length());
3983 else
3984 JsonWriteConstChar("", 0);
3985}
3986
3987////////////////////////////////////////////////////////////////////////////////
3988/// Writes a char*
3989
3991{
3992 JsonPushValue();
3993
3995}
3996
3997////////////////////////////////////////////////////////////////////////////////
3998/// converts Char_t to string and add to json value buffer
3999
4001{
4002 char buf[50];
4003 snprintf(buf, sizeof(buf), "%d", value);
4004 fValue.Append(buf);
4005}
4006
4007////////////////////////////////////////////////////////////////////////////////
4008/// converts Short_t to string and add to json value buffer
4009
4011{
4012 char buf[50];
4013 snprintf(buf, sizeof(buf), "%hd", value);
4014 fValue.Append(buf);
4015}
4016
4017////////////////////////////////////////////////////////////////////////////////
4018/// converts Int_t to string and add to json value buffer
4019
4021{
4022 char buf[50];
4023 snprintf(buf, sizeof(buf), "%d", value);
4024 fValue.Append(buf);
4025}
4026
4027////////////////////////////////////////////////////////////////////////////////
4028/// converts Long_t to string and add to json value buffer
4029
4031{
4032 char buf[50];
4033 snprintf(buf, sizeof(buf), "%ld", value);
4034 fValue.Append(buf);
4035}
4036
4037////////////////////////////////////////////////////////////////////////////////
4038/// converts Long64_t to string and add to json value buffer
4039
4041{
4042 fValue.Append(std::to_string(value).c_str());
4043}
4044
4045////////////////////////////////////////////////////////////////////////////////
4046/// converts Float_t to string and add to json value buffer
4047
4049{
4050 if (std::isinf(value)) {
4051 if (!fStoreInfNaN)
4052 fValue.Append((value < 0.) ? "-2e308" : "2e308"); // JavaScript Number.MAX_VALUE is approx 1.79e308
4053 else
4054 fValue.Append((value < 0.) ? "\"-inff\"" : "\"inff\"");
4055 } else if (std::isnan(value)) {
4056 if (!fStoreInfNaN)
4057 fValue.Append("null");
4058 else
4059 fValue.Append("\"nanf\"");
4060 } else {
4061 char buf[200];
4062 ConvertFloat(value, buf, sizeof(buf));
4063 fValue.Append(buf);
4064 }
4065}
4066
4067////////////////////////////////////////////////////////////////////////////////
4068/// converts Double_t to string and add to json value buffer
4069
4071{
4072 if (std::isinf(value)) {
4073 if (!fStoreInfNaN)
4074 fValue.Append((value < 0.) ? "-2e308" : "2e308"); // JavaScript Number.MAX_VALUE is approx 1.79e308
4075 else
4076 fValue.Append((value < 0.) ? "\"-inf\"" : "\"inf\"");
4077 } else if (std::isnan(value)) {
4078 if (!fStoreInfNaN)
4079 fValue.Append("null");
4080 else
4081 fValue.Append("\"nan\"");
4082 } else {
4083 char buf[200];
4084 ConvertDouble(value, buf, sizeof(buf));
4085 fValue.Append(buf);
4086 }
4087}
4088
4089////////////////////////////////////////////////////////////////////////////////
4090/// converts Bool_t to string and add to json value buffer
4091
4093{
4094 fValue.Append(value ? "true" : "false");
4095}
4096
4097////////////////////////////////////////////////////////////////////////////////
4098/// converts UChar_t to string and add to json value buffer
4099
4101{
4102 char buf[50];
4103 snprintf(buf, sizeof(buf), "%u", value);
4104 fValue.Append(buf);
4105}
4106
4107////////////////////////////////////////////////////////////////////////////////
4108/// converts UShort_t to string and add to json value buffer
4109
4111{
4112 char buf[50];
4113 snprintf(buf, sizeof(buf), "%hu", value);
4114 fValue.Append(buf);
4115}
4116
4117////////////////////////////////////////////////////////////////////////////////
4118/// converts UInt_t to string and add to json value buffer
4119
4121{
4122 char buf[50];
4123 snprintf(buf, sizeof(buf), "%u", value);
4124 fValue.Append(buf);
4125}
4126
4127////////////////////////////////////////////////////////////////////////////////
4128/// converts ULong_t to string and add to json value buffer
4129
4131{
4132 char buf[50];
4133 snprintf(buf, sizeof(buf), "%lu", value);
4134 fValue.Append(buf);
4135}
4136
4137////////////////////////////////////////////////////////////////////////////////
4138/// converts ULong64_t to string and add to json value buffer
4139
4141{
4142 fValue.Append(std::to_string(value).c_str());
4143}
4144
4145////////////////////////////////////////////////////////////////////////////////
4146/// writes string value, processing all kind of special characters
4147
4148void TBufferJSON::JsonWriteConstChar(const char *value, Int_t len, const char * /* typname */)
4149{
4150 if (!value) {
4151
4152 fValue.Append("\"\"");
4153
4154 } else {
4155
4156 fValue.Append("\"");
4157
4158 if (len < 0)
4159 len = strlen(value);
4160
4161 for (Int_t n = 0; n < len; n++) {
4162 unsigned char c = value[n];
4163 switch (c) {
4164 case 0: n = len; break;
4165 case '\n': fValue.Append("\\n"); break;
4166 case '\t': fValue.Append("\\t"); break;
4167 case '\"': fValue.Append("\\\""); break;
4168 case '\\': fValue.Append("\\\\"); break;
4169 case '\b': fValue.Append("\\b"); break;
4170 case '\f': fValue.Append("\\f"); break;
4171 case '\r': fValue.Append("\\r"); break;
4172 case '/': fValue.Append("\\/"); break;
4173 default:
4174 if (c < 31) {
4175 fValue.Append(TString::Format("\\u%04x", (unsigned)c));
4176 } else if (c < 0x80) {
4177 fValue.Append(c);
4178 } else if ((n < len - 1) && ((c & 0xe0) == 0xc0) && ((value[n+1] & 0xc0) == 0x80)) {
4179 unsigned code = ((unsigned)value[n+1] & 0x3f) | (((unsigned) c & 0x1f) << 6);
4180 fValue.Append(TString::Format("\\u%04x", code));
4181 n++;
4182 } else if ((n < len - 2) && ((c & 0xf0) == 0xe0) && ((value[n+1] & 0xc0) == 0x80) && ((value[n+2] & 0xc0) == 0x80)) {
4183 unsigned code = ((unsigned)value[n+2] & 0x3f) | (((unsigned) value[n+1] & 0x3f) << 6) | (((unsigned) c & 0x0f) << 12);
4184 fValue.Append(TString::Format("\\u%04x", code));
4185 n+=2;
4186 } else if ((n < len - 3) && ((c & 0xf8) == 0xf0) && ((value[n+1] & 0xc0) == 0x80) && ((value[n+2] & 0xc0) == 0x80) && ((value[n+3] & 0xc0) == 0x80)) {
4187 unsigned code = ((unsigned)value[n+3] & 0x3f) | (((unsigned) value[n+2] & 0x3f) << 6) | (((unsigned) value[n+1] & 0x3f) << 12) | (((unsigned) c & 0x07) << 18);
4188 // TODO: no idea how to add codes which are higher then 0xFFFF
4189 fValue.Append(TString::Format("\\u%04x\\u%04x", code & 0xffff, code >> 16));
4190 n+=3;
4191 } else {
4192 fValue.Append(TString::Format("\\u%04x", (unsigned)c));
4193 }
4194 }
4195 }
4196
4197 fValue.Append("\"");
4198 }
4199}
4200
4201////////////////////////////////////////////////////////////////////////////////
4202/// Read data of base class.
4203
4205{
4206 if (elem->GetClassPointer() == TObject::Class()) {
4208 } else {
4210 }
4211}
nlohmann::json json
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define h(i)
Definition RSha256.hxx:106
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
unsigned short UShort_t
Unsigned Short integer 2 bytes (unsigned short)
Definition RtypesCore.h:54
long Longptr_t
Integer large enough to hold a pointer (platform-dependent)
Definition RtypesCore.h:89
short Version_t
Class version identifier (short)
Definition RtypesCore.h:79
unsigned char UChar_t
Unsigned Character 1 byte (unsigned char)
Definition RtypesCore.h:52
char Char_t
Character 1 byte (char)
Definition RtypesCore.h:51
unsigned long ULong_t
Unsigned long integer 4 bytes (unsigned long). Size depends on architecture.
Definition RtypesCore.h:69
long Long_t
Signed long integer 4 bytes (long). Size depends on architecture.
Definition RtypesCore.h:68
float Float_t
Float 4 bytes (float)
Definition RtypesCore.h:71
short Short_t
Signed Short integer 2 bytes (short)
Definition RtypesCore.h:53
constexpr Bool_t kFALSE
Definition RtypesCore.h:108
long long Long64_t
Portable signed long integer 8 bytes.
Definition RtypesCore.h:83
unsigned long long ULong64_t
Portable unsigned long integer 8 bytes.
Definition RtypesCore.h:84
constexpr Bool_t kTRUE
Definition RtypesCore.h:107
@ json_stdstring
@ json_TCollection
@ json_TString
@ json_TArray
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
@ kNoType_t
Definition TDataType.h:33
@ kFloat_t
Definition TDataType.h:31
@ kULong64_t
Definition TDataType.h:32
@ kInt_t
Definition TDataType.h:30
@ kchar
Definition TDataType.h:31
@ kLong_t
Definition TDataType.h:30
@ kDouble32_t
Definition TDataType.h:31
@ kShort_t
Definition TDataType.h:29
@ kBool_t
Definition TDataType.h:32
@ kBits
Definition TDataType.h:34
@ kULong_t
Definition TDataType.h:30
@ kLong64_t
Definition TDataType.h:32
@ kVoid_t
Definition TDataType.h:35
@ kUShort_t
Definition TDataType.h:29
@ kDouble_t
Definition TDataType.h:31
@ kCharStar
Definition TDataType.h:34
@ kChar_t
Definition TDataType.h:29
@ kUChar_t
Definition TDataType.h:29
@ kCounter
Definition TDataType.h:34
@ kUInt_t
Definition TDataType.h:30
@ kFloat16_t
Definition TDataType.h:33
@ kOther_t
Definition TDataType.h:32
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:208
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t option
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 filename
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 offset
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 UChar_t len
Option_t Option_t TPoint TPoint const char mode
char name[80]
Definition TGX11.cxx:110
char idname[128]
Int_t gDebug
Global variable setting the debug level. Set to 0 to disable, increase it in steps of 1 to increase t...
Definition TROOT.cxx:627
#define gROOT
Definition TROOT.h:411
#define free
Definition civetweb.c:1578
#define snprintf
Definition civetweb.c:1579
#define malloc
Definition civetweb.c:1575
const_iterator begin() const
Array of integers (32 bits per element).
Definition TArrayI.h:27
void Set(Int_t n) override
Set size of this array to n ints.
Definition TArrayI.cxx:104
void Reset()
Definition TArrayI.h:47
JSON array separators for multi-dimensional JSON arrays It fully reproduces array dimensions as in or...
TArrayI & GetIndices()
return array with current index
nlohmann::json * ExtractNode(nlohmann::json *topnode, bool next=true)
Int_t NumDimensions() const
returns number of array dimensions
Int_t TotalLength() const
returns total number of elements in array
const char * GetBegin()
Bool_t IsDone() const
const char * GetEnd()
TArrayIndexProducer(TDataMember *member, Int_t extradim, const char *separ)
Bool_t IsArray() const
const char * NextSeparator()
increment indexes and returns intermediate or last separator
TArrayIndexProducer(TStreamerElement *elem, Int_t arraylen, const char *separ)
Abstract array base class.
Definition TArray.h:31
Int_t GetSize() const
Definition TArray.h:47
static TClass * Class()
static TString Decode(const char *data)
Decode a base64 string date into a generic TString.
Definition TBase64.cxx:130
static TString Encode(const char *data)
Transform data into a null terminated base64 string.
Definition TBase64.cxx:106
void InitMap() override
Create the fMap container and initialize them with the null object.
void MapObject(const TObject *obj, UInt_t offset=1) override
Add object to the fMap container.
Long64_t GetObjectTag(const void *obj)
Returns tag for specified object from objects map (if exists) Returns 0 if object not included into o...
void GetMappedObject(UInt_t tag, void *&ptr, TClass *&ClassPtr) const override
Retrieve the object stored in the buffer's object map at 'tag' Set ptr and ClassPtr respectively to t...
Int_t WriteObjectAny(const void *obj, const TClass *ptrClass, Bool_t cacheReuse=kTRUE) override
Write object to I/O buffer.
Class for serializing object to and from JavaScript Object Notation (JSON) format.
Definition TBufferJSON.h:30
void ReadULong(ULong_t &l) final
Reads ULong_t value from buffer.
void JsonWriteBasic(Char_t value)
converts Char_t to string and add to json value buffer
void WriteShort(Short_t s) final
Writes Short_t value to buffer.
void JsonWriteCollection(TCollection *obj, const TClass *objClass)
store content of ROOT collection
TString fSemicolon
! depending from compression level, " : " or ":"
Int_t fCompact
! 0 - no any compression, 1 - no spaces in the begin, 2 - no new lines, 3 - no spaces at all
void ReadULong64(ULong64_t &l) final
Reads ULong64_t value from buffer.
void WriteStdString(const std::string *s) final
Writes a std::string.
void JsonWriteFastArray(const T *arr, Long64_t arrsize, const char *typname, void(TBufferJSON::*method)(const T *, Int_t, const char *))
Template method to write array of arbitrary dimensions Different methods can be used for store last a...
void * ReadObjectAny(const TClass *clCast) final
Read object from buffer. Only used from TBuffer.
static TObject * ConvertFromJSON(const char *str)
Read TObject-based class from JSON, produced by ConvertToJSON() method.
void ClassBegin(const TClass *, Version_t=-1) final
Should be called in the beginning of custom class streamer.
Int_t JsonReadArray(T *value)
Read static array from JSON - not used.
void IncrementLevel(TVirtualStreamerInfo *) final
Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions and indent new level in js...
void WriteLong(Long_t l) final
Writes Long_t value to buffer.
TString fValue
! buffer for current value
void WriteUInt(UInt_t i) final
Writes UInt_t value to buffer.
TJSONStackObj * Stack()
void ReadFloat(Float_t &f) final
Reads Float_t value from buffer.
static TString ConvertToJSON(const TObject *obj, Int_t compact=0, const char *member_name=nullptr)
Converts object, inherited from TObject class, to JSON string Lower digit of compact parameter define...
void WriteCharStar(char *s) final
Writes a char*.
void PerformPostProcessing(TJSONStackObj *stack, const TClass *obj_cl=nullptr)
Function is converts TObject and TString structures to more compact representation.
void ReadShort(Short_t &s) final
Reads Short_t value from buffer.
void JsonReadFastArray(T *arr, Int_t arrsize, bool asstring=false)
Template method to read array from the JSON.
TString StoreObject(const void *obj, const TClass *cl)
Store provided object as JSON structure Allows to configure different TBufferJSON properties before c...
std::deque< std::unique_ptr< TJSONStackObj > > fStack
! hierarchy of currently streamed element
void ReadChar(Char_t &c) final
Reads Char_t value from buffer.
static Int_t ExportToFile(const char *filename, const TObject *obj, const char *option=nullptr)
Convert object into JSON and store in text file Returns size of the produce file Used in TObject::Sav...
TString fNumericLocale
! stored value of setlocale(LC_NUMERIC), which should be recovered at the end
void SetTypeversionTag(const char *tag=nullptr)
Configures _typeversion tag in JSON One can specify name of the JSON tag like "_typeversion" or "$tv"...
TString fTypeVersionTag
! JSON member used to store class version, default empty
void ReadCharStar(char *&s) final
Reads a char* string.
UInt_t WriteVersion(const TClass *cl, Bool_t useBcnt=kFALSE) final
Ignored in TBufferJSON.
void ReadUShort(UShort_t &s) final
Reads UShort_t value from buffer.
TJSONStackObj * PushStack(Int_t inclevel=0, void *readnode=nullptr)
add new level to the structures stack
TBufferJSON(TBuffer::EMode mode=TBuffer::kWrite)
Creates buffer object to serialize data into json.
void JsonDisablePostprocessing()
disable post-processing of the code
void WorkWithElement(TStreamerElement *elem, Int_t)
This is call-back from streamer which indicates that class member will be streamed Name of element us...
void ReadCharP(Char_t *c) final
Reads array of characters from buffer.
void ReadUChar(UChar_t &c) final
Reads UChar_t value from buffer.
@ kStoreInfNaN
explicitly store special float numbers as strings ("inf", "nan")
Definition TBufferJSON.h:50
@ kBase64
all binary arrays will be compressed with base64 coding, supported by JSROOT
Definition TBufferJSON.h:46
@ kSkipTypeInfo
do not store typenames in JSON
Definition TBufferJSON.h:48
@ kNoSpaces
no new lines plus remove all spaces around "," and ":" symbols
Definition TBufferJSON.h:39
@ kMapAsObject
store std::map, std::unordered_map as JSON object
Definition TBufferJSON.h:41
@ kSameSuppression
zero suppression plus compress many similar values together
Definition TBufferJSON.h:45
void WriteUShort(UShort_t s) final
Writes UShort_t value to buffer.
unsigned fJsonrCnt
! counter for all objects, used for referencing
Int_t fArrayCompact
! 0 - no array compression, 1 - exclude leading/trailing zeros, 2 - check value repetition
void ReadFastArray(Bool_t *b, Int_t n) final
read array of Bool_t from buffer
void JsonReadBasic(T &value)
Template function to read basic value from JSON.
void JsonReadCollection(TCollection *obj, const TClass *objClass)
read content of ROOT collection
void JsonPushValue()
If value exists, push in the current stack for post-processing.
void WriteULong(ULong_t l) final
Writes ULong_t value to buffer.
void SetTypenameTag(const char *tag="_typename")
Configures _typename tag in JSON structures By default "_typename" field in JSON structures used to s...
TVirtualStreamerInfo * GetInfo() final
Return current streamer info element.
~TBufferJSON() override
destroy buffer
void JsonStartElement(const TStreamerElement *elem, const TClass *base_class)
Start new class member in JSON structures.
void DecrementLevel(TVirtualStreamerInfo *) final
Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions and decrease level in json...
void WriteFloat(Float_t f) final
Writes Float_t value to buffer.
Bool_t IsSkipClassInfo(const TClass *cl) const
Returns true if class info will be skipped from JSON.
void ReadLong(Long_t &l) final
Reads Long_t value from buffer.
void WriteClass(const TClass *cl) final
suppressed function of TBuffer
TClass * ReadClass(const TClass *cl=nullptr, UInt_t *objTag=nullptr) final
suppressed function of TBuffer
static TString zipJSON(const char *json)
zip JSON string and convert into base64 string to be used with JSROOT unzipJSON() function Main appli...
void ClassMember(const char *name, const char *typeName=nullptr, Int_t arrsize1=-1, Int_t arrsize2=-1) final
Method indicates name and typename of class member, which should be now streamed in custom streamer F...
TString * fOutput
! current output buffer for json code
TString fTypeNameTag
! JSON member used for storing class name, when empty - no class name will be stored
static void * ConvertFromJSONAny(const char *str, TClass **cl=nullptr)
Read object from JSON In class pointer (if specified) read class is returned One must specify expecte...
void ReadUInt(UInt_t &i) final
Reads UInt_t value from buffer.
void ReadLong64(Long64_t &l) final
Reads Long64_t value from buffer.
Bool_t fStoreInfNaN
! when true, store inf and nan as string, this is not portable for other JSON readers
Version_t ReadVersion(UInt_t *start=nullptr, UInt_t *bcnt=nullptr, const TClass *cl=nullptr) final
read version value from buffer
static void * ConvertFromJSONChecked(const char *str, const TClass *expectedClass)
Read objects from JSON, one can reuse existing object.
Int_t ReadStaticArray(Bool_t *b) final
Read array of Bool_t from buffer.
void WriteBool(Bool_t b) final
Writes Bool_t value to buffer.
void SetStreamerElementNumber(TStreamerElement *elem, Int_t comp_type) final
Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions and add/verify next elemen...
void WriteDouble(Double_t d) final
Writes Double_t value to buffer.
TString JsonWriteMember(const void *ptr, TDataMember *member, TClass *memberClass, Int_t arraylen)
Convert single data member to JSON structures Note; if data member described by 'member'is pointer,...
void ReadInt(Int_t &i) final
Reads Int_t value from buffer.
std::vector< const TClass * > fSkipClasses
! list of classes, which class info is not stored
void WriteCharP(const Char_t *c) final
Writes array of characters to buffer.
TString fArraySepar
! depending from compression level, ", " or ","
void SetSkipClassInfo(const TClass *cl)
Specify class which typename will not be stored in JSON Several classes can be configured To exclude ...
Int_t ReadArray(Bool_t *&b) final
Read array of Bool_t from buffer.
void WriteFastArray(const Bool_t *b, Long64_t n) final
Write array of Bool_t to buffer.
void AppendOutput(const char *line0, const char *line1=nullptr)
Append two string to the output JSON, normally separate by line break.
TString fOutBuffer
! main output buffer for json code
TJSONStackObj * PopStack()
remove one level from stack
void JsonWriteArrayCompress(const T *vname, Int_t arrsize, const char *typname)
void WriteInt(Int_t i) final
Writes Int_t value to buffer.
void WriteArray(const Bool_t *b, Int_t n) final
Write array of Bool_t to buffer.
void ReadBaseClass(void *start, TStreamerBase *elem) final
Read data of base class.
void ReadFastArrayString(Char_t *c, Int_t n) final
read array of Char_t from buffer
TJSONStackObj * JsonStartObjectWrite(const TClass *obj_class, TStreamerInfo *info=nullptr)
Start object element with typeinfo.
void ReadStdString(std::string *s) final
Reads a std::string.
void ReadDouble(Double_t &d) final
Reads Double_t value from buffer.
void ClassEnd(const TClass *) final
Should be called at the end of custom streamer See TBufferJSON::ClassBegin for more details.
Int_t JsonSpecialClass(const TClass *cl) const
return non-zero value when class has special handling in JSON it is TCollection (-130),...
void SkipObjectAny() final
Skip any kind of object from buffer.
void SetCompact(int level)
Set level of space/newline/array compression Lower digit of compact parameter define formatting rules...
Bool_t fMapAsObject
! when true, std::map will be converted into JSON object
void WriteUChar(UChar_t c) final
Writes UChar_t value to buffer.
void WriteTString(const TString &s) final
Writes a TString.
void JsonWriteConstChar(const char *value, Int_t len=-1, const char *=nullptr)
writes string value, processing all kind of special characters
void * RestoreObject(const char *str, TClass **cl)
Read object from JSON In class pointer (if specified) read class is returned One must specify expecte...
void WriteObjectClass(const void *actualObjStart, const TClass *actualClass, Bool_t cacheReuse) final
Write object to buffer. Only used from TBuffer.
void StreamObject(void *obj, const TClass *cl, const TClass *onFileClass=nullptr) final
stream object to/from buffer
void WriteLong64(Long64_t l) final
Writes Long64_t value to buffer.
void WriteFastArrayString(const Char_t *c, Long64_t n) final
Write array of Char_t to buffer.
void JsonReadTObjectMembers(TObject *obj, void *node=nullptr)
Read TObject data members from JSON.
void WriteULong64(ULong64_t l) final
Writes ULong64_t value to buffer.
void ReadBool(Bool_t &b) final
Reads Bool_t value from buffer.
void WriteChar(Char_t c) final
Writes Char_t value to buffer.
void JsonWriteObject(const void *obj, const TClass *objClass, Bool_t check_map=kTRUE)
Write object to buffer If object was written before, only pointer will be stored If check_map==kFALSE...
void * JsonReadObject(void *obj, const TClass *objClass=nullptr, TClass **readClass=nullptr)
Read object from current JSON node.
void WorkWithClass(TStreamerInfo *info, const TClass *cl=nullptr)
Prepares buffer to stream data of specified class.
void ReadTString(TString &s) final
Reads a TString.
Base class for text-based streamers like TBufferJSON or TBufferXML Special actions list will use meth...
Definition TBufferText.h:20
static const char * ConvertFloat(Float_t v, char *buf, unsigned len, Bool_t not_optimize=kFALSE)
convert float to string with configured format
static const char * ConvertDouble(Double_t v, char *buf, unsigned len, Bool_t not_optimize=kFALSE)
convert float to string with configured format
virtual void ReadBaseClass(void *start, TStreamerBase *elem)
Read data of base class.
@ kRead
Definition TBuffer.h:73
Bool_t IsWriting() const
Definition TBuffer.h:87
Bool_t IsReading() const
Definition TBuffer.h:86
Int_t Length() const
Definition TBuffer.h:100
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
ROOT::ESTLType GetCollectionType() const
Return the 'type' of the STL the TClass is representing.
Definition TClass.cxx:2891
Bool_t HasDictionary() const
Check whether a class has a dictionary or not.
Definition TClass.cxx:3933
void Destructor(void *obj, Bool_t dtorOnly=kFALSE)
Explicitly call destructor for object.
Definition TClass.cxx:5439
Int_t Size() const
Return size of object of this class.
Definition TClass.cxx:5743
Bool_t IsTObject() const
Return kTRUE is the class inherits from TObject.
Definition TClass.cxx:5980
Int_t GetBaseClassOffset(const TClass *toBase, void *address=nullptr, bool isDerivedObject=true)
Definition TClass.cxx:2796
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition TClass.cxx:2902
Version_t GetClassVersion() const
Definition TClass.h:432
TClass * GetActualClass(const void *object) const
Return a pointer to the real class of the object.
Definition TClass.cxx:2612
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2973
An array of clone (identical) objects.
static TClass * Class()
Collection abstract base class.
Definition TCollection.h:65
static TClass * Class()
void SetName(const char *name)
const char * GetName() const override
Return name of this collection.
virtual void Add(TObject *obj)=0
All ROOT classes may have RTTI (run time type identification) support added.
Definition TDataMember.h:31
Basic data type descriptor (datatype information is obtained from CINT).
Definition TDataType.h:44
Bool_t IsJsonString()
TJSONStackObj()=default
keep actual class version, workaround for ReadVersion in custom streamer
Int_t PopIntValue()
nlohmann::json * GetStlNode()
Bool_t AssignStl(TClass *cl, Int_t map_convert, const char *typename_tag)
Bool_t fIsPostProcessed
Bool_t IsStreamerInfo() const
Bool_t fIsStreamerInfo
element in streamer info
void PushValue(TString &v)
Bool_t IsStl() const
TStreamerInfo * fInfo
~TJSONStackObj() override
Bool_t IsStreamerElement() const
std::unique_ptr< TArrayIndexProducer > MakeReadIndexes()
int fMemberCnt
raw values
nlohmann::json * fNode
producer of ndim indexes
int * fMemberPtr
count number of object members, normally _typename is first member
std::vector< std::string > fValues
enable base64 coding when writing array
Bool_t fAccObjects
indicate that object writing started, should be closed in postprocess
std::unique_ptr< StlRead > fStlRead
JSON node, used for reading.
Version_t fClVersion
custom structure for stl container reading
void PushIntValue(Int_t v)
Int_t fLevel
pointer on members counter, can be inherit from parent stack objects
std::unique_ptr< TArrayIndexProducer > fIndx
indent level
TStreamerElement * fElem
Int_t IsJsonArray(nlohmann::json *json=nullptr, const char *map_convert_type=nullptr)
checks if specified JSON node is array (compressed or not compressed) returns length of array (or -1 ...
Bool_t fIsObjStarted
indicate that value is written
Bool_t fBase64
if true, accumulate whole objects in values
const char * NextMemberSeparator()
returns separator for data members
A doubly linked list.
Definition TList.h:38
static TClass * Class()
TMap implements an associative array of (key,value) pairs using a THashTable for efficient retrieval ...
Definition TMap.h:40
void Add(TObject *obj) override
This function may not be used (but we need to provide it since it is a pure virtual in TCollection).
Definition TMap.cxx:53
static TClass * Class()
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:50
Mother of all ROOT objects.
Definition TObject.h:41
@ kIsOnHeap
object is on heap
Definition TObject.h:87
@ kNotDeleted
object has not been deleted
Definition TObject.h:88
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:1057
static TClass * Class()
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:543
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1071
virtual void Fatal(const char *method, const char *msgfmt,...) const
Issue fatal error message.
Definition TObject.cxx:1099
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1045
The TRealData class manages the effective list of all data members for a given class.
Definition TRealData.h:30
static TClass * Class()
Describe one element (data member) to be Streamed.
Int_t GetType() const
Int_t GetArrayDim() const
virtual Bool_t IsBase() const
Return kTRUE if the element represent a base class.
Describes a persistent version of a class.
Basic string class.
Definition TString.h:138
Ssiz_t Length() const
Definition TString.h:425
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1994
void Clear()
Clear string without changing its capacity.
Definition TString.cxx:1241
const char * Data() const
Definition TString.h:384
Ssiz_t Capacity() const
Definition TString.h:372
TString & Append(const char *cs)
Definition TString.h:581
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2384
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition TString.cxx:2362
static TClass * Class()
Abstract Interface class describing Streamer information for one class.
static Bool_t CanDelete()
static function returning true if ReadBuffer can delete object
const Int_t n
Definition legend1.C:16
@ kSTLend
Definition ESTLType.h:47
@ kSTLvector
Definition ESTLType.h:30
@ kSTLlist
Definition ESTLType.h:31
@ kSTLforwardlist
Definition ESTLType.h:41
@ kNotSTL
Definition ESTLType.h:29
@ kUnorderedMultiSet
Definition TClassEdit.h:105
@ kUnorderedMultiMap
Definition TClassEdit.h:107
bool IsStdClass(const char *type)
return true if the class belongs to the std namespace
@ kDefaultZLIB
Compression level reserved for ZLIB compression algorithm (fastest compression)
Definition Compression.h:74
const char * fTypeTag
iterator for std::map stored as JSON object
nlohmann::json fValue
type tag used for std::map stored as JSON object
Bool_t fFirst
special iterator over STL map::key members
nlohmann::json * GetStlNode(nlohmann::json *prnt)
temporary value reading std::map as JSON
nlohmann::json::iterator fIter
is first or second element is used in the pair
Int_t fMap
index of object in STL container
TLine l
Definition textangle.C:4