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