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
568
569 return TBase64::Encode(buf.data(), nout);
570}
571
572////////////////////////////////////////////////////////////////////////////////
573/// Set level of space/newline/array compression
574/// Lower digit of compact parameter define formatting rules
575/// - kNoCompress = 0 - no any compression, human-readable form
576/// - kNoIndent = 1 - remove indentation spaces in the begin of each line
577/// - kNoNewLine = 2 - remove also newlines
578/// - kNoSpaces = 3 - exclude all spaces and new lines
579///
580/// Second digit of compact parameter defines algorithm for arrays compression
581/// - 0 - no compression, standard JSON array
582/// - kZeroSuppression = 10 - exclude leading and trailing zeros
583/// - kSameSuppression = 20 - check values repetition and empty gaps
584///
585/// Third digit defines usage of typeinfo
586/// - kSkipTypeInfo = 100 - "_typename" field will be skipped, reading by ROOT or JSROOT may be impossible
587///
588/// Fourth digit (1 or 0) defines whether to set kStoreInfNaN
589
591{
592 if (level < 0)
593 level = 0;
594 fCompact = level % 10;
595 if (fCompact >= kMapAsObject) {
598 }
599 fSemicolon = (fCompact >= kNoSpaces) ? ":" : " : ";
600 fArraySepar = (fCompact >= kNoSpaces) ? "," : ", ";
601 fArrayCompact = ((level / 10) % 10) * 10;
602 if ((((level / 100) % 10) * 100) == kSkipTypeInfo)
604 else if (fTypeNameTag.Length() == 0)
605 fTypeNameTag = "_typename";
606 fStoreInfNaN = ((((level / 1000) % 10) * 1000) == kStoreInfNaN);
607}
608
609////////////////////////////////////////////////////////////////////////////////
610/// Configures _typename tag in JSON structures
611/// By default "_typename" field in JSON structures used to store class information
612/// One can specify alternative tag like "$typename" or "xy", but such JSON can not be correctly used in JSROOT
613/// If empty string is provided, class information will not be stored
614
615void TBufferJSON::SetTypenameTag(const char *tag)
616{
617 if (!tag)
619 else
620 fTypeNameTag = tag;
621}
622
623////////////////////////////////////////////////////////////////////////////////
624/// Configures _typeversion tag in JSON
625/// One can specify name of the JSON tag like "_typeversion" or "$tv" which will be used to store class version
626/// Such tag can be used to correctly recover objects from JSON
627/// If empty string is provided (default), class version will not be stored
628
630{
631 if (!tag)
633 else
634 fTypeVersionTag = tag;
635}
636
637////////////////////////////////////////////////////////////////////////////////
638/// Specify class which typename will not be stored in JSON
639/// Several classes can be configured
640/// To exclude typeinfo for all classes, call TBufferJSON::SetTypenameTag("")
641
643{
644 if (cl && (std::find(fSkipClasses.begin(), fSkipClasses.end(), cl) == fSkipClasses.end()))
645 fSkipClasses.emplace_back(cl);
646}
647
648////////////////////////////////////////////////////////////////////////////////
649/// Returns true if class info will be skipped from JSON
650
652{
653 return cl && (std::find(fSkipClasses.begin(), fSkipClasses.end(), cl) != fSkipClasses.end());
654}
655
656////////////////////////////////////////////////////////////////////////////////
657/// Converts any type of object to JSON string
658/// One should provide pointer on object and its class name
659/// Lower digit of compact parameter define formatting rules
660/// - TBufferJSON::kNoCompress (0) - no any compression, human-readable form
661/// - TBufferJSON::kNoIndent (1) - exclude spaces in the begin
662/// - TBufferJSON::kNoNewLine (2) - no indent and no newlines
663/// - TBufferJSON::kNoSpaces (3) - exclude spaces as much as possible
664/// Second digit of compact parameter defines algorithm for arrays compression
665/// - 0 - no compression, standard JSON array
666/// - TBufferJSON::kZeroSuppression (10) - exclude leading and trailing zeros
667/// - TBufferJSON::kSameSuppression (20) - check values repetition and empty gaps
668/// - TBufferJSON::kBase64 (30) - arrays will be coded with base64 coding
669/// Third digit of compact parameter defines typeinfo storage:
670/// - TBufferJSON::kSkipTypeInfo (100) - "_typename" will be skipped, not always can be read back
671/// Fourth digit: (1 or 0) defines whether to set kStoreInfNaN (1000) - inf and nan to be stored as string
672/// Maximal none-destructive compression can be achieved when
673/// compact parameter equal to TBufferJSON::kNoSpaces + TBufferJSON::kSameSuppression
674/// When member_name specified, converts only this data member
675
676TString TBufferJSON::ConvertToJSON(const void *obj, const TClass *cl, Int_t compact, const char *member_name)
677{
678 if (!cl) {
679 ::Error("TBufferJSON::ConvertToJSON", "Unknown class (probably missing dictionary).");
680 return TString();
681 }
682 TClass *clActual = obj ? cl->GetActualClass(obj) : nullptr;
683 const void *actualStart = obj;
684 if (clActual && (clActual != cl)) {
685 actualStart = (char *)obj - clActual->GetBaseClassOffset(cl);
686 } else {
687 // We could not determine the real type of this object,
688 // let's assume it is the one given by the caller.
689 clActual = const_cast<TClass *>(cl);
690 }
691
692 if (member_name && actualStart) {
693 TRealData *rdata = clActual->GetRealData(member_name);
694 TDataMember *member = rdata ? rdata->GetDataMember() : nullptr;
695 if (!member) {
696 TIter iter(clActual->GetListOfRealData());
697 while ((rdata = dynamic_cast<TRealData *>(iter())) != nullptr) {
698 member = rdata->GetDataMember();
699 if (member && strcmp(member->GetName(), member_name) == 0)
700 break;
701 }
702 }
703 if (!member)
704 return TString();
705
706 Int_t arraylen = -1;
707 if (member->GetArrayIndex() != 0) {
708 TRealData *idata = clActual->GetRealData(member->GetArrayIndex());
709 TDataMember *imember = idata ? idata->GetDataMember() : nullptr;
710 if (imember && (strcmp(imember->GetTrueTypeName(), "int") == 0)) {
711 arraylen = *((int *)((char *)actualStart + idata->GetThisOffset()));
712 }
713 }
714
715 void *ptr = (char *)actualStart + rdata->GetThisOffset();
716 if (member->IsaPointer())
717 ptr = *((char **)ptr);
718
720 }
721
722 TBufferJSON buf;
723
724 buf.SetCompact(compact);
725
726 return buf.StoreObject(actualStart, clActual);
727}
728
729////////////////////////////////////////////////////////////////////////////////
730/// Store provided object as JSON structure
731/// Allows to configure different TBufferJSON properties before converting object into JSON
732/// Actual object class must be specified here
733/// Method can be safely called once - after that TBufferJSON instance must be destroyed
734/// Code should look like:
735///
736/// auto obj = new UserClass();
737/// TBufferJSON buf;
738/// buf.SetCompact(TBufferJSON::kNoSpaces); // change any other settings in TBufferJSON
739/// auto json = buf.StoreObject(obj, TClass::GetClass<UserClass>());
740///
741
742TString TBufferJSON::StoreObject(const void *obj, const TClass *cl)
743{
744 if (IsWriting()) {
745
746 InitMap();
747
748 PushStack(); // dummy stack entry to avoid extra checks in the beginning
749
750 JsonWriteObject(obj, cl);
751
752 PopStack();
753 } else {
754 Error("StoreObject", "Can not store object into TBuffer for reading");
755 }
756
757 return fOutBuffer.Length() ? fOutBuffer : fValue;
758}
759
760////////////////////////////////////////////////////////////////////////////////
761/// Converts selected data member into json
762/// \param ptr specifies address in memory, where data member is located.
763/// \note if data member described by `member` is pointer, `ptr` should be the
764/// value of the pointer, not the address of the pointer.
765/// \param compact defines compactness of produced JSON. See
766/// TBufferJSON::SetCompact for more details
767/// \param arraylen (when specified) is array length for this data member, //[fN] case
768
770{
771 if (!ptr || !member)
772 return TString("null");
773
774 Bool_t stlstring = !strcmp(member->GetTrueTypeName(), "string");
775
776 Int_t isstl = member->IsSTLContainer();
777
778 TClass *mcl = member->IsBasic() ? nullptr : gROOT->GetClass(member->GetTypeName());
779
780 if (mcl && (mcl != TString::Class()) && !stlstring && !isstl && (mcl->GetBaseClassOffset(TArray::Class()) != 0) &&
781 (arraylen <= 0) && (member->GetArrayDim() == 0))
783
784 TBufferJSON buf;
785
786 buf.SetCompact(compact);
787
788 return buf.JsonWriteMember(ptr, member, mcl, arraylen);
789}
790
791////////////////////////////////////////////////////////////////////////////////
792/// Convert object into JSON and store in text file
793/// Returns size of the produce file
794/// Used in TObject::SaveAs()
795
796Int_t TBufferJSON::ExportToFile(const char *filename, const TObject *obj, const char *option)
797{
798 if (!obj || !filename || (*filename == 0))
799 return 0;
800
801 Int_t compact = strstr(filename, ".json.gz") ? 3 : 0;
802 if (option && (*option >= '0') && (*option <= '3'))
804
806
807 std::ofstream ofs(filename);
808
809 if (strstr(filename, ".json.gz")) {
810 const char *objbuf = json.Data();
811 Long_t objlen = json.Length();
812
813 unsigned long objcrc = R__crc32(0, nullptr, 0);
814 objcrc = R__crc32(objcrc, (const unsigned char *)objbuf, objlen);
815
816 // 10 bytes (ZIP header), compressed data, 8 bytes (CRC and original length)
817 Int_t buflen = 10 + objlen + 8;
818 if (buflen < 512)
819 buflen = 512;
820
821 char *buffer = (char *)malloc(buflen);
822 if (!buffer)
823 return 0; // failure
824
825 char *bufcur = buffer;
826
827 *bufcur++ = 0x1f; // first byte of ZIP identifier
828 *bufcur++ = 0x8b; // second byte of ZIP identifier
829 *bufcur++ = 0x08; // compression method
830 *bufcur++ = 0x00; // FLAG - empty, no any file names
831 *bufcur++ = 0; // empty timestamp
832 *bufcur++ = 0; //
833 *bufcur++ = 0; //
834 *bufcur++ = 0; //
835 *bufcur++ = 0; // XFL (eXtra FLags)
836 *bufcur++ = 3; // OS 3 means Unix
837 // strcpy(bufcur, "item.json");
838 // bufcur += strlen("item.json")+1;
839
840 char dummy[8];
841 memcpy(dummy, bufcur - 6, 6);
842
843 // R__memcompress fills first 6 bytes with own header, therefore just overwrite them
844 unsigned long ziplen = R__memcompress(bufcur - 6, objlen + 6, (char *)objbuf, objlen);
845
846 memcpy(bufcur - 6, dummy, 6);
847
848 bufcur += (ziplen - 6); // jump over compressed data (6 byte is extra ROOT header)
849
850 *bufcur++ = objcrc & 0xff; // CRC32
851 *bufcur++ = (objcrc >> 8) & 0xff;
852 *bufcur++ = (objcrc >> 16) & 0xff;
853 *bufcur++ = (objcrc >> 24) & 0xff;
854
855 *bufcur++ = objlen & 0xff; // original data length
856 *bufcur++ = (objlen >> 8) & 0xff; // original data length
857 *bufcur++ = (objlen >> 16) & 0xff; // original data length
858 *bufcur++ = (objlen >> 24) & 0xff; // original data length
859
860 ofs.write(buffer, bufcur - buffer);
861
862 free(buffer);
863 } else {
864 ofs << json.Data();
865 }
866
867 ofs.close();
868
869 return json.Length();
870}
871
872////////////////////////////////////////////////////////////////////////////////
873/// Convert object into JSON and store in text file
874/// Returns size of the produce file
875
876Int_t TBufferJSON::ExportToFile(const char *filename, const void *obj, const TClass *cl, const char *option)
877{
878 if (!obj || !cl || !filename || (*filename == 0))
879 return 0;
880
881 Int_t compact = strstr(filename, ".json.gz") ? 3 : 0;
882 if (option && (*option >= '0') && (*option <= '3'))
884
886
887 std::ofstream ofs(filename);
888
889 if (strstr(filename, ".json.gz")) {
890 const char *objbuf = json.Data();
891 Long_t objlen = json.Length();
892
893 unsigned long objcrc = R__crc32(0, nullptr, 0);
894 objcrc = R__crc32(objcrc, (const unsigned char *)objbuf, objlen);
895
896 // 10 bytes (ZIP header), compressed data, 8 bytes (CRC and original length)
897 Int_t buflen = 10 + objlen + 8;
898 if (buflen < 512)
899 buflen = 512;
900
901 char *buffer = (char *)malloc(buflen);
902 if (!buffer)
903 return 0; // failure
904
905 char *bufcur = buffer;
906
907 *bufcur++ = 0x1f; // first byte of ZIP identifier
908 *bufcur++ = 0x8b; // second byte of ZIP identifier
909 *bufcur++ = 0x08; // compression method
910 *bufcur++ = 0x00; // FLAG - empty, no any file names
911 *bufcur++ = 0; // empty timestamp
912 *bufcur++ = 0; //
913 *bufcur++ = 0; //
914 *bufcur++ = 0; //
915 *bufcur++ = 0; // XFL (eXtra FLags)
916 *bufcur++ = 3; // OS 3 means Unix
917 // strcpy(bufcur, "item.json");
918 // bufcur += strlen("item.json")+1;
919
920 char dummy[8];
921 memcpy(dummy, bufcur - 6, 6);
922
923 // R__memcompress fills first 6 bytes with own header, therefore just overwrite them
924 unsigned long ziplen = R__memcompress(bufcur - 6, objlen + 6, (char *)objbuf, objlen);
925
926 memcpy(bufcur - 6, dummy, 6);
927
928 bufcur += (ziplen - 6); // jump over compressed data (6 byte is extra ROOT header)
929
930 *bufcur++ = objcrc & 0xff; // CRC32
931 *bufcur++ = (objcrc >> 8) & 0xff;
932 *bufcur++ = (objcrc >> 16) & 0xff;
933 *bufcur++ = (objcrc >> 24) & 0xff;
934
935 *bufcur++ = objlen & 0xff; // original data length
936 *bufcur++ = (objlen >> 8) & 0xff; // original data length
937 *bufcur++ = (objlen >> 16) & 0xff; // original data length
938 *bufcur++ = (objlen >> 24) & 0xff; // original data length
939
940 ofs.write(buffer, bufcur - buffer);
941
942 free(buffer);
943 } else {
944 ofs << json.Data();
945 }
946
947 ofs.close();
948
949 return json.Length();
950}
951
952////////////////////////////////////////////////////////////////////////////////
953/// Read TObject-based class from JSON, produced by ConvertToJSON() method.
954/// If object does not inherit from TObject class, return 0.
955
957{
958 TClass *cl = nullptr;
959 void *obj = ConvertFromJSONAny(str, &cl);
960
961 if (!cl || !obj)
962 return nullptr;
963
965
966 if (delta < 0) {
967 cl->Destructor(obj);
968 return nullptr;
969 }
970
971 return (TObject *)(((char *)obj) + delta);
972}
973
974////////////////////////////////////////////////////////////////////////////////
975/// Read object from JSON
976/// In class pointer (if specified) read class is returned
977/// One must specify expected object class, if it is TArray or STL container
978
979void *TBufferJSON::ConvertFromJSONAny(const char *str, TClass **cl)
980{
982
983 return buf.RestoreObject(str, cl);
984}
985
986////////////////////////////////////////////////////////////////////////////////
987/// Read object from JSON
988/// In class pointer (if specified) read class is returned
989/// One must specify expected object class, if it is TArray or STL container
990
992{
993 if (!IsReading())
994 return nullptr;
995
996 nlohmann::json docu = nlohmann::json::parse(json_str);
997
998 if (docu.is_null() || (!docu.is_object() && !docu.is_array()))
999 return nullptr;
1000
1001 TClass *objClass = nullptr;
1002
1003 if (cl) {
1004 objClass = *cl; // this is class which suppose to created when reading JSON
1005 *cl = nullptr;
1006 }
1007
1008 InitMap();
1009
1010 PushStack(0, &docu);
1011
1012 void *obj = JsonReadObject(nullptr, objClass, cl);
1013
1014 PopStack();
1015
1016 return obj;
1017}
1018
1019////////////////////////////////////////////////////////////////////////////////
1020/// Read objects from JSON, one can reuse existing object
1021
1023{
1024 if (!expectedClass)
1025 return nullptr;
1026
1027 TClass *resClass = const_cast<TClass *>(expectedClass);
1028
1029 void *res = ConvertFromJSONAny(str, &resClass);
1030
1031 if (!res || !resClass)
1032 return nullptr;
1033
1034 if (resClass == expectedClass)
1035 return res;
1036
1037 Int_t offset = resClass->GetBaseClassOffset(expectedClass);
1038 if (offset < 0) {
1039 ::Error("TBufferJSON::ConvertFromJSONChecked", "expected class %s is not base for read class %s",
1040 expectedClass->GetName(), resClass->GetName());
1041 resClass->Destructor(res);
1042 return nullptr;
1043 }
1044
1045 return (char *)res - offset;
1046}
1047
1048////////////////////////////////////////////////////////////////////////////////
1049/// Convert single data member to JSON structures
1050/// Note; if data member described by 'member'is pointer, `ptr` should be the
1051/// value of the pointer, not the address of the pointer.
1052/// Returns string with converted member
1053
1055{
1056 if (!member)
1057 return "null";
1058
1059 if (gDebug > 2)
1060 Info("JsonWriteMember", "Write member %s type %s ndim %d", member->GetName(), member->GetTrueTypeName(),
1061 member->GetArrayDim());
1062
1063 Int_t tid = member->GetDataType() ? member->GetDataType()->GetType() : kNoType_t;
1064 if (strcmp(member->GetTrueTypeName(), "const char*") == 0)
1065 tid = kCharStar;
1066 else if (!member->IsBasic() || (tid == kOther_t) || (tid == kVoid_t))
1067 tid = kNoType_t;
1068
1069 if (!ptr)
1070 return (tid == kCharStar) ? "\"\"" : "null";
1071
1072 PushStack(0);
1073 fValue.Clear();
1074
1075 if (tid != kNoType_t) {
1076
1078
1079 Int_t shift = 1;
1080
1081 if (indx.IsArray() && (tid == kChar_t))
1082 shift = indx.ReduceDimension();
1083
1084 auto unitSize = member->GetUnitSize();
1085 char *ppp = (char *)ptr;
1086 if (member->IsaPointer()) {
1087 // UnitSize was the sizeof(void*)
1088 assert(member->GetDataType());
1089 unitSize = member->GetDataType()->Size();
1090 }
1091
1092 if (indx.IsArray())
1093 fOutBuffer.Append(indx.GetBegin());
1094
1095 do {
1096 fValue.Clear();
1097
1098 switch (tid) {
1099 case kChar_t:
1100 if (shift > 1)
1101 JsonWriteConstChar((Char_t *)ppp, shift);
1102 else
1103 JsonWriteBasic(*((Char_t *)ppp));
1104 break;
1105 case kShort_t: JsonWriteBasic(*((Short_t *)ppp)); break;
1106 case kInt_t: JsonWriteBasic(*((Int_t *)ppp)); break;
1107 case kLong_t: JsonWriteBasic(*((Long_t *)ppp)); break;
1108 case kFloat_t: JsonWriteBasic(*((Float_t *)ppp)); break;
1109 case kCounter: JsonWriteBasic(*((Int_t *)ppp)); break;
1110 case kCharStar: JsonWriteConstChar((Char_t *)ppp); break;
1111 case kDouble_t: JsonWriteBasic(*((Double_t *)ppp)); break;
1112 case kDouble32_t: JsonWriteBasic(*((Double_t *)ppp)); break;
1113 case kchar: JsonWriteBasic(*((char *)ppp)); break;
1114 case kUChar_t: JsonWriteBasic(*((UChar_t *)ppp)); break;
1115 case kUShort_t: JsonWriteBasic(*((UShort_t *)ppp)); break;
1116 case kUInt_t: JsonWriteBasic(*((UInt_t *)ppp)); break;
1117 case kULong_t: JsonWriteBasic(*((ULong_t *)ppp)); break;
1118 case kBits: JsonWriteBasic(*((UInt_t *)ppp)); break;
1119 case kLong64_t: JsonWriteBasic(*((Long64_t *)ppp)); break;
1120 case kULong64_t: JsonWriteBasic(*((ULong64_t *)ppp)); break;
1121 case kBool_t: JsonWriteBasic(*((Bool_t *)ppp)); break;
1122 case kFloat16_t: JsonWriteBasic(*((Float_t *)ppp)); break;
1123 case kOther_t:
1124 case kVoid_t: break;
1125 }
1126
1128 if (indx.IsArray())
1129 fOutBuffer.Append(indx.NextSeparator());
1130
1131 ppp += shift * unitSize;
1132
1133 } while (!indx.IsDone());
1134
1136
1137 } else if (memberClass == TString::Class()) {
1138 TString *str = (TString *)ptr;
1139 JsonWriteConstChar(str ? str->Data() : nullptr);
1140 } else if ((member->IsSTLContainer() == ROOT::kSTLvector) || (member->IsSTLContainer() == ROOT::kSTLlist) ||
1141 (member->IsSTLContainer() == ROOT::kSTLforwardlist)) {
1142
1143 if (memberClass)
1144 memberClass->Streamer((void *)ptr, *this);
1145 else
1146 fValue = "[]";
1147
1148 if (fValue == "0")
1149 fValue = "[]";
1150
1151 } else if (memberClass && memberClass->GetBaseClassOffset(TArray::Class()) == 0) {
1152 TArray *arr = (TArray *)ptr;
1153 if (arr && (arr->GetSize() > 0)) {
1154 arr->Streamer(*this);
1155 // WriteFastArray(arr->GetArray(), arr->GetSize());
1156 if (Stack()->fValues.size() > 1) {
1157 Warning("TBufferJSON", "When streaming TArray, more than 1 object in the stack, use second item");
1158 fValue = Stack()->fValues[1].c_str();
1159 }
1160 } else
1161 fValue = "[]";
1162 } else if (memberClass && !strcmp(memberClass->GetName(), "string")) {
1163 // here value contains quotes, stack can be ignored
1164 memberClass->Streamer((void *)ptr, *this);
1165 }
1166 PopStack();
1167
1168 if (fValue.Length())
1169 return fValue;
1170
1171 if (!memberClass || (member->GetArrayDim() > 0) || (arraylen > 0))
1172 return "<not supported>";
1173
1175}
1176
1177////////////////////////////////////////////////////////////////////////////////
1178/// add new level to the structures stack
1179
1181{
1182 auto next = new TJSONStackObj();
1183 next->fLevel = inclevel;
1184 if (IsReading()) {
1185 next->fNode = (nlohmann::json *)readnode;
1186 } else if (fStack.size() > 0) {
1187 auto prev = Stack();
1188 next->fLevel += prev->fLevel;
1189 next->fMemberPtr = prev->fMemberPtr;
1190 }
1191 fStack.emplace_back(next);
1192 return next;
1193}
1194
1195////////////////////////////////////////////////////////////////////////////////
1196/// remove one level from stack
1197
1199{
1200 if (fStack.size() > 0)
1201 fStack.pop_back();
1202
1203 return fStack.size() > 0 ? fStack.back().get() : nullptr;
1204}
1205
1206////////////////////////////////////////////////////////////////////////////////
1207/// Append two string to the output JSON, normally separate by line break
1208
1209void TBufferJSON::AppendOutput(const char *line0, const char *line1)
1210{
1211 if (line0)
1213
1214 if (line1) {
1215 if (fCompact < 2)
1216 fOutput->Append("\n");
1217
1218 if (strlen(line1) > 0) {
1219 if (fCompact < 1) {
1220 if (Stack()->fLevel > 0)
1221 fOutput->Append(' ', Stack()->fLevel);
1222 }
1223 fOutput->Append(line1);
1224 }
1225 }
1226}
1227
1228////////////////////////////////////////////////////////////////////////////////
1229/// Start object element with typeinfo
1230
1232{
1233 auto stack = PushStack(2);
1234
1235 // new object started - assign own member counter
1236 stack->fMemberPtr = &stack->fMemberCnt;
1237
1238 if ((fTypeNameTag.Length() > 0) && !IsSkipClassInfo(obj_class)) {
1239 // stack->fMemberCnt = 1; // default value, comment out here
1240 AppendOutput("{", "\"");
1242 AppendOutput("\"");
1244 AppendOutput("\"");
1245 AppendOutput(obj_class->GetName());
1246 AppendOutput("\"");
1247 if (fTypeVersionTag.Length() > 0) {
1248 AppendOutput(stack->NextMemberSeparator(), "\"");
1250 AppendOutput("\"");
1252 AppendOutput(TString::Format("%d", (int)(info ? info->GetClassVersion() : obj_class->GetClassVersion())));
1253 }
1254 } else {
1255 stack->fMemberCnt = 0; // exclude typename
1256 AppendOutput("{");
1257 }
1258
1259 return stack;
1260}
1261
1262////////////////////////////////////////////////////////////////////////////////
1263/// Start new class member in JSON structures
1264
1266{
1267 const char *elem_name = nullptr;
1269
1270 switch (special_kind) {
1271 case 0:
1272 if (base_class) return;
1273 elem_name = elem->GetName();
1274 if (strcmp(elem_name,"fLineStyle") == 0)
1275 if ((strcmp(elem->GetTypeName(),"TString") == 0) && (strcmp(elem->GetFullName(),"fLineStyle[30]") == 0)) {
1276 auto st1 = fStack.at(fStack.size() - 2).get();
1277 if (st1->IsStreamerInfo() && st1->fInfo && (strcmp(st1->fInfo->GetName(),"TStyle") == 0))
1278 elem_name = "fLineStyles";
1279 }
1280 break;
1281 case TClassEdit::kVector: elem_name = "fVector"; break;
1282 case TClassEdit::kList: elem_name = "fList"; break;
1283 case TClassEdit::kForwardlist: elem_name = "fForwardlist"; break;
1284 case TClassEdit::kDeque: elem_name = "fDeque"; break;
1285 case TClassEdit::kMap: elem_name = "fMap"; break;
1286 case TClassEdit::kMultiMap: elem_name = "fMultiMap"; break;
1287 case TClassEdit::kSet: elem_name = "fSet"; break;
1288 case TClassEdit::kMultiSet: elem_name = "fMultiSet"; break;
1289 case TClassEdit::kUnorderedSet: elem_name = "fUnorderedSet"; break;
1290 case TClassEdit::kUnorderedMultiSet: elem_name = "fUnorderedMultiSet"; break;
1291 case TClassEdit::kUnorderedMap: elem_name = "fUnorderedMap"; break;
1292 case TClassEdit::kUnorderedMultiMap: elem_name = "fUnorderedMultiMap"; break;
1293 case TClassEdit::kBitSet: elem_name = "fBitSet"; break;
1294 case json_TArray: elem_name = "fArray"; break;
1295 case json_TString:
1296 case json_stdstring: elem_name = "fString"; break;
1297 }
1298
1299 if (!elem_name)
1300 return;
1301
1302 if (IsReading()) {
1303 nlohmann::json *json = Stack()->fNode;
1304
1305 if (json->count(elem_name) != 1) {
1306 Error("JsonStartElement", "Missing JSON structure for element %s", elem_name);
1307 } else {
1308 Stack()->fNode = &((*json)[elem_name]);
1309 if (special_kind == json_TArray) {
1310 Int_t len = Stack()->IsJsonArray();
1311 Stack()->PushIntValue(len > 0 ? len : 0);
1312 if (len < 0)
1313 Error("JsonStartElement", "Missing array when reading TArray class for element %s", elem->GetName());
1314 }
1315 if ((gDebug > 1) && base_class)
1316 Info("JsonStartElement", "Reading baseclass %s from element %s", base_class->GetName(), elem_name);
1317 }
1318
1319 } else {
1320 AppendOutput(Stack()->NextMemberSeparator(), "\"");
1322 AppendOutput("\"");
1324 }
1325}
1326
1327////////////////////////////////////////////////////////////////////////////////
1328/// disable post-processing of the code
1333
1334////////////////////////////////////////////////////////////////////////////////
1335/// return non-zero value when class has special handling in JSON
1336/// it is TCollection (-130), TArray (100), TString (110), std::string (120) and STL containers (1..6)
1337
1339{
1340 if (!cl)
1341 return 0;
1342
1343 Bool_t isarray = strncmp("TArray", cl->GetName(), 6) == 0;
1344 if (isarray)
1345 isarray = (const_cast<TClass *>(cl))->GetBaseClassOffset(TArray::Class()) == 0;
1346 if (isarray)
1347 return json_TArray;
1348
1349 // negative value used to indicate that collection stored as object
1350 if ((const_cast<TClass *>(cl))->GetBaseClassOffset(TCollection::Class()) == 0)
1351 return json_TCollection;
1352
1353 // special case for TString - it is saved as string in JSON
1354 if (cl == TString::Class())
1355 return json_TString;
1356
1357 bool isstd = TClassEdit::IsStdClass(cl->GetName());
1359 if (isstd)
1361 if (isstlcont > 0)
1362 return isstlcont;
1363
1364 // also special handling for STL string, which handled similar to TString
1365 if (isstd && !strcmp(cl->GetName(), "string"))
1366 return json_stdstring;
1367
1368 return 0;
1369}
1370
1371////////////////////////////////////////////////////////////////////////////////
1372/// Write object to buffer
1373/// If object was written before, only pointer will be stored
1374/// If check_map==kFALSE, object will be stored in any case and pointer will not be registered in the map
1375
1376void TBufferJSON::JsonWriteObject(const void *obj, const TClass *cl, Bool_t check_map)
1377{
1378 if (!cl)
1379 obj = nullptr;
1380
1381 if (gDebug > 0)
1382 Info("JsonWriteObject", "Object %p class %s check_map %s", obj, cl ? cl->GetName() : "null",
1383 check_map ? "true" : "false");
1384
1386
1388
1389 TJSONStackObj *stack = Stack();
1390
1391 if (stack && stack->fAccObjects && ((fValue.Length() > 0) || (stack->fValues.size() > 0))) {
1392 // accumulate data of super-object in stack
1393
1394 if (fValue.Length() > 0)
1395 stack->PushValue(fValue);
1396
1397 // redirect output to local buffer, use it later as value
1400 } else if ((special_kind <= 0) || (special_kind > json_TArray)) {
1401 // FIXME: later post processing should be active for all special classes, while they all keep output in the value
1405
1406 if ((fMapAsObject && (fStack.size()==1)) || (stack && stack->fElem && strstr(stack->fElem->GetTitle(), "JSON_object")))
1407 map_convert = 2; // mapped into normal object
1408 else
1409 map_convert = 1;
1410
1411 if (!cl->HasDictionary()) {
1412 Error("JsonWriteObject", "Cannot stream class %s without dictionary", cl->GetName());
1413 AppendOutput(map_convert == 1 ? "[]" : "null");
1414 goto post_process;
1415 }
1416 }
1417
1418 if (!obj) {
1419 AppendOutput("null");
1420 goto post_process;
1421 }
1422
1423 if (special_kind <= 0) {
1424 // add element name which should correspond to the object
1425 if (check_map) {
1427 if (refid > 0) {
1428 // old-style refs, coded into string like "$ref12"
1429 // AppendOutput(TString::Format("\"$ref:%u\"", iter->second));
1430 // new-style refs, coded into extra object {"$ref":12}, auto-detected by JSROOT 4.8 and higher
1431 AppendOutput(TString::Format("{\"$ref\":%u}", (unsigned)(refid - 1)));
1432 goto post_process;
1433 }
1434 MapObject(obj, cl, fJsonrCnt + 1); // +1 used
1435 }
1436
1437 fJsonrCnt++; // object counts required in dereferencing part
1438
1439 stack = JsonStartObjectWrite(cl);
1440
1441 } else if (map_convert == 2) {
1442 // special handling of map - it is object, but stored in the fValue
1443
1444 if (check_map) {
1446 if (refid > 0) {
1447 fValue.Form("{\"$ref\":%u}", (unsigned)(refid - 1));
1448 goto post_process;
1449 }
1450 MapObject(obj, cl, fJsonrCnt + 1); // +1 used
1451 }
1452
1453 fJsonrCnt++; // object counts required in dereferencing part
1454 stack = PushStack(0);
1455
1456 } else {
1457
1458 bool base64 = ((special_kind == TClassEdit::kVector) && stack && stack->fElem && strstr(stack->fElem->GetTitle(), "JSON_base64"));
1459
1460 // for array, string and STL collections different handling -
1461 // they not recognized at the end as objects in JSON
1462 stack = PushStack(0);
1463
1464 stack->fBase64 = base64;
1465 }
1466
1467 if (gDebug > 3)
1468 Info("JsonWriteObject", "Starting object %p write for class: %s", obj, cl->GetName());
1469
1471
1473 JsonWriteCollection((TCollection *)obj, cl);
1474 else
1475 (const_cast<TClass *>(cl))->Streamer((void *)obj, *this);
1476
1477 if (gDebug > 3)
1478 Info("JsonWriteObject", "Done object %p write for class: %s", obj, cl->GetName());
1479
1480 if (special_kind == json_TArray) {
1481 if (stack->fValues.size() != 1)
1482 Error("JsonWriteObject", "Problem when writing array");
1483 stack->fValues.clear();
1484 } else if ((special_kind == json_TString) || (special_kind == json_stdstring)) {
1485 if (stack->fValues.size() > 2)
1486 Error("JsonWriteObject", "Problem when writing TString or std::string");
1487 stack->fValues.clear();
1489 fValue.Clear();
1490 } else if ((special_kind > 0) && (special_kind < ROOT::kSTLend)) {
1491 // here make STL container processing
1492
1493 if (map_convert == 2) {
1494 // converting map into object
1495
1496 if (!stack->fValues.empty() && (fValue.Length() > 0))
1497 stack->PushValue(fValue);
1498
1499 const char *separ = (fCompact < 2) ? ", " : ",";
1500 const char *semi = (fCompact < 2) ? ": " : ":";
1501 bool first = true;
1502
1503 fValue = "{";
1504 if ((fTypeNameTag.Length() > 0) && !IsSkipClassInfo(cl)) {
1505 fValue.Append("\"");
1507 fValue.Append("\"");
1509 fValue.Append("\"");
1510 fValue.Append(cl->GetName());
1511 fValue.Append("\"");
1512 first = false;
1513 }
1514 for (Int_t k = 1; k < (int)stack->fValues.size() - 1; k += 2) {
1515 if (!first)
1517 first = false;
1518 fValue.Append(stack->fValues[k].c_str());
1520 fValue.Append(stack->fValues[k + 1].c_str());
1521 }
1522 fValue.Append("}");
1523 stack->fValues.clear();
1524 } else if (stack->fValues.empty()) {
1525 // empty container
1526 if (fValue != "0")
1527 Error("JsonWriteObject", "With empty stack fValue!=0");
1528 fValue = "[]";
1529 } else {
1530
1531 auto size = std::stoi(stack->fValues[0]);
1532
1533 bool trivial_format = false;
1534
1535 if ((stack->fValues.size() == 1) && ((size > 1) || ((fValue.Length() > 1) && (fValue[0]=='[')))) {
1536 // prevent case of vector<vector<value_class>>
1537 const auto proxy = cl->GetCollectionProxy();
1538 TClass *value_class = proxy ? proxy->GetValueClass() : nullptr;
1539 if (value_class && TClassEdit::IsStdClass(value_class->GetName()) && (value_class->GetCollectionType() != ROOT::kNotSTL))
1540 trivial_format = false;
1541 else
1542 trivial_format = true;
1543 }
1544
1545 if (trivial_format) {
1546 // case of simple vector, array already in the value
1547 stack->fValues.clear();
1548 if (fValue.Length() == 0) {
1549 Error("JsonWriteObject", "Empty value when it should contain something");
1550 fValue = "[]";
1551 }
1552
1553 } else {
1554 const char *separ = "[";
1555
1556 if (fValue.Length() > 0)
1557 stack->PushValue(fValue);
1558
1559 if ((size * 2 == (int) stack->fValues.size() - 1) && (map_convert > 0)) {
1560 // special handling for std::map.
1561 // Create entries like { '$pair': 'typename' , 'first' : key, 'second' : value }
1562 TString pairtype = cl->GetName();
1563 if (pairtype.Index("unordered_map<") == 0)
1564 pairtype.Replace(0, 14, "pair<");
1565 else if (pairtype.Index("unordered_multimap<") == 0)
1566 pairtype.Replace(0, 19, "pair<");
1567 else if (pairtype.Index("multimap<") == 0)
1568 pairtype.Replace(0, 9, "pair<");
1569 else if (pairtype.Index("map<") == 0)
1570 pairtype.Replace(0, 4, "pair<");
1571 else
1572 pairtype = "TPair";
1573 if (fTypeNameTag.Length() == 0)
1574 pairtype = "1";
1575 else
1576 pairtype = TString("\"") + pairtype + TString("\"");
1577 for (Int_t k = 1; k < (int) stack->fValues.size() - 1; k += 2) {
1580 // fJsonrCnt++; // do not add entry in the map, can conflict with objects inside values
1581 fValue.Append("{");
1582 fValue.Append("\"$pair\"");
1584 fValue.Append(pairtype.Data());
1586 fValue.Append("\"first\"");
1588 fValue.Append(stack->fValues[k].c_str());
1590 fValue.Append("\"second\"");
1592 fValue.Append(stack->fValues[k + 1].c_str());
1593 fValue.Append("}");
1594 }
1595 } else {
1596 // for most stl containers write just like blob, but skipping first element with size
1597 for (Int_t k = 1; k < (int) stack->fValues.size(); k++) {
1600 fValue.Append(stack->fValues[k].c_str());
1601 }
1602 }
1603
1604 fValue.Append("]");
1605 stack->fValues.clear();
1606 }
1607 }
1608 }
1609
1610 // reuse post-processing code for TObject or TRef
1611 PerformPostProcessing(stack, cl);
1612
1613 if ((special_kind == 0) && (!stack->fValues.empty() || (fValue.Length() > 0))) {
1614 if (gDebug > 0)
1615 Info("JsonWriteObject", "Create blob value for class %s", cl->GetName());
1616
1617 AppendOutput(fArraySepar.Data(), "\"_blob\"");
1619
1620 const char *separ = "[";
1621
1622 for (auto &elem: stack->fValues) {
1625 AppendOutput(elem.c_str());
1626 }
1627
1628 if (fValue.Length() > 0) {
1631 }
1632
1633 AppendOutput("]");
1634
1635 fValue.Clear();
1636 stack->fValues.clear();
1637 }
1638
1639 PopStack();
1640
1641 if ((special_kind <= 0))
1642 AppendOutput(nullptr, "}");
1643
1645
1646 if (fPrevOutput) {
1648 // for STL containers and TArray object in fValue itself
1649 if ((special_kind <= 0) || (special_kind > json_TArray))
1651 else if (fObjectOutput.Length() != 0)
1652 Error("JsonWriteObject", "Non-empty object output for special class %s", cl->GetName());
1653 }
1654}
1655
1656////////////////////////////////////////////////////////////////////////////////
1657/// store content of ROOT collection
1658
1660{
1661 AppendOutput(Stack()->NextMemberSeparator(), "\"name\"");
1663 AppendOutput("\"");
1664 AppendOutput(col->GetName());
1665 AppendOutput("\"");
1666 AppendOutput(Stack()->NextMemberSeparator(), "\"arr\"");
1668
1669 // collection treated as JS Array
1670 AppendOutput("[");
1671
1672 auto map = dynamic_cast<TMap *>(col);
1673 auto lst = dynamic_cast<TList *>(col);
1674
1675 TString sopt;
1676 Bool_t first = kTRUE;
1677
1678 if (lst) {
1679 // handle TList with extra options
1680 sopt.Capacity(500);
1681 sopt = "[";
1682
1683 auto lnk = lst->FirstLink();
1684 while (lnk) {
1685 if (!first) {
1687 sopt.Append(fArraySepar.Data());
1688 }
1689
1690 WriteObjectAny(lnk->GetObject(), TObject::Class());
1691
1692 if (dynamic_cast<TObjOptLink *>(lnk)) {
1693 sopt.Append("\"");
1694 sopt.Append(lnk->GetAddOption());
1695 sopt.Append("\"");
1696 } else
1697 sopt.Append("null");
1698
1699 lnk = lnk->Next();
1700 first = kFALSE;
1701 }
1702 } else if (map) {
1703 // handle TMap with artificial TPair object
1704 TIter iter(col);
1705 while (auto obj = iter()) {
1706 if (!first)
1708
1709 // fJsonrCnt++; // do not account map pair as JSON object
1710 AppendOutput("{", "\"$pair\"");
1712 AppendOutput("\"TPair\"");
1713 AppendOutput(fArraySepar.Data(), "\"first\"");
1715
1717
1718 AppendOutput(fArraySepar.Data(), "\"second\"");
1720 WriteObjectAny(map->GetValue(obj), TObject::Class());
1721 AppendOutput("", "}");
1722 first = kFALSE;
1723 }
1724 } else {
1725 TIter iter(col);
1726 while (auto obj = iter()) {
1727 if (!first)
1729
1731 first = kFALSE;
1732 }
1733 }
1734
1735 AppendOutput("]");
1736
1737 if (lst) {
1738 sopt.Append("]");
1739 AppendOutput(Stack()->NextMemberSeparator(), "\"opt\"");
1741 AppendOutput(sopt.Data());
1742 }
1743
1744 fValue.Clear();
1745}
1746
1747////////////////////////////////////////////////////////////////////////////////
1748/// read content of ROOT collection
1749
1751{
1752 if (!col)
1753 return;
1754
1755 TList *lst = nullptr;
1756 TMap *map = nullptr;
1757 TClonesArray *clones = nullptr;
1758 if (col->InheritsFrom(TList::Class()))
1759 lst = dynamic_cast<TList *>(col);
1760 else if (col->InheritsFrom(TMap::Class()))
1761 map = dynamic_cast<TMap *>(col);
1762 else if (col->InheritsFrom(TClonesArray::Class()))
1763 clones = dynamic_cast<TClonesArray *>(col);
1764
1765 nlohmann::json *json = Stack()->fNode;
1766
1767 std::string name = json->at("name");
1768 col->SetName(name.c_str());
1769
1770 nlohmann::json &arr = json->at("arr");
1771 int size = arr.size();
1772
1773 for (int n = 0; n < size; ++n) {
1774 nlohmann::json *subelem = &arr.at(n);
1775
1776 if (map)
1777 subelem = &subelem->at("first");
1778
1779 PushStack(0, subelem);
1780
1781 TClass *readClass = nullptr, *objClass = nullptr;
1782 void *subobj = nullptr;
1783
1784 if (clones) {
1785 if (n == 0) {
1786 if (!clones->GetClass() || (clones->GetSize() == 0)) {
1787 if (fTypeNameTag.Length() > 0) {
1788 clones->SetClass(subelem->at(fTypeNameTag.Data()).get<std::string>().c_str(), size);
1789 } else {
1790 Error("JsonReadCollection",
1791 "Cannot detect class name for TClonesArray - typename tag not configured");
1792 return;
1793 }
1794 } else if (size > clones->GetSize()) {
1795 Error("JsonReadCollection", "TClonesArray size %d smaller than required %d", clones->GetSize(), size);
1796 return;
1797 }
1798 }
1799 objClass = clones->GetClass();
1800 subobj = clones->ConstructedAt(n);
1801 }
1802
1804
1805 PopStack();
1806
1807 if (clones)
1808 continue;
1809
1810 if (!subobj || !readClass) {
1811 subobj = nullptr;
1812 } else if (readClass->GetBaseClassOffset(TObject::Class()) != 0) {
1813 Error("JsonReadCollection", "Try to add object %s not derived from TObject", readClass->GetName());
1814 subobj = nullptr;
1815 }
1816
1817 TObject *tobj = static_cast<TObject *>(subobj);
1818
1819 if (map) {
1820 PushStack(0, &arr.at(n).at("second"));
1821
1822 readClass = nullptr;
1823 void *subobj2 = JsonReadObject(nullptr, nullptr, &readClass);
1824
1825 PopStack();
1826
1827 if (!subobj2 || !readClass) {
1828 subobj2 = nullptr;
1829 } else if (readClass->GetBaseClassOffset(TObject::Class()) != 0) {
1830 Error("JsonReadCollection", "Try to add object %s not derived from TObject", readClass->GetName());
1831 subobj2 = nullptr;
1832 }
1833
1834 map->Add(tobj, static_cast<TObject *>(subobj2));
1835 } else if (lst) {
1836 auto &elem = json->at("opt").at(n);
1837 if (elem.is_null())
1838 lst->Add(tobj);
1839 else
1840 lst->Add(tobj, elem.get<std::string>().c_str());
1841 } else {
1842 // generic method, all kinds of TCollection should work
1843 col->Add(tobj);
1844 }
1845 }
1846}
1847
1848////////////////////////////////////////////////////////////////////////////////
1849/// Read object from current JSON node
1850
1852{
1853 if (readClass)
1854 *readClass = nullptr;
1855
1856 TJSONStackObj *stack = Stack();
1857
1858 Bool_t process_stl = stack->IsStl();
1859 nlohmann::json *json = stack->GetStlNode();
1860
1861 // check if null pointer
1862 if (json->is_null())
1863 return nullptr;
1864
1866
1867 // Extract pointer
1868 if (json->is_object() && (json->size() == 1) && (json->find("$ref") != json->end())) {
1869 unsigned refid = json->at("$ref").get<unsigned>();
1870
1871 void *ref_obj = nullptr;
1872 TClass *ref_cl = nullptr;
1873
1875
1876 if (!ref_obj || !ref_cl) {
1877 Error("JsonReadObject", "Fail to find object for reference %u", refid);
1878 return nullptr;
1879 }
1880
1881 if (readClass)
1882 *readClass = ref_cl;
1883
1884 if (gDebug > 2)
1885 Info("JsonReadObject", "Extract object reference %u %p cl:%s expects:%s", refid, ref_obj, ref_cl->GetName(),
1886 (objClass ? objClass->GetName() : "---"));
1887
1888 return ref_obj;
1889 }
1890
1891 // special case of strings - they do not create JSON object, but just string
1893 if (!obj)
1894 obj = objClass->New();
1895
1896 if (gDebug > 2)
1897 Info("JsonReadObject", "Read string from %s", json->dump().c_str());
1898
1900 *((std::string *)obj) = json->get<std::string>();
1901 else
1902 *((TString *)obj) = json->get<std::string>().c_str();
1903
1904 if (readClass)
1905 *readClass = const_cast<TClass *>(objClass);
1906
1907 return obj;
1908 }
1909
1910 Bool_t isBase = (stack->fElem && objClass) ? stack->fElem->IsBase() : kFALSE; // base class
1911
1912 if (isBase && (!obj || !objClass)) {
1913 Error("JsonReadObject", "No object when reading base class");
1914 return obj;
1915 }
1916
1917 Int_t map_convert = 0;
1920 map_convert = json->is_object() ? 2 : 1; // check if map was written as array or as object
1921
1922 if (objClass && !objClass->HasDictionary()) {
1923 Error("JsonReadObject", "Cannot stream class %s without dictionary", objClass->GetName());
1924 return obj;
1925 }
1926 }
1927
1928 // from now all operations performed with sub-element,
1929 // stack should be repaired at the end
1930 if (process_stl)
1931 stack = PushStack(0, json);
1932
1933 TClass *jsonClass = nullptr;
1935
1936 if ((special_kind == json_TArray) || ((special_kind > 0) && (special_kind < ROOT::kSTLend))) {
1937
1938 jsonClass = const_cast<TClass *>(objClass);
1939
1940 if (!obj)
1941 obj = jsonClass->New();
1942
1943 Int_t len = stack->IsJsonArray(json, map_convert == 2 ? fTypeNameTag.Data() : nullptr);
1944
1945 stack->PushIntValue(len > 0 ? len : 0);
1946
1947 if (len < 0) // should never happens
1948 Error("JsonReadObject", "Not array when expecting such %s", json->dump().c_str());
1949
1950 if (gDebug > 1)
1951 Info("JsonReadObject", "Reading special kind %d %s ptr %p", special_kind, objClass->GetName(), obj);
1952
1953 } else if (isBase) {
1954 // base class has special handling - no additional level and no extra refid
1955
1956 jsonClass = const_cast<TClass *>(objClass);
1957
1958 if (gDebug > 1)
1959 Info("JsonReadObject", "Reading baseclass %s ptr %p", objClass->GetName(), obj);
1960 } else {
1961
1962 if ((fTypeNameTag.Length() > 0) && (json->count(fTypeNameTag.Data()) > 0)) {
1963 std::string clname = json->at(fTypeNameTag.Data()).get<std::string>();
1965 if (!jsonClass)
1966 Error("JsonReadObject", "Cannot find class %s", clname.c_str());
1967 } else {
1968 // try to use class which is assigned by streamers - better than nothing
1969 jsonClass = const_cast<TClass *>(objClass);
1970 }
1971
1972 if (!jsonClass) {
1973 if (process_stl)
1974 PopStack();
1975 return obj;
1976 }
1977
1978 if ((fTypeVersionTag.Length() > 0) && (json->count(fTypeVersionTag.Data()) > 0))
1979 jsonClassVersion = json->at(fTypeVersionTag.Data()).get<int>();
1980
1981 if (objClass && (jsonClass != objClass)) {
1982 if (obj || (jsonClass->GetBaseClassOffset(objClass) != 0)) {
1983 if (jsonClass->GetBaseClassOffset(objClass) < 0)
1984 Error("JsonReadObject", "Not possible to read %s and casting to %s pointer as the two classes are unrelated",
1985 jsonClass->GetName(), objClass->GetName());
1986 else
1987 Error("JsonReadObject", "Reading %s and casting to %s pointer is currently not supported",
1988 jsonClass->GetName(), objClass->GetName());
1989 if (process_stl)
1990 PopStack();
1991 return obj;
1992 }
1993 }
1994
1995 if (!obj)
1996 obj = jsonClass->New();
1997
1998 if (gDebug > 1)
1999 Info("JsonReadObject", "Reading object of class %s refid %u ptr %p", jsonClass->GetName(), fJsonrCnt, obj);
2000
2001 if (!special_kind)
2003
2004 // add new element to the reading map
2005 MapObject(obj, jsonClass, ++fJsonrCnt);
2006 }
2007
2008 // there are two ways to handle custom streamers
2009 // either prepare data before streamer and tweak basic function which are reading values like UInt32_t
2010 // or try re-implement custom streamer here
2011
2012 if ((jsonClass == TObject::Class()) || (jsonClass == TRef::Class())) {
2013 // for TObject we re-implement custom streamer - it is much easier
2014
2016
2017 } else if (special_kind == json_TCollection) {
2018
2020
2021 } else {
2022
2024
2025 // special handling of STL which coded into arrays
2026 if ((special_kind > 0) && (special_kind < ROOT::kSTLend))
2028
2029 // if provided - use class version from JSON
2030 stack->fClVersion = jsonClassVersion ? jsonClassVersion : jsonClass->GetClassVersion();
2031
2032 if (gDebug > 3)
2033 Info("JsonReadObject", "Calling streamer of class %s", jsonClass->GetName());
2034
2035 if (isBase && (special_kind == 0))
2036 Error("JsonReadObject", "Should not be used for reading of base class %s", jsonClass->GetName());
2037
2038 if (do_read)
2039 jsonClass->Streamer((void *)obj, *this);
2040
2041 stack->fClVersion = 0;
2042
2043 stack->ClearStl(); // reset STL index for itself to prevent looping
2044 }
2045
2046 // return back stack position
2047 if (process_stl)
2048 PopStack();
2049
2050 if (gDebug > 1)
2051 Info("JsonReadObject", "Reading object of class %s done", jsonClass->GetName());
2052
2053 if (readClass)
2055
2056 return obj;
2057}
2058
2059////////////////////////////////////////////////////////////////////////////////
2060/// Read TObject data members from JSON.
2061/// Do not call TObject::Streamer() to avoid special tweaking of TBufferJSON interface
2062
2064{
2065 nlohmann::json *json = node ? (nlohmann::json *)node : Stack()->fNode;
2066
2067 UInt_t uid = json->at("fUniqueID").get<unsigned>();
2068 UInt_t bits = json->at("fBits").get<unsigned>();
2069 // UInt32_t pid = json->at("fPID").get<unsigned>(); // ignore PID for the moment
2070
2071 tobj->SetUniqueID(uid);
2072
2073 static auto tobj_fbits_offset = TObject::Class()->GetDataMemberOffset("fBits");
2074
2075 // there is no method to set all bits directly - do it differently
2076 if (tobj_fbits_offset > 0) {
2077 UInt_t *fbits = (UInt_t *) ((char* ) tobj + tobj_fbits_offset);
2079 }
2080}
2081
2082////////////////////////////////////////////////////////////////////////////////
2083/// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2084/// and indent new level in json structure.
2085/// This call indicates, that TStreamerInfo functions starts streaming
2086/// object data of correspondent class
2087
2089{
2090 if (gDebug > 2)
2091 Info("IncrementLevel", "Class: %s", (info ? info->GetClass()->GetName() : "custom"));
2092
2094}
2095
2096////////////////////////////////////////////////////////////////////////////////
2097/// Prepares buffer to stream data of specified class
2098
2100{
2101 if (sinfo)
2102 cl = sinfo->GetClass();
2103
2104 if (!cl)
2105 return;
2106
2107 if (gDebug > 3)
2108 Info("WorkWithClass", "Class: %s", cl->GetName());
2109
2110 TJSONStackObj *stack = Stack();
2111
2112 if (IsReading()) {
2113 stack = PushStack(0, stack->fNode);
2114 } else if (stack && stack->IsStreamerElement() && !stack->fIsObjStarted &&
2115 ((stack->fElem->GetType() == TStreamerInfo::kObject) ||
2116 (stack->fElem->GetType() == TStreamerInfo::kAny))) {
2117
2118 stack->fIsObjStarted = kTRUE;
2119
2120 fJsonrCnt++; // count object, but do not keep reference
2121
2122 stack = JsonStartObjectWrite(cl, sinfo);
2123 } else {
2124 stack = PushStack(0);
2125 }
2126
2127 stack->fInfo = sinfo;
2128 stack->fIsStreamerInfo = kTRUE;
2129}
2130
2131////////////////////////////////////////////////////////////////////////////////
2132/// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2133/// and decrease level in json structure.
2134
2136{
2137 if (gDebug > 2)
2138 Info("DecrementLevel", "Class: %s", (info ? info->GetClass()->GetName() : "custom"));
2139
2140 TJSONStackObj *stack = Stack();
2141
2142 if (stack->IsStreamerElement()) {
2143
2144 if (IsWriting()) {
2145 if (gDebug > 3)
2146 Info("DecrementLevel", " Perform post-processing elem: %s", stack->fElem->GetName());
2147
2148 PerformPostProcessing(stack);
2149 }
2150
2151 stack = PopStack(); // remove stack of last element
2152 }
2153
2154 if (stack->fInfo != (TStreamerInfo *)info)
2155 Error("DecrementLevel", " Mismatch of streamer info");
2156
2157 PopStack(); // back from data of stack info
2158
2159 if (gDebug > 3)
2160 Info("DecrementLevel", "Class: %s done", (info ? info->GetClass()->GetName() : "custom"));
2161}
2162
2163////////////////////////////////////////////////////////////////////////////////
2164/// Return current streamer info element
2165
2170
2171////////////////////////////////////////////////////////////////////////////////
2172/// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2173/// and add/verify next element of json structure
2174/// This calls allows separate data, correspondent to one class member, from another
2175
2177{
2178 if (gDebug > 3)
2179 Info("SetStreamerElementNumber", "Element name %s", elem->GetName());
2180
2182}
2183
2184////////////////////////////////////////////////////////////////////////////////
2185/// This is call-back from streamer which indicates
2186/// that class member will be streamed
2187/// Name of element used in JSON
2188
2190{
2191 TJSONStackObj *stack = Stack();
2192 if (!stack) {
2193 Error("WorkWithElement", "stack is empty");
2194 return;
2195 }
2196
2197 if (gDebug > 0)
2198 Info("WorkWithElement", " Start element %s type %d typename %s", elem ? elem->GetName() : "---",
2199 elem ? elem->GetType() : -1, elem ? elem->GetTypeName() : "---");
2200
2201 if (stack->IsStreamerElement()) {
2202 // this is post processing
2203
2204 if (IsWriting()) {
2205 if (gDebug > 3)
2206 Info("WorkWithElement", " Perform post-processing elem: %s", stack->fElem->GetName());
2207 PerformPostProcessing(stack);
2208 }
2209
2210 stack = PopStack(); // go level back
2211 }
2212
2213 fValue.Clear();
2214
2215 if (!stack) {
2216 Error("WorkWithElement", "Lost of stack");
2217 return;
2218 }
2219
2220 TStreamerInfo *info = stack->fInfo;
2221 if (!stack->IsStreamerInfo()) {
2222 Error("WorkWithElement", "Problem in Inc/Dec level");
2223 return;
2224 }
2225
2226 Int_t number = info ? info->GetElements()->IndexOf(elem) : -1;
2227
2228 if (!elem) {
2229 Error("WorkWithElement", "streamer info returns elem = nullptr");
2230 return;
2231 }
2232
2233 TClass *base_class = elem->IsBase() ? elem->GetClassPointer() : nullptr;
2234
2235 stack = PushStack(0, stack->fNode);
2236 stack->fElem = elem;
2237 stack->fIsElemOwner = (number < 0);
2238
2240
2241 if (base_class && IsReading())
2242 stack->fClVersion = base_class->GetClassVersion();
2243
2244 if ((elem->GetType() == TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop) && (elem->GetArrayDim() > 0)) {
2245 // array of array, start handling here
2246 stack->fIndx = std::make_unique<TArrayIndexProducer>(elem, -1, fArraySepar.Data());
2247 if (IsWriting())
2248 AppendOutput(stack->fIndx->GetBegin());
2249 }
2250
2251 if (IsReading() && (elem->GetType() > TStreamerInfo::kOffsetP) && (elem->GetType() < TStreamerInfo::kOffsetP + 20)) {
2252 // reading of such array begins with reading of single Char_t value
2253 // it indicates if array should be read or not
2254 stack->PushIntValue(stack->IsJsonString() || (stack->IsJsonArray() > 0) ? 1 : 0);
2255 }
2256}
2257
2258////////////////////////////////////////////////////////////////////////////////
2259/// Should be called in the beginning of custom class streamer.
2260/// Informs buffer data about class which will be streamed now.
2261///
2262/// ClassBegin(), ClassEnd() and ClassMember() should be used in
2263/// custom class streamers to specify which kind of data are
2264/// now streamed. Such information is used to correctly
2265/// convert class data to JSON. Without that functions calls
2266/// classes with custom streamers cannot be used with TBufferJSON
2267
2269{
2270 WorkWithClass(nullptr, cl);
2271}
2272
2273////////////////////////////////////////////////////////////////////////////////
2274/// Should be called at the end of custom streamer
2275/// See TBufferJSON::ClassBegin for more details
2276
2278{
2279 DecrementLevel(0);
2280}
2281
2282////////////////////////////////////////////////////////////////////////////////
2283/// Method indicates name and typename of class member,
2284/// which should be now streamed in custom streamer
2285/// Following combinations are supported:
2286/// 1. name = "ClassName", typeName = 0 or typename==ClassName
2287/// This is a case, when data of parent class "ClassName" should be streamed.
2288/// For instance, if class directly inherited from TObject, custom
2289/// streamer should include following code:
2290/// ~~~{.cpp}
2291/// b.ClassMember("TObject");
2292/// TObject::Streamer(b);
2293/// ~~~
2294/// 2. Basic data type
2295/// ~~~{.cpp}
2296/// b.ClassMember("fInt","Int_t");
2297/// b >> fInt;
2298/// ~~~
2299/// 3. Array of basic data types
2300/// ~~~{.cpp}
2301/// b.ClassMember("fArr","Int_t", 5);
2302/// b.ReadFastArray(fArr, 5);
2303/// ~~~
2304/// 4. Object as data member
2305/// ~~~{.cpp}
2306/// b.ClassMember("fName","TString");
2307/// fName.Streamer(b);
2308/// ~~~
2309/// 5. Pointer on object as data member
2310/// ~~~{.cpp}
2311/// b.ClassMember("fObj","TObject*");
2312/// b.StreamObject(fObj);
2313/// ~~~
2314///
2315/// arrsize1 and arrsize2 arguments (when specified) indicate first and
2316/// second dimension of array. Can be used for array of basic types.
2317/// See ClassBegin() method for more details.
2318
2319void TBufferJSON::ClassMember(const char *name, const char *typeName, Int_t arrsize1, Int_t arrsize2)
2320{
2321 if (!typeName)
2322 typeName = name;
2323
2324 if (!name || (strlen(name) == 0)) {
2325 Error("ClassMember", "Invalid member name");
2326 return;
2327 }
2328
2329 TString tname = typeName;
2330
2331 Int_t typ_id = -1;
2332
2333 if (strcmp(typeName, "raw:data") == 0)
2335
2336 if (typ_id < 0) {
2337 TDataType *dt = gROOT->GetType(typeName);
2338 if (dt && (dt->GetType() > 0) && (dt->GetType() < 20))
2339 typ_id = dt->GetType();
2340 }
2341
2342 if (typ_id < 0)
2343 if (strcmp(name, typeName) == 0) {
2344 TClass *cl = TClass::GetClass(tname.Data());
2345 if (cl)
2347 }
2348
2349 if (typ_id < 0) {
2351 if (tname[tname.Length() - 1] == '*') {
2352 tname.Resize(tname.Length() - 1);
2353 isptr = kTRUE;
2354 }
2355 TClass *cl = TClass::GetClass(tname.Data());
2356 if (!cl) {
2357 Error("ClassMember", "Invalid class specifier %s", typeName);
2358 return;
2359 }
2360
2361 if (cl->IsTObject())
2363 else
2365
2366 if ((cl == TString::Class()) && !isptr)
2368 }
2369
2370 TStreamerElement *elem = nullptr;
2371
2373 elem = new TStreamerElement(name, "title", 0, typ_id, "raw:data");
2374 } else if (typ_id == TStreamerInfo::kBase) {
2375 TClass *cl = TClass::GetClass(tname.Data());
2376 if (cl) {
2377 TStreamerBase *b = new TStreamerBase(tname.Data(), "title", 0);
2378 b->SetBaseVersion(cl->GetClassVersion());
2379 elem = b;
2380 }
2381 } else if ((typ_id > 0) && (typ_id < 20)) {
2382 elem = new TStreamerBasicType(name, "title", 0, typ_id, typeName);
2385 elem = new TStreamerObject(name, "title", 0, tname.Data());
2386 } else if (typ_id == TStreamerInfo::kObjectp) {
2387 elem = new TStreamerObjectPointer(name, "title", 0, tname.Data());
2388 } else if (typ_id == TStreamerInfo::kAny) {
2389 elem = new TStreamerObjectAny(name, "title", 0, tname.Data());
2390 } else if (typ_id == TStreamerInfo::kAnyp) {
2391 elem = new TStreamerObjectAnyPointer(name, "title", 0, tname.Data());
2392 } else if (typ_id == TStreamerInfo::kTString) {
2393 elem = new TStreamerString(name, "title", 0);
2394 }
2395
2396 if (!elem) {
2397 Error("ClassMember", "Invalid combination name = %s type = %s", name, typeName);
2398 return;
2399 }
2400
2401 if (arrsize1 > 0) {
2402 elem->SetArrayDim(arrsize2 > 0 ? 2 : 1);
2403 elem->SetMaxIndex(0, arrsize1);
2404 if (arrsize2 > 0)
2405 elem->SetMaxIndex(1, arrsize2);
2406 }
2407
2408 // we indicate that there is no streamerinfo
2409 WorkWithElement(elem, -1);
2410}
2411
2412////////////////////////////////////////////////////////////////////////////////
2413/// Function is converts TObject and TString structures to more compact representation
2414
2416{
2417 if (stack->fIsPostProcessed)
2418 return;
2419
2420 const TStreamerElement *elem = stack->fElem;
2421
2422 if (!elem && !obj_cl)
2423 return;
2424
2425 stack->fIsPostProcessed = kTRUE;
2426
2427 // when element was written as separate object, close only braces and exit
2428 if (stack->fIsObjStarted) {
2429 AppendOutput("", "}");
2430 return;
2431 }
2432
2435
2436 if (obj_cl) {
2437 if (obj_cl == TObject::Class())
2438 isTObject = kTRUE;
2439 else if (obj_cl == TRef::Class())
2440 isTRef = kTRUE;
2441 else
2442 return;
2443 } else {
2444 const char *typname = elem->IsBase() ? elem->GetName() : elem->GetTypeName();
2445 isTObject = (elem->GetType() == TStreamerInfo::kTObject) || (strcmp("TObject", typname) == 0);
2446 isTString = elem->GetType() == TStreamerInfo::kTString;
2448 isOffsetPArray = (elem->GetType() > TStreamerInfo::kOffsetP) && (elem->GetType() < TStreamerInfo::kOffsetP + 20);
2449 isTArray = (strncmp("TArray", typname, 6) == 0);
2450 }
2451
2452 if (isTString || isSTLstring) {
2453 // just remove all kind of string length information
2454
2455 if (gDebug > 3)
2456 Info("PerformPostProcessing", "reformat string value = '%s'", fValue.Data());
2457
2458 stack->fValues.clear();
2459 } else if (isOffsetPArray) {
2460 // basic array with [fN] comment
2461
2462 if (stack->fValues.empty() && (fValue == "0")) {
2463 fValue = "[]";
2464 } else if ((stack->fValues.size() == 1) && (stack->fValues[0] == "1")) {
2465 stack->fValues.clear();
2466 } else {
2467 Error("PerformPostProcessing", "Wrong values for kOffsetP element %s", (elem ? elem->GetName() : "---"));
2468 stack->fValues.clear();
2469 fValue = "[]";
2470 }
2471 } else if (isTObject || isTRef) {
2472 // complex workaround for TObject/TRef streamer
2473 // would be nice if other solution can be found
2474 // Here is not supported TRef on TRef (double reference)
2475
2476 Int_t cnt = stack->fValues.size();
2477 if (fValue.Length() > 0)
2478 cnt++;
2479
2480 if (cnt < 2 || cnt > 3) {
2481 if (gDebug > 0)
2482 Error("PerformPostProcessing", "When storing TObject/TRef, strange number of items %d", cnt);
2483 AppendOutput(stack->NextMemberSeparator(), "\"dummy\"");
2485 } else {
2486 AppendOutput(stack->NextMemberSeparator(), "\"fUniqueID\"");
2488 AppendOutput(stack->fValues[0].c_str());
2489 AppendOutput(stack->NextMemberSeparator(), "\"fBits\"");
2491 auto tbits = std::atol((stack->fValues.size() > 1) ? stack->fValues[1].c_str() : fValue.Data());
2492 AppendOutput(std::to_string(tbits & ~TObject::kNotDeleted & ~TObject::kIsOnHeap).c_str());
2493 if (cnt == 3) {
2494 AppendOutput(stack->NextMemberSeparator(), "\"fPID\"");
2496 AppendOutput((stack->fValues.size() > 2) ? stack->fValues[2].c_str() : fValue.Data());
2497 }
2498
2499 stack->fValues.clear();
2500 fValue.Clear();
2501 return;
2502 }
2503
2504 } else if (isTArray) {
2505 // for TArray one deletes complete stack
2506 stack->fValues.clear();
2507 }
2508
2509 if (elem && elem->IsBase() && (fValue.Length() == 0)) {
2510 // here base class data already completely stored
2511 return;
2512 }
2513
2514 if (!stack->fValues.empty()) {
2515 // append element blob data just as abstract array, user is responsible to decode it
2516 AppendOutput("[");
2517 for (auto &blob: stack->fValues) {
2518 AppendOutput(blob.c_str());
2520 }
2521 }
2522
2523 if (fValue.Length() == 0) {
2524 AppendOutput("null");
2525 } else {
2527 fValue.Clear();
2528 }
2529
2530 if (!stack->fValues.empty())
2531 AppendOutput("]");
2532}
2533
2534////////////////////////////////////////////////////////////////////////////////
2535/// suppressed function of TBuffer
2536
2538{
2539 return nullptr;
2540}
2541
2542////////////////////////////////////////////////////////////////////////////////
2543/// suppressed function of TBuffer
2544
2546
2547////////////////////////////////////////////////////////////////////////////////
2548/// read version value from buffer
2549
2551{
2552 Version_t res = cl ? cl->GetClassVersion() : 0;
2553
2554 if (start)
2555 *start = 0;
2556 if (bcnt)
2557 *bcnt = 0;
2558
2559 if (!cl && Stack()->fClVersion) {
2560 res = Stack()->fClVersion;
2561 Stack()->fClVersion = 0;
2562 }
2563
2564 if (gDebug > 3)
2565 Info("ReadVersion", "Result: %d Class: %s", res, (cl ? cl->GetName() : "---"));
2566
2567 return res;
2568}
2569
2570////////////////////////////////////////////////////////////////////////////////
2571/// Ignored in TBufferJSON
2572
2573UInt_t TBufferJSON::WriteVersion(const TClass * /*cl*/, Bool_t /* useBcnt */)
2574{
2575 return 0;
2576}
2577
2578////////////////////////////////////////////////////////////////////////////////
2579/// Read object from buffer. Only used from TBuffer
2580
2582{
2583 if (gDebug > 2)
2584 Info("ReadObjectAny", "From current JSON node");
2585 void *res = JsonReadObject(nullptr, expectedClass);
2586 return res;
2587}
2588
2589////////////////////////////////////////////////////////////////////////////////
2590/// Skip any kind of object from buffer
2591
2593
2594////////////////////////////////////////////////////////////////////////////////
2595/// Write object to buffer. Only used from TBuffer
2596
2598{
2599 if (gDebug > 3)
2600 Info("WriteObjectClass", "Class %s", (actualClass ? actualClass->GetName() : " null"));
2601
2603}
2604
2605////////////////////////////////////////////////////////////////////////////////
2606/// If value exists, push in the current stack for post-processing
2607
2609{
2610 if (fValue.Length() > 0)
2612}
2613
2614////////////////////////////////////////////////////////////////////////////////
2615/// Read array of Bool_t from buffer
2616
2621
2622////////////////////////////////////////////////////////////////////////////////
2623/// Read array of Char_t from buffer
2624
2629
2630////////////////////////////////////////////////////////////////////////////////
2631/// Read array of UChar_t from buffer
2632
2637
2638////////////////////////////////////////////////////////////////////////////////
2639/// Read array of Short_t from buffer
2640
2645
2646////////////////////////////////////////////////////////////////////////////////
2647/// Read array of UShort_t from buffer
2648
2653
2654////////////////////////////////////////////////////////////////////////////////
2655/// Read array of Int_t from buffer
2656
2658{
2659 return JsonReadArray(i);
2660}
2661
2662////////////////////////////////////////////////////////////////////////////////
2663/// Read array of UInt_t from buffer
2664
2666{
2667 return JsonReadArray(i);
2668}
2669
2670////////////////////////////////////////////////////////////////////////////////
2671/// Read array of Long_t from buffer
2672
2677
2678////////////////////////////////////////////////////////////////////////////////
2679/// Read array of ULong_t from buffer
2680
2685
2686////////////////////////////////////////////////////////////////////////////////
2687/// Read array of Long64_t from buffer
2688
2693
2694////////////////////////////////////////////////////////////////////////////////
2695/// Read array of ULong64_t from buffer
2696
2701
2702////////////////////////////////////////////////////////////////////////////////
2703/// Read array of Float_t from buffer
2704
2709
2710////////////////////////////////////////////////////////////////////////////////
2711/// Read array of Double_t from buffer
2712
2717
2718////////////////////////////////////////////////////////////////////////////////
2719/// Read static array from JSON - not used
2720
2721template <typename T>
2723{
2724 Info("ReadArray", "Not implemented");
2725 return value ? 1 : 0;
2726}
2727
2728////////////////////////////////////////////////////////////////////////////////
2729/// Read array of Bool_t from buffer
2730
2735
2736////////////////////////////////////////////////////////////////////////////////
2737/// Read array of Char_t from buffer
2738
2743
2744////////////////////////////////////////////////////////////////////////////////
2745/// Read array of UChar_t from buffer
2746
2751
2752////////////////////////////////////////////////////////////////////////////////
2753/// Read array of Short_t from buffer
2754
2759
2760////////////////////////////////////////////////////////////////////////////////
2761/// Read array of UShort_t from buffer
2762
2767
2768////////////////////////////////////////////////////////////////////////////////
2769/// Read array of Int_t from buffer
2770
2775
2776////////////////////////////////////////////////////////////////////////////////
2777/// Read array of UInt_t from buffer
2778
2783
2784////////////////////////////////////////////////////////////////////////////////
2785/// Read array of Long_t from buffer
2786
2791
2792////////////////////////////////////////////////////////////////////////////////
2793/// Read array of ULong_t from buffer
2794
2799
2800////////////////////////////////////////////////////////////////////////////////
2801/// Read array of Long64_t from buffer
2802
2807
2808////////////////////////////////////////////////////////////////////////////////
2809/// Read array of ULong64_t from buffer
2810
2815
2816////////////////////////////////////////////////////////////////////////////////
2817/// Read array of Float_t from buffer
2818
2823
2824////////////////////////////////////////////////////////////////////////////////
2825/// Read array of Double_t from buffer
2826
2831
2832////////////////////////////////////////////////////////////////////////////////
2833/// Template method to read array from the JSON
2834
2835template <typename T>
2837{
2838 if (!arr || (arrsize <= 0))
2839 return;
2840 nlohmann::json *json = Stack()->fNode;
2841 if (gDebug > 2)
2842 Info("ReadFastArray", "Reading array sz %d from JSON %s", arrsize, json->dump().substr(0, 30).c_str());
2843 auto indexes = Stack()->MakeReadIndexes();
2844 if (indexes) { /* at least two dims */
2845 TArrayI &indx = indexes->GetIndices();
2846 Int_t lastdim = indx.GetSize() - 1;
2847 if (indexes->TotalLength() != arrsize)
2848 Error("ReadFastArray", "Mismatch %d-dim array sizes %d %d", lastdim + 1, arrsize, (int)indexes->TotalLength());
2849 for (int cnt = 0; cnt < arrsize; ++cnt) {
2850 nlohmann::json *elem = &(json->at(indx[0]));
2851 for (int k = 1; k < lastdim; ++k)
2852 elem = &((*elem)[indx[k]]);
2853 arr[cnt] = (asstring && elem->is_string()) ? elem->get<std::string>()[indx[lastdim]] : (*elem)[indx[lastdim]].get<T>();
2854 indexes->NextSeparator();
2855 }
2856 } else if (asstring && json->is_string()) {
2857 std::string str = json->get<std::string>();
2858 for (int cnt = 0; cnt < arrsize; ++cnt)
2859 arr[cnt] = (cnt < (int)str.length()) ? str[cnt] : 0;
2860 } else if (json->is_object() && (json->count("$arr") == 1)) {
2861 if (json->at("len").get<int>() != arrsize)
2862 Error("ReadFastArray", "Mismatch compressed array size %d %d", arrsize, json->at("len").get<int>());
2863
2864 for (int cnt = 0; cnt < arrsize; ++cnt)
2865 arr[cnt] = 0;
2866
2867 if (json->count("b") == 1) {
2868 auto base64 = json->at("b").get<std::string>();
2869
2870 int offset = (json->count("o") == 1) ? json->at("o").get<int>() : 0;
2871
2872 // TODO: provide TBase64::Decode with direct write into target buffer
2873 auto decode = TBase64::Decode(base64.c_str());
2874
2875 if (arrsize * (long) sizeof(T) < (offset + decode.Length())) {
2876 Error("ReadFastArray", "Base64 data %ld larger than target array size %ld", (long) decode.Length() + offset, (long) (arrsize*sizeof(T)));
2877 } else if ((sizeof(T) > 1) && (decode.Length() % sizeof(T) != 0)) {
2878 Error("ReadFastArray", "Base64 data size %ld not matches with element size %ld", (long) decode.Length(), (long) sizeof(T));
2879 } else {
2880 memcpy((char *) arr + offset, decode.Data(), decode.Length());
2881 }
2882 return;
2883 }
2884
2885 int p = 0, id = 0;
2886 std::string idname = "", pname, vname, nname;
2887 while (p < arrsize) {
2888 pname = std::string("p") + idname;
2889 if (json->count(pname) == 1)
2890 p = json->at(pname).get<int>();
2891 vname = std::string("v") + idname;
2892 if (json->count(vname) != 1)
2893 break;
2894 nlohmann::json &v = json->at(vname);
2895 if (v.is_array()) {
2896 for (unsigned sub = 0; sub < v.size(); ++sub)
2897 arr[p++] = v[sub].get<T>();
2898 } else {
2899 nname = std::string("n") + idname;
2900 unsigned ncopy = (json->count(nname) == 1) ? json->at(nname).get<unsigned>() : 1;
2901 for (unsigned sub = 0; sub < ncopy; ++sub)
2902 arr[p++] = v.get<T>();
2903 }
2904 idname = std::to_string(++id);
2905 }
2906 } else {
2907 if ((int)json->size() != arrsize)
2908 Error("ReadFastArray", "Mismatch array sizes %d %d", arrsize, (int)json->size());
2909 for (int cnt = 0; cnt < arrsize; ++cnt)
2910 arr[cnt] = json->at(cnt).get<T>();
2911 }
2912}
2913
2914////////////////////////////////////////////////////////////////////////////////
2915/// read array of Bool_t from buffer
2916
2921
2922////////////////////////////////////////////////////////////////////////////////
2923/// read array of Char_t from buffer
2924
2926{
2927 JsonReadFastArray(c, n, true);
2928}
2929
2930////////////////////////////////////////////////////////////////////////////////
2931/// read array of Char_t from buffer
2932
2937
2938////////////////////////////////////////////////////////////////////////////////
2939/// read array of UChar_t from buffer
2940
2945
2946////////////////////////////////////////////////////////////////////////////////
2947/// read array of Short_t from buffer
2948
2953
2954////////////////////////////////////////////////////////////////////////////////
2955/// read array of UShort_t from buffer
2956
2961
2962////////////////////////////////////////////////////////////////////////////////
2963/// read array of Int_t from buffer
2964
2969
2970////////////////////////////////////////////////////////////////////////////////
2971/// read array of UInt_t from buffer
2972
2977
2978////////////////////////////////////////////////////////////////////////////////
2979/// read array of Long_t from buffer
2980
2985
2986////////////////////////////////////////////////////////////////////////////////
2987/// read array of ULong_t from buffer
2988
2993
2994////////////////////////////////////////////////////////////////////////////////
2995/// read array of Long64_t from buffer
2996
3001
3002////////////////////////////////////////////////////////////////////////////////
3003/// read array of ULong64_t from buffer
3004
3009
3010////////////////////////////////////////////////////////////////////////////////
3011/// read array of Float_t from buffer
3012
3017
3018////////////////////////////////////////////////////////////////////////////////
3019/// read array of Double_t from buffer
3020
3025
3026////////////////////////////////////////////////////////////////////////////////
3027/// Read an array of 'n' objects from the I/O buffer.
3028/// Stores the objects read starting at the address 'start'.
3029/// The objects in the array are assume to be of class 'cl'.
3030/// Copied code from TBufferFile
3031
3032void TBufferJSON::ReadFastArray(void *start, const TClass *cl, Int_t n, TMemberStreamer * /* streamer */,
3033 const TClass * /* onFileClass */)
3034{
3035 if (gDebug > 1)
3036 Info("ReadFastArray", "void* n:%d cl:%s", n, cl->GetName());
3037
3038 // if (streamer) {
3039 // Info("ReadFastArray", "(void*) Calling streamer - not handled correctly");
3040 // streamer->SetOnFileClass(onFileClass);
3041 // (*streamer)(*this, start, 0);
3042 // return;
3043 // }
3044
3045 int objectSize = cl->Size();
3046 char *obj = (char *)start;
3047
3048 TJSONStackObj *stack = Stack();
3049 nlohmann::json *topnode = stack->fNode, *subnode = topnode;
3050 if (stack->fIndx)
3051 subnode = stack->fIndx->ExtractNode(topnode);
3052
3053 TArrayIndexProducer indexes(stack->fElem, n, "");
3054
3055 if (gDebug > 1)
3056 Info("ReadFastArray", "Indexes ndim:%d totallen:%d", indexes.NumDimensions(), indexes.TotalLength());
3057
3058 for (Int_t j = 0; j < n; j++, obj += objectSize) {
3059
3060 stack->fNode = indexes.ExtractNode(subnode);
3061
3062 JsonReadObject(obj, cl);
3063 }
3064
3065 // restore top node - show we use stack here?
3066 stack->fNode = topnode;
3067}
3068
3069////////////////////////////////////////////////////////////////////////////////
3070/// redefined here to avoid warning message from gcc
3071
3073 TMemberStreamer * /* streamer */, const TClass * /* onFileClass */)
3074{
3075 if (gDebug > 1)
3076 Info("ReadFastArray", "void** n:%d cl:%s prealloc:%s", n, cl->GetName(), (isPreAlloc ? "true" : "false"));
3077
3078 // if (streamer) {
3079 // Info("ReadFastArray", "(void**) Calling streamer - not handled correctly");
3080 // if (isPreAlloc) {
3081 // for (Int_t j = 0; j < n; j++) {
3082 // if (!start[j])
3083 // start[j] = cl->New();
3084 // }
3085 // }
3086 // streamer->SetOnFileClass(onFileClass);
3087 // (*streamer)(*this, (void *)start, 0);
3088 // return;
3089 // }
3090
3091 TJSONStackObj *stack = Stack();
3092 nlohmann::json *topnode = stack->fNode, *subnode = topnode;
3093 if (stack->fIndx)
3094 subnode = stack->fIndx->ExtractNode(topnode);
3095
3096 TArrayIndexProducer indexes(stack->fElem, n, "");
3097
3098 for (Int_t j = 0; j < n; j++) {
3099
3100 stack->fNode = indexes.ExtractNode(subnode);
3101
3102 if (!isPreAlloc) {
3103 void *old = start[j];
3104 start[j] = JsonReadObject(nullptr, cl);
3105 if (old && old != start[j] && TStreamerInfo::CanDelete())
3106 (const_cast<TClass *>(cl))->Destructor(old, kFALSE); // call delete and destruct
3107 } else {
3108 if (!start[j])
3109 start[j] = (const_cast<TClass *>(cl))->New();
3110 JsonReadObject(start[j], cl);
3111 }
3112 }
3113
3114 stack->fNode = topnode;
3115}
3116
3117template <typename T>
3119{
3120 bool is_base64 = Stack()->fBase64 || (fArrayCompact == kBase64);
3121
3122 if (!is_base64 && ((fArrayCompact == 0) || (arrsize < 6))) {
3123 fValue.Append("[");
3124 for (Int_t indx = 0; indx < arrsize; indx++) {
3125 if (indx > 0)
3128 }
3129 fValue.Append("]");
3130 } else if (is_base64 && !arrsize) {
3131 fValue.Append("[]");
3132 } else {
3133 fValue.Append("{");
3134 fValue.Append(TString::Format("\"$arr\":\"%s\"%s\"len\":%d", typname, fArraySepar.Data(), arrsize));
3135 Int_t aindx(0), bindx(arrsize);
3136 while ((aindx < arrsize) && (vname[aindx] == 0))
3137 aindx++;
3138 while ((aindx < bindx) && (vname[bindx - 1] == 0))
3139 bindx--;
3140
3141 if (is_base64) {
3142 // small initial offset makes no sense - JSON code is large then size gain
3143 if ((aindx * sizeof(T) < 5) && (aindx < bindx))
3144 aindx = 0;
3145
3146 if ((aindx > 0) && (aindx < bindx))
3147 fValue.Append(TString::Format("%s\"o\":%ld", fArraySepar.Data(), (long) (aindx * (int) sizeof(T))));
3148
3150 fValue.Append("\"b\":\"");
3151
3152 if (aindx < bindx)
3153 fValue.Append(TBase64::Encode((const char *) (vname + aindx), (bindx - aindx) * sizeof(T)));
3154
3155 fValue.Append("\"");
3156 } else if (aindx < bindx) {
3157 TString suffix("");
3158 Int_t p(aindx), suffixcnt(-1), lastp(0);
3159 while (p < bindx) {
3160 if (vname[p] == 0) {
3161 p++;
3162 continue;
3163 }
3164 Int_t p0(p++), pp(0), nsame(1);
3166 pp = bindx;
3167 p = bindx + 1;
3168 nsame = 0;
3169 }
3170 for (; p <= bindx; ++p) {
3171 if ((p < bindx) && (vname[p] == vname[p - 1])) {
3172 nsame++;
3173 continue;
3174 }
3175 if (vname[p - 1] == 0) {
3176 if (nsame > 9) {
3177 nsame = 0;
3178 break;
3179 }
3180 } else if (nsame > 5) {
3181 if (pp) {
3182 p = pp;
3183 nsame = 0;
3184 } else
3185 pp = p;
3186 break;
3187 }
3188 pp = p;
3189 nsame = 1;
3190 }
3191 if (pp <= p0)
3192 continue;
3193 if (++suffixcnt > 0)
3194 suffix.Form("%d", suffixcnt);
3195 if (p0 != lastp)
3196 fValue.Append(TString::Format("%s\"p%s\":%d", fArraySepar.Data(), suffix.Data(), p0));
3197 lastp = pp; /* remember cursor, it may be the same */
3198 fValue.Append(TString::Format("%s\"v%s\":", fArraySepar.Data(), suffix.Data()));
3199 if ((nsame > 1) || (pp - p0 == 1)) {
3201 if (nsame > 1)
3202 fValue.Append(TString::Format("%s\"n%s\":%d", fArraySepar.Data(), suffix.Data(), nsame));
3203 } else {
3204 fValue.Append("[");
3205 for (Int_t indx = p0; indx < pp; indx++) {
3206 if (indx > p0)
3209 }
3210 fValue.Append("]");
3211 }
3212 }
3213 }
3214 fValue.Append("}");
3215 }
3216}
3217
3218////////////////////////////////////////////////////////////////////////////////
3219/// Write array of Bool_t to buffer
3220
3222{
3223 JsonPushValue();
3224 JsonWriteArrayCompress(b, n, "Bool");
3225}
3226
3227////////////////////////////////////////////////////////////////////////////////
3228/// Write array of Char_t to buffer
3229
3231{
3232 JsonPushValue();
3233 JsonWriteArrayCompress(c, n, "Int8");
3234}
3235
3236////////////////////////////////////////////////////////////////////////////////
3237/// Write array of UChar_t to buffer
3238
3240{
3241 JsonPushValue();
3242 JsonWriteArrayCompress(c, n, "Uint8");
3243}
3244
3245////////////////////////////////////////////////////////////////////////////////
3246/// Write array of Short_t to buffer
3247
3249{
3250 JsonPushValue();
3251 JsonWriteArrayCompress(h, n, "Int16");
3252}
3253
3254////////////////////////////////////////////////////////////////////////////////
3255/// Write array of UShort_t to buffer
3256
3258{
3259 JsonPushValue();
3260 JsonWriteArrayCompress(h, n, "Uint16");
3261}
3262
3263////////////////////////////////////////////////////////////////////////////////
3264/// Write array of Int_ to buffer
3265
3267{
3268 JsonPushValue();
3269 JsonWriteArrayCompress(i, n, "Int32");
3270}
3271
3272////////////////////////////////////////////////////////////////////////////////
3273/// Write array of UInt_t to buffer
3274
3276{
3277 JsonPushValue();
3278 JsonWriteArrayCompress(i, n, "Uint32");
3279}
3280
3281////////////////////////////////////////////////////////////////////////////////
3282/// Write array of Long_t to buffer
3283
3285{
3286 JsonPushValue();
3287 JsonWriteArrayCompress(l, n, "Int64");
3288}
3289
3290////////////////////////////////////////////////////////////////////////////////
3291/// Write array of ULong_t to buffer
3292
3294{
3295 JsonPushValue();
3296 JsonWriteArrayCompress(l, n, "Uint64");
3297}
3298
3299////////////////////////////////////////////////////////////////////////////////
3300/// Write array of Long64_t to buffer
3301
3303{
3304 JsonPushValue();
3305 JsonWriteArrayCompress(l, n, "Int64");
3306}
3307
3308////////////////////////////////////////////////////////////////////////////////
3309/// Write array of ULong64_t to buffer
3310
3312{
3313 JsonPushValue();
3314 JsonWriteArrayCompress(l, n, "Uint64");
3315}
3316
3317////////////////////////////////////////////////////////////////////////////////
3318/// Write array of Float_t to buffer
3319
3321{
3322 JsonPushValue();
3323 JsonWriteArrayCompress(f, n, "Float32");
3324}
3325
3326////////////////////////////////////////////////////////////////////////////////
3327/// Write array of Double_t to buffer
3328
3330{
3331 JsonPushValue();
3332 JsonWriteArrayCompress(d, n, "Float64");
3333}
3334
3335////////////////////////////////////////////////////////////////////////////////
3336/// Template method to write array of arbitrary dimensions
3337/// Different methods can be used for store last array dimension -
3338/// either JsonWriteArrayCompress<T>() or JsonWriteConstChar()
3339/// \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.
3340///
3341template <typename T>
3343 void (TBufferJSON::*method)(const T *, Int_t, const char *))
3344{
3345 JsonPushValue();
3346 if (arrsize <= 0) { /*fJsonrCnt++;*/
3347 fValue.Append("[]");
3348 return;
3349 }
3350 const Int_t maxElements = std::numeric_limits<Int_t>::max();
3351 if (arrsize > maxElements) {
3352 Fatal("JsonWriteFastArray", "Array larger than 2^31 elements cannot be stored in JSON");
3353 return; // In case the user re-routes the error handler to not die when Fatal is called
3354 }
3355
3357 if (elem && (elem->GetArrayDim() > 1) && (elem->GetArrayLength() == arrsize)) {
3358 TArrayI indexes(elem->GetArrayDim() - 1);
3359 indexes.Reset(0);
3360 Int_t cnt = 0, shift = 0, len = elem->GetMaxIndex(indexes.GetSize());
3361 while (cnt >= 0) {
3362 if (indexes[cnt] >= elem->GetMaxIndex(cnt)) {
3363 fValue.Append("]");
3364 indexes[cnt--] = 0;
3365 if (cnt >= 0)
3366 indexes[cnt]++;
3367 continue;
3368 }
3369 fValue.Append(indexes[cnt] == 0 ? "[" : fArraySepar.Data());
3370 if (++cnt == indexes.GetSize()) {
3371 (*this.*method)((arr + shift), len, typname);
3372 indexes[--cnt]++;
3373 shift += len;
3374 }
3375 }
3376 } else {
3377 (*this.*method)(arr, arrsize, typname);
3378 }
3379}
3380
3381////////////////////////////////////////////////////////////////////////////////
3382/// Write array of Bool_t to buffer
3383
3385{
3386 JsonWriteFastArray(b, n, "Bool", &TBufferJSON::JsonWriteArrayCompress<Bool_t>);
3387}
3388
3389////////////////////////////////////////////////////////////////////////////////
3390/// Write array of Char_t to buffer
3391///
3392/// Normally written as JSON string, but if string includes \0 in the middle
3393/// or some special characters, uses regular array. From array size 1000 it
3394/// will be automatically converted into base64 coding
3395
3397{
3398 Bool_t need_blob = false;
3399 Bool_t has_zero = false;
3400 for (Long64_t i=0;i<n;++i) {
3401 if (!c[i]) {
3402 has_zero = true; // might be terminal '\0'
3403 } else if (has_zero || !isprint(c[i])) {
3404 need_blob = true;
3405 break;
3406 }
3407 }
3408
3409 if (need_blob && (n >= 1000) && (!Stack()->fElem || (Stack()->fElem->GetArrayDim() < 2)))
3410 Stack()->fBase64 = true;
3411
3412 JsonWriteFastArray(c, n, "Int8", need_blob ? &TBufferJSON::JsonWriteArrayCompress<Char_t> : &TBufferJSON::JsonWriteConstChar);
3413}
3414
3415////////////////////////////////////////////////////////////////////////////////
3416/// Write array of Char_t to buffer
3417
3422
3423////////////////////////////////////////////////////////////////////////////////
3424/// Write array of UChar_t to buffer
3425
3427{
3428 JsonWriteFastArray(c, n, "Uint8", &TBufferJSON::JsonWriteArrayCompress<UChar_t>);
3429}
3430
3431////////////////////////////////////////////////////////////////////////////////
3432/// Write array of Short_t to buffer
3433
3435{
3436 JsonWriteFastArray(h, n, "Int16", &TBufferJSON::JsonWriteArrayCompress<Short_t>);
3437}
3438
3439////////////////////////////////////////////////////////////////////////////////
3440/// Write array of UShort_t to buffer
3441
3443{
3444 JsonWriteFastArray(h, n, "Uint16", &TBufferJSON::JsonWriteArrayCompress<UShort_t>);
3445}
3446
3447////////////////////////////////////////////////////////////////////////////////
3448/// Write array of Int_t to buffer
3449
3451{
3452 JsonWriteFastArray(i, n, "Int32", &TBufferJSON::JsonWriteArrayCompress<Int_t>);
3453}
3454
3455////////////////////////////////////////////////////////////////////////////////
3456/// Write array of UInt_t to buffer
3457
3459{
3460 JsonWriteFastArray(i, n, "Uint32", &TBufferJSON::JsonWriteArrayCompress<UInt_t>);
3461}
3462
3463////////////////////////////////////////////////////////////////////////////////
3464/// Write array of Long_t to buffer
3465
3467{
3468 JsonWriteFastArray(l, n, "Int64", &TBufferJSON::JsonWriteArrayCompress<Long_t>);
3469}
3470
3471////////////////////////////////////////////////////////////////////////////////
3472/// Write array of ULong_t to buffer
3473
3475{
3476 JsonWriteFastArray(l, n, "Uint64", &TBufferJSON::JsonWriteArrayCompress<ULong_t>);
3477}
3478
3479////////////////////////////////////////////////////////////////////////////////
3480/// Write array of Long64_t to buffer
3481
3483{
3484 JsonWriteFastArray(l, n, "Int64", &TBufferJSON::JsonWriteArrayCompress<Long64_t>);
3485}
3486
3487////////////////////////////////////////////////////////////////////////////////
3488/// Write array of ULong64_t to buffer
3489
3491{
3492 JsonWriteFastArray(l, n, "Uint64", &TBufferJSON::JsonWriteArrayCompress<ULong64_t>);
3493}
3494
3495////////////////////////////////////////////////////////////////////////////////
3496/// Write array of Float_t to buffer
3497
3499{
3500 JsonWriteFastArray(f, n, "Float32", &TBufferJSON::JsonWriteArrayCompress<Float_t>);
3501}
3502
3503////////////////////////////////////////////////////////////////////////////////
3504/// Write array of Double_t to buffer
3505
3507{
3508 JsonWriteFastArray(d, n, "Float64", &TBufferJSON::JsonWriteArrayCompress<Double_t>);
3509}
3510
3511////////////////////////////////////////////////////////////////////////////////
3512/// Recall TBuffer function to avoid gcc warning message
3513
3514void TBufferJSON::WriteFastArray(void *start, const TClass *cl, Long64_t n, TMemberStreamer * /* streamer */)
3515{
3516 if (gDebug > 2)
3517 Info("WriteFastArray", "void *start cl:%s n:%lld", cl ? cl->GetName() : "---", n);
3518
3519 // if (streamer) {
3520 // JsonDisablePostprocessing();
3521 // (*streamer)(*this, start, 0);
3522 // return;
3523 // }
3524
3525 if (n < 0) {
3526 // special handling of empty StreamLoop
3527 AppendOutput("null");
3529 } else {
3530
3531 char *obj = (char *)start;
3532 if (!n)
3533 n = 1;
3534 int size = cl->Size();
3535
3537
3538 if (indexes.IsArray()) {
3540 AppendOutput(indexes.GetBegin());
3541 }
3542
3543 for (Long64_t j = 0; j < n; j++, obj += size) {
3544
3545 if (j > 0)
3546 AppendOutput(indexes.NextSeparator());
3547
3548 JsonWriteObject(obj, cl, kFALSE);
3549
3550 if (indexes.IsArray() && (fValue.Length() > 0)) {
3552 fValue.Clear();
3553 }
3554 }
3555
3556 if (indexes.IsArray())
3557 AppendOutput(indexes.GetEnd());
3558 }
3559
3560 if (Stack()->fIndx)
3561 AppendOutput(Stack()->fIndx->NextSeparator());
3562}
3563
3564////////////////////////////////////////////////////////////////////////////////
3565/// Recall TBuffer function to avoid gcc warning message
3566
3568 TMemberStreamer * /* streamer */)
3569{
3570 if (gDebug > 2)
3571 Info("WriteFastArray", "void **startp cl:%s n:%lld", cl->GetName(), n);
3572
3573 // if (streamer) {
3574 // JsonDisablePostprocessing();
3575 // (*streamer)(*this, (void *)start, 0);
3576 // return 0;
3577 // }
3578
3579 if (n <= 0)
3580 return 0;
3581
3582 Int_t res = 0;
3583
3585
3586 if (indexes.IsArray()) {
3588 AppendOutput(indexes.GetBegin());
3589 }
3590
3591 for (Long64_t j = 0; j < n; j++) {
3592
3593 if (j > 0)
3594 AppendOutput(indexes.NextSeparator());
3595
3596 if (!isPreAlloc) {
3597 res |= WriteObjectAny(start[j], cl);
3598 } else {
3599 if (!start[j])
3600 start[j] = (const_cast<TClass *>(cl))->New();
3601 // ((TClass*)cl)->Streamer(start[j],*this);
3602 JsonWriteObject(start[j], cl, kFALSE);
3603 }
3604
3605 if (indexes.IsArray() && (fValue.Length() > 0)) {
3607 fValue.Clear();
3608 }
3609 }
3610
3611 if (indexes.IsArray())
3612 AppendOutput(indexes.GetEnd());
3613
3614 if (Stack()->fIndx)
3615 AppendOutput(Stack()->fIndx->NextSeparator());
3616
3617 return res;
3618}
3619
3620////////////////////////////////////////////////////////////////////////////////
3621/// stream object to/from buffer
3622
3623void TBufferJSON::StreamObject(void *obj, const TClass *cl, const TClass * /* onfileClass */)
3624{
3625 if (gDebug > 3)
3626 Info("StreamObject", "Class: %s", (cl ? cl->GetName() : "none"));
3627
3628 if (IsWriting())
3629 JsonWriteObject(obj, cl);
3630 else
3631 JsonReadObject(obj, cl);
3632}
3633
3634////////////////////////////////////////////////////////////////////////////////
3635/// Template function to read basic value from JSON
3636
3637template <typename T>
3639{
3640 value = Stack()->GetStlNode()->get<T>();
3641}
3642
3643////////////////////////////////////////////////////////////////////////////////
3644/// Reads Bool_t value from buffer
3645
3647{
3648 JsonReadBasic(val);
3649}
3650
3651////////////////////////////////////////////////////////////////////////////////
3652/// Reads Char_t value from buffer
3653
3655{
3656 if (!Stack()->fValues.empty())
3657 val = (Char_t)Stack()->PopIntValue();
3658 else
3659 val = Stack()->GetStlNode()->get<Char_t>();
3660}
3661
3662////////////////////////////////////////////////////////////////////////////////
3663/// Reads UChar_t value from buffer
3664
3666{
3667 JsonReadBasic(val);
3668}
3669
3670////////////////////////////////////////////////////////////////////////////////
3671/// Reads Short_t value from buffer
3672
3674{
3675 JsonReadBasic(val);
3676}
3677
3678////////////////////////////////////////////////////////////////////////////////
3679/// Reads UShort_t value from buffer
3680
3682{
3683 JsonReadBasic(val);
3684}
3685
3686////////////////////////////////////////////////////////////////////////////////
3687/// Reads Int_t value from buffer
3688
3690{
3691 if (!Stack()->fValues.empty())
3692 val = Stack()->PopIntValue();
3693 else
3694 JsonReadBasic(val);
3695}
3696
3697////////////////////////////////////////////////////////////////////////////////
3698/// Reads UInt_t value from buffer
3699
3701{
3702 JsonReadBasic(val);
3703}
3704
3705////////////////////////////////////////////////////////////////////////////////
3706/// Reads Long_t value from buffer
3707
3709{
3710 JsonReadBasic(val);
3711}
3712
3713////////////////////////////////////////////////////////////////////////////////
3714/// Reads ULong_t value from buffer
3715
3717{
3718 JsonReadBasic(val);
3719}
3720
3721////////////////////////////////////////////////////////////////////////////////
3722/// Reads Long64_t value from buffer
3723
3725{
3726 JsonReadBasic(val);
3727}
3728
3729////////////////////////////////////////////////////////////////////////////////
3730/// Reads ULong64_t value from buffer
3731
3733{
3734 JsonReadBasic(val);
3735}
3736
3737////////////////////////////////////////////////////////////////////////////////
3738/// Reads Float_t value from buffer
3739
3741{
3742 nlohmann::json *json = Stack()->GetStlNode();
3743 if (json->is_null())
3744 val = std::numeric_limits<Float_t>::quiet_NaN();
3745 else
3746 try {
3747 val = json->get<Float_t>();
3748 } catch (nlohmann::detail::type_error &e) {
3749 auto aux = json->get<std::string>();
3750 if (aux == "nanf") {
3751 val = std::numeric_limits<Float_t>::quiet_NaN();
3752 } else if (aux == "inff") {
3753 val = std::numeric_limits<Float_t>::infinity();
3754 } else if (aux == "-inff") {
3755 val = -std::numeric_limits<Float_t>::infinity();
3756 } else {
3757 Error("ReadFloat", "%s '%s'", e.what(), aux.c_str());
3758 val = std::numeric_limits<Float_t>::quiet_NaN();
3759 }
3760 }
3761}
3762
3763////////////////////////////////////////////////////////////////////////////////
3764/// Reads Double_t value from buffer
3765
3767{
3768 nlohmann::json *json = Stack()->GetStlNode();
3769 if (json->is_null())
3770 val = std::numeric_limits<Double_t>::quiet_NaN();
3771 else
3772 try {
3773 val = json->get<Double_t>();
3774 } catch (nlohmann::detail::type_error &e) {
3775 auto aux = json->get<std::string>();
3776 if (aux == "nan") {
3777 val = std::numeric_limits<Double_t>::quiet_NaN();
3778 } else if (aux == "inf") {
3779 val = std::numeric_limits<Double_t>::infinity();
3780 } else if (aux == "-inf") {
3781 val = -std::numeric_limits<Double_t>::infinity();
3782 } else {
3783 Error("ReadDouble", "%s '%s'", e.what(), aux.c_str());
3784 val = std::numeric_limits<Double_t>::quiet_NaN();
3785 }
3786 }
3787}
3788
3789////////////////////////////////////////////////////////////////////////////////
3790/// Reads array of characters from buffer
3791
3793{
3794 Error("ReadCharP", "Not implemented");
3795}
3796
3797////////////////////////////////////////////////////////////////////////////////
3798/// Reads a TString
3799
3801{
3802 std::string str;
3803 JsonReadBasic(str);
3804 val = str.c_str();
3805}
3806
3807////////////////////////////////////////////////////////////////////////////////
3808/// Reads a std::string
3809
3810void TBufferJSON::ReadStdString(std::string *val)
3811{
3812 JsonReadBasic(*val);
3813}
3814
3815////////////////////////////////////////////////////////////////////////////////
3816/// Reads a char* string
3817
3819{
3820 std::string str;
3821 JsonReadBasic(str);
3822
3823 if (s) {
3824 delete[] s;
3825 s = nullptr;
3826 }
3827
3828 std::size_t nch = str.length();
3829 if (nch > 0) {
3830 s = new char[nch + 1];
3831 memcpy(s, str.c_str(), nch);
3832 s[nch] = 0;
3833 }
3834}
3835
3836////////////////////////////////////////////////////////////////////////////////
3837/// Writes Bool_t value to buffer
3838
3844
3845////////////////////////////////////////////////////////////////////////////////
3846/// Writes Char_t value to buffer
3847
3853
3854////////////////////////////////////////////////////////////////////////////////
3855/// Writes UChar_t value to buffer
3856
3862
3863////////////////////////////////////////////////////////////////////////////////
3864/// Writes Short_t value to buffer
3865
3871
3872////////////////////////////////////////////////////////////////////////////////
3873/// Writes UShort_t value to buffer
3874
3880
3881////////////////////////////////////////////////////////////////////////////////
3882/// Writes Int_t value to buffer
3883
3885{
3886 JsonPushValue();
3887 JsonWriteBasic(i);
3888}
3889
3890////////////////////////////////////////////////////////////////////////////////
3891/// Writes UInt_t value to buffer
3892
3894{
3895 JsonPushValue();
3896 JsonWriteBasic(i);
3897}
3898
3899////////////////////////////////////////////////////////////////////////////////
3900/// Writes Long_t value to buffer
3901
3907
3908////////////////////////////////////////////////////////////////////////////////
3909/// Writes ULong_t value to buffer
3910
3916
3917////////////////////////////////////////////////////////////////////////////////
3918/// Writes Long64_t value to buffer
3919
3925
3926////////////////////////////////////////////////////////////////////////////////
3927/// Writes ULong64_t value to buffer
3928
3934
3935////////////////////////////////////////////////////////////////////////////////
3936/// Writes Float_t value to buffer
3937
3943
3944////////////////////////////////////////////////////////////////////////////////
3945/// Writes Double_t value to buffer
3946
3952
3953////////////////////////////////////////////////////////////////////////////////
3954/// Writes array of characters to buffer
3955
3957{
3958 JsonPushValue();
3959
3961}
3962
3963////////////////////////////////////////////////////////////////////////////////
3964/// Writes a TString
3965
3967{
3968 JsonPushValue();
3969
3970 JsonWriteConstChar(s.Data(), s.Length());
3971}
3972
3973////////////////////////////////////////////////////////////////////////////////
3974/// Writes a std::string
3975
3976void TBufferJSON::WriteStdString(const std::string *s)
3977{
3978 JsonPushValue();
3979
3980 if (s)
3981 JsonWriteConstChar(s->c_str(), s->length());
3982 else
3983 JsonWriteConstChar("", 0);
3984}
3985
3986////////////////////////////////////////////////////////////////////////////////
3987/// Writes a char*
3988
3990{
3991 JsonPushValue();
3992
3994}
3995
3996////////////////////////////////////////////////////////////////////////////////
3997/// converts Char_t to string and add to json value buffer
3998
4000{
4001 char buf[50];
4002 snprintf(buf, sizeof(buf), "%d", value);
4003 fValue.Append(buf);
4004}
4005
4006////////////////////////////////////////////////////////////////////////////////
4007/// converts Short_t to string and add to json value buffer
4008
4010{
4011 char buf[50];
4012 snprintf(buf, sizeof(buf), "%hd", value);
4013 fValue.Append(buf);
4014}
4015
4016////////////////////////////////////////////////////////////////////////////////
4017/// converts Int_t to string and add to json value buffer
4018
4020{
4021 char buf[50];
4022 snprintf(buf, sizeof(buf), "%d", value);
4023 fValue.Append(buf);
4024}
4025
4026////////////////////////////////////////////////////////////////////////////////
4027/// converts Long_t to string and add to json value buffer
4028
4030{
4031 char buf[50];
4032 snprintf(buf, sizeof(buf), "%ld", value);
4033 fValue.Append(buf);
4034}
4035
4036////////////////////////////////////////////////////////////////////////////////
4037/// converts Long64_t to string and add to json value buffer
4038
4040{
4041 fValue.Append(std::to_string(value).c_str());
4042}
4043
4044////////////////////////////////////////////////////////////////////////////////
4045/// converts Float_t to string and add to json value buffer
4046
4048{
4049 if (std::isinf(value)) {
4050 if (!fStoreInfNaN)
4051 fValue.Append((value < 0.) ? "-2e308" : "2e308"); // JavaScript Number.MAX_VALUE is approx 1.79e308
4052 else
4053 fValue.Append((value < 0.) ? "\"-inff\"" : "\"inff\"");
4054 } else if (std::isnan(value)) {
4055 if (!fStoreInfNaN)
4056 fValue.Append("null");
4057 else
4058 fValue.Append("\"nanf\"");
4059 } else {
4060 char buf[200];
4061 ConvertFloat(value, buf, sizeof(buf));
4062 fValue.Append(buf);
4063 }
4064}
4065
4066////////////////////////////////////////////////////////////////////////////////
4067/// converts Double_t to string and add to json value buffer
4068
4070{
4071 if (std::isinf(value)) {
4072 if (!fStoreInfNaN)
4073 fValue.Append((value < 0.) ? "-2e308" : "2e308"); // JavaScript Number.MAX_VALUE is approx 1.79e308
4074 else
4075 fValue.Append((value < 0.) ? "\"-inf\"" : "\"inf\"");
4076 } else if (std::isnan(value)) {
4077 if (!fStoreInfNaN)
4078 fValue.Append("null");
4079 else
4080 fValue.Append("\"nan\"");
4081 } else {
4082 char buf[200];
4083 ConvertDouble(value, buf, sizeof(buf));
4084 fValue.Append(buf);
4085 }
4086}
4087
4088////////////////////////////////////////////////////////////////////////////////
4089/// converts Bool_t to string and add to json value buffer
4090
4092{
4093 fValue.Append(value ? "true" : "false");
4094}
4095
4096////////////////////////////////////////////////////////////////////////////////
4097/// converts UChar_t to string and add to json value buffer
4098
4100{
4101 char buf[50];
4102 snprintf(buf, sizeof(buf), "%u", value);
4103 fValue.Append(buf);
4104}
4105
4106////////////////////////////////////////////////////////////////////////////////
4107/// converts UShort_t to string and add to json value buffer
4108
4110{
4111 char buf[50];
4112 snprintf(buf, sizeof(buf), "%hu", value);
4113 fValue.Append(buf);
4114}
4115
4116////////////////////////////////////////////////////////////////////////////////
4117/// converts UInt_t to string and add to json value buffer
4118
4120{
4121 char buf[50];
4122 snprintf(buf, sizeof(buf), "%u", value);
4123 fValue.Append(buf);
4124}
4125
4126////////////////////////////////////////////////////////////////////////////////
4127/// converts ULong_t to string and add to json value buffer
4128
4130{
4131 char buf[50];
4132 snprintf(buf, sizeof(buf), "%lu", value);
4133 fValue.Append(buf);
4134}
4135
4136////////////////////////////////////////////////////////////////////////////////
4137/// converts ULong64_t to string and add to json value buffer
4138
4140{
4141 fValue.Append(std::to_string(value).c_str());
4142}
4143
4144////////////////////////////////////////////////////////////////////////////////
4145/// writes string value, processing all kind of special characters
4146
4147void TBufferJSON::JsonWriteConstChar(const char *value, Int_t len, const char * /* typname */)
4148{
4149 if (!value) {
4150
4151 fValue.Append("\"\"");
4152
4153 } else {
4154
4155 fValue.Append("\"");
4156
4157 if (len < 0)
4158 len = strlen(value);
4159
4160 for (Int_t n = 0; n < len; n++) {
4161 unsigned char c = value[n];
4162 switch (c) {
4163 case 0: n = len; break;
4164 case '\n': fValue.Append("\\n"); break;
4165 case '\t': fValue.Append("\\t"); break;
4166 case '\"': fValue.Append("\\\""); break;
4167 case '\\': fValue.Append("\\\\"); break;
4168 case '\b': fValue.Append("\\b"); break;
4169 case '\f': fValue.Append("\\f"); break;
4170 case '\r': fValue.Append("\\r"); break;
4171 case '/': fValue.Append("\\/"); break;
4172 default:
4173 if (c < 31) {
4174 fValue.Append(TString::Format("\\u%04x", (unsigned)c));
4175 } else if (c < 0x80) {
4176 fValue.Append(c);
4177 } else if ((n < len - 1) && ((c & 0xe0) == 0xc0) && ((value[n+1] & 0xc0) == 0x80)) {
4178 unsigned code = ((unsigned)value[n+1] & 0x3f) | (((unsigned) c & 0x1f) << 6);
4179 fValue.Append(TString::Format("\\u%04x", code));
4180 n++;
4181 } else if ((n < len - 2) && ((c & 0xf0) == 0xe0) && ((value[n+1] & 0xc0) == 0x80) && ((value[n+2] & 0xc0) == 0x80)) {
4182 unsigned code = ((unsigned)value[n+2] & 0x3f) | (((unsigned) value[n+1] & 0x3f) << 6) | (((unsigned) c & 0x0f) << 12);
4183 fValue.Append(TString::Format("\\u%04x", code));
4184 n+=2;
4185 } else if ((n < len - 3) && ((c & 0xf8) == 0xf0) && ((value[n+1] & 0xc0) == 0x80) && ((value[n+2] & 0xc0) == 0x80) && ((value[n+3] & 0xc0) == 0x80)) {
4186 unsigned code = ((unsigned)value[n+3] & 0x3f) | (((unsigned) value[n+2] & 0x3f) << 6) | (((unsigned) value[n+1] & 0x3f) << 12) | (((unsigned) c & 0x07) << 18);
4187 // TODO: no idea how to add codes which are higher then 0xFFFF
4188 fValue.Append(TString::Format("\\u%04x\\u%04x", code & 0xffff, code >> 16));
4189 n+=3;
4190 } else {
4191 fValue.Append(TString::Format("\\u%04x", (unsigned)c));
4192 }
4193 }
4194 }
4195
4196 fValue.Append("\"");
4197 }
4198}
4199
4200////////////////////////////////////////////////////////////////////////////////
4201/// Read data of base class.
4202
4204{
4205 if (elem->GetClassPointer() == TObject::Class()) {
4207 } else {
4209 }
4210}
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:414
#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
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
Int_t PopIntValue()
nlohmann::json * GetStlNode()
Bool_t AssignStl(TClass *cl, Int_t map_convert, const char *typename_tag)
Bool_t fIsPostProcessed
! indicate that value is written
Bool_t IsStreamerInfo() const
Bool_t fIsStreamerInfo
!
void PushValue(TString &v)
Bool_t IsStl() const
TStreamerInfo * fInfo
!
~TJSONStackObj() override
Bool_t IsStreamerElement() const
std::unique_ptr< TArrayIndexProducer > MakeReadIndexes()
int fMemberCnt
! count number of object members, normally _typename is first member
nlohmann::json * fNode
! JSON node, used for reading
int * fMemberPtr
! pointer on members counter, can be inherit from parent stack objects
std::vector< std::string > fValues
! raw values
Bool_t fIsElemOwner
!
Bool_t fAccObjects
! if true, accumulate whole objects in values
std::unique_ptr< StlRead > fStlRead
! custom structure for stl container reading
Version_t fClVersion
! keep actual class version, workaround for ReadVersion in custom streamer
void PushIntValue(Int_t v)
Int_t fLevel
! indent level
std::unique_ptr< TArrayIndexProducer > fIndx
! producer of ndim indexes
TStreamerElement * fElem
! element in streamer info
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 object writing started, should be closed in postprocess
Bool_t fBase64
! enable base64 coding when writing array
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:42
@ kIsOnHeap
object is on heap
Definition TObject.h:89
@ kNotDeleted
object has not been deleted
Definition TObject.h:90
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:1075
static TClass * Class()
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:544
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1089
virtual void Fatal(const char *method, const char *msgfmt,...) const
Issue fatal error message.
Definition TObject.cxx:1117
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1063
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
! type tag used for std::map stored as JSON object
nlohmann::json fValue
! temporary value reading std::map as JSON
Bool_t fFirst
! is first or second element is used in the pair
nlohmann::json * GetStlNode(nlohmann::json *prnt)
nlohmann::json::iterator fIter
! iterator for std::map stored as JSON object
Int_t fIndx
! index of object in STL container
Int_t fMap
! special iterator over STL map::key members
TLine l
Definition textangle.C:4