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