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 if (!ziplen) {
847 free(buffer);
848 return 0;
849 }
850
851 memcpy(bufcur - 6, dummy, 6);
852
853 bufcur += (ziplen - 6); // jump over compressed data (6 byte is extra ROOT header)
854
855 *bufcur++ = objcrc & 0xff; // CRC32
856 *bufcur++ = (objcrc >> 8) & 0xff;
857 *bufcur++ = (objcrc >> 16) & 0xff;
858 *bufcur++ = (objcrc >> 24) & 0xff;
859
860 *bufcur++ = objlen & 0xff; // original data length
861 *bufcur++ = (objlen >> 8) & 0xff; // original data length
862 *bufcur++ = (objlen >> 16) & 0xff; // original data length
863 *bufcur++ = (objlen >> 24) & 0xff; // original data length
864
865 ofs.write(buffer, bufcur - buffer);
866
867 free(buffer);
868 } else {
869 ofs << json.Data();
870 }
871
872 ofs.close();
873
874 return json.Length();
875}
876
877////////////////////////////////////////////////////////////////////////////////
878/// Convert object into JSON and store in text file
879/// Returns size of the produce file
880
881Int_t TBufferJSON::ExportToFile(const char *filename, const void *obj, const TClass *cl, const char *option)
882{
883 if (!obj || !cl || !filename || (*filename == 0))
884 return 0;
885
886 Int_t compact = strstr(filename, ".json.gz") ? 3 : 0;
887 if (option && (*option >= '0') && (*option <= '3'))
889
891
892 std::ofstream ofs(filename);
893
894 if (strstr(filename, ".json.gz")) {
895 const char *objbuf = json.Data();
896 Long_t objlen = json.Length();
897
898 unsigned long objcrc = R__crc32(0, nullptr, 0);
899 objcrc = R__crc32(objcrc, (const unsigned char *)objbuf, objlen);
900
901 // 10 bytes (ZIP header), compressed data, 8 bytes (CRC and original length)
902 Int_t buflen = 10 + objlen + 8;
903 if (buflen < 512)
904 buflen = 512;
905
906 char *buffer = (char *)malloc(buflen);
907 if (!buffer)
908 return 0; // failure
909
910 char *bufcur = buffer;
911
912 *bufcur++ = 0x1f; // first byte of ZIP identifier
913 *bufcur++ = 0x8b; // second byte of ZIP identifier
914 *bufcur++ = 0x08; // compression method
915 *bufcur++ = 0x00; // FLAG - empty, no any file names
916 *bufcur++ = 0; // empty timestamp
917 *bufcur++ = 0; //
918 *bufcur++ = 0; //
919 *bufcur++ = 0; //
920 *bufcur++ = 0; // XFL (eXtra FLags)
921 *bufcur++ = 3; // OS 3 means Unix
922 // strcpy(bufcur, "item.json");
923 // bufcur += strlen("item.json")+1;
924
925 char dummy[8];
926 memcpy(dummy, bufcur - 6, 6);
927
928 // R__memcompress fills first 6 bytes with own header, therefore just overwrite them
929 unsigned long ziplen = R__memcompress(bufcur - 6, objlen + 6, (char *)objbuf, objlen);
930
931 memcpy(bufcur - 6, dummy, 6);
932
933 bufcur += (ziplen - 6); // jump over compressed data (6 byte is extra ROOT header)
934
935 *bufcur++ = objcrc & 0xff; // CRC32
936 *bufcur++ = (objcrc >> 8) & 0xff;
937 *bufcur++ = (objcrc >> 16) & 0xff;
938 *bufcur++ = (objcrc >> 24) & 0xff;
939
940 *bufcur++ = objlen & 0xff; // original data length
941 *bufcur++ = (objlen >> 8) & 0xff; // original data length
942 *bufcur++ = (objlen >> 16) & 0xff; // original data length
943 *bufcur++ = (objlen >> 24) & 0xff; // original data length
944
945 ofs.write(buffer, bufcur - buffer);
946
947 free(buffer);
948 } else {
949 ofs << json.Data();
950 }
951
952 ofs.close();
953
954 return json.Length();
955}
956
957////////////////////////////////////////////////////////////////////////////////
958/// Read TObject-based class from JSON, produced by ConvertToJSON() method.
959/// If object does not inherit from TObject class, return 0.
960
962{
963 TClass *cl = nullptr;
964 void *obj = ConvertFromJSONAny(str, &cl);
965
966 if (!cl || !obj)
967 return nullptr;
968
970
971 if (delta < 0) {
972 cl->Destructor(obj);
973 return nullptr;
974 }
975
976 return (TObject *)(((char *)obj) + delta);
977}
978
979////////////////////////////////////////////////////////////////////////////////
980/// Read object from JSON
981/// In class pointer (if specified) read class is returned
982/// One must specify expected object class, if it is TArray or STL container
983
984void *TBufferJSON::ConvertFromJSONAny(const char *str, TClass **cl)
985{
987
988 return buf.RestoreObject(str, cl);
989}
990
991////////////////////////////////////////////////////////////////////////////////
992/// Read object from JSON
993/// In class pointer (if specified) read class is returned
994/// One must specify expected object class, if it is TArray or STL container
995
997{
998 if (!IsReading())
999 return nullptr;
1000
1001 nlohmann::json docu = nlohmann::json::parse(json_str);
1002
1003 if (docu.is_null() || (!docu.is_object() && !docu.is_array()))
1004 return nullptr;
1005
1006 TClass *objClass = nullptr;
1007
1008 if (cl) {
1009 objClass = *cl; // this is class which suppose to created when reading JSON
1010 *cl = nullptr;
1011 }
1012
1013 InitMap();
1014
1015 PushStack(0, &docu);
1016
1017 void *obj = JsonReadObject(nullptr, objClass, cl);
1018
1019 PopStack();
1020
1021 return obj;
1022}
1023
1024////////////////////////////////////////////////////////////////////////////////
1025/// Read objects from JSON, one can reuse existing object
1026
1028{
1029 if (!expectedClass)
1030 return nullptr;
1031
1032 TClass *resClass = const_cast<TClass *>(expectedClass);
1033
1034 void *res = ConvertFromJSONAny(str, &resClass);
1035
1036 if (!res || !resClass)
1037 return nullptr;
1038
1039 if (resClass == expectedClass)
1040 return res;
1041
1042 Int_t offset = resClass->GetBaseClassOffset(expectedClass);
1043 if (offset < 0) {
1044 ::Error("TBufferJSON::ConvertFromJSONChecked", "expected class %s is not base for read class %s",
1045 expectedClass->GetName(), resClass->GetName());
1046 resClass->Destructor(res);
1047 return nullptr;
1048 }
1049
1050 return (char *)res - offset;
1051}
1052
1053////////////////////////////////////////////////////////////////////////////////
1054/// Convert single data member to JSON structures
1055/// Note; if data member described by 'member'is pointer, `ptr` should be the
1056/// value of the pointer, not the address of the pointer.
1057/// Returns string with converted member
1058
1060{
1061 if (!member)
1062 return "null";
1063
1064 if (gDebug > 2)
1065 Info("JsonWriteMember", "Write member %s type %s ndim %d", member->GetName(), member->GetTrueTypeName(),
1066 member->GetArrayDim());
1067
1068 Int_t tid = member->GetDataType() ? member->GetDataType()->GetType() : kNoType_t;
1069 if (strcmp(member->GetTrueTypeName(), "const char*") == 0)
1070 tid = kCharStar;
1071 else if (!member->IsBasic() || (tid == kOther_t) || (tid == kVoid_t))
1072 tid = kNoType_t;
1073
1074 if (!ptr)
1075 return (tid == kCharStar) ? "\"\"" : "null";
1076
1077 PushStack(0);
1078 fValue.Clear();
1079
1080 if (tid != kNoType_t) {
1081
1083
1084 Int_t shift = 1;
1085
1086 if (indx.IsArray() && (tid == kChar_t))
1087 shift = indx.ReduceDimension();
1088
1089 auto unitSize = member->GetUnitSize();
1090 char *ppp = (char *)ptr;
1091 if (member->IsaPointer()) {
1092 // UnitSize was the sizeof(void*)
1093 assert(member->GetDataType());
1094 unitSize = member->GetDataType()->Size();
1095 }
1096
1097 if (indx.IsArray())
1098 fOutBuffer.Append(indx.GetBegin());
1099
1100 do {
1101 fValue.Clear();
1102
1103 switch (tid) {
1104 case kChar_t:
1105 if (shift > 1)
1106 JsonWriteConstChar((Char_t *)ppp, shift);
1107 else
1108 JsonWriteBasic(*((Char_t *)ppp));
1109 break;
1110 case kShort_t: JsonWriteBasic(*((Short_t *)ppp)); break;
1111 case kInt_t: JsonWriteBasic(*((Int_t *)ppp)); break;
1112 case kLong_t: JsonWriteBasic(*((Long_t *)ppp)); break;
1113 case kFloat_t: JsonWriteBasic(*((Float_t *)ppp)); break;
1114 case kCounter: JsonWriteBasic(*((Int_t *)ppp)); break;
1115 case kCharStar: JsonWriteConstChar((Char_t *)ppp); break;
1116 case kDouble_t: JsonWriteBasic(*((Double_t *)ppp)); break;
1117 case kDouble32_t: JsonWriteBasic(*((Double_t *)ppp)); break;
1118 case kchar: JsonWriteBasic(*((char *)ppp)); break;
1119 case kUChar_t: JsonWriteBasic(*((UChar_t *)ppp)); break;
1120 case kUShort_t: JsonWriteBasic(*((UShort_t *)ppp)); break;
1121 case kUInt_t: JsonWriteBasic(*((UInt_t *)ppp)); break;
1122 case kULong_t: JsonWriteBasic(*((ULong_t *)ppp)); break;
1123 case kBits: JsonWriteBasic(*((UInt_t *)ppp)); break;
1124 case kLong64_t: JsonWriteBasic(*((Long64_t *)ppp)); break;
1125 case kULong64_t: JsonWriteBasic(*((ULong64_t *)ppp)); break;
1126 case kBool_t: JsonWriteBasic(*((Bool_t *)ppp)); break;
1127 case kFloat16_t: JsonWriteBasic(*((Float_t *)ppp)); break;
1128 case kOther_t:
1129 case kVoid_t: break;
1130 }
1131
1133 if (indx.IsArray())
1134 fOutBuffer.Append(indx.NextSeparator());
1135
1136 ppp += shift * unitSize;
1137
1138 } while (!indx.IsDone());
1139
1141
1142 } else if (memberClass == TString::Class()) {
1143 TString *str = (TString *)ptr;
1144 JsonWriteConstChar(str ? str->Data() : nullptr);
1145 } else if ((member->IsSTLContainer() == ROOT::kSTLvector) || (member->IsSTLContainer() == ROOT::kSTLlist) ||
1146 (member->IsSTLContainer() == ROOT::kSTLforwardlist)) {
1147
1148 if (memberClass)
1149 memberClass->Streamer((void *)ptr, *this);
1150 else
1151 fValue = "[]";
1152
1153 if (fValue == "0")
1154 fValue = "[]";
1155
1156 } else if (memberClass && memberClass->GetBaseClassOffset(TArray::Class()) == 0) {
1157 TArray *arr = (TArray *)ptr;
1158 if (arr && (arr->GetSize() > 0)) {
1159 arr->Streamer(*this);
1160 // WriteFastArray(arr->GetArray(), arr->GetSize());
1161 if (Stack()->fValues.size() > 1) {
1162 Warning("TBufferJSON", "When streaming TArray, more than 1 object in the stack, use second item");
1163 fValue = Stack()->fValues[1].c_str();
1164 }
1165 } else
1166 fValue = "[]";
1167 } else if (memberClass && !strcmp(memberClass->GetName(), "string")) {
1168 // here value contains quotes, stack can be ignored
1169 memberClass->Streamer((void *)ptr, *this);
1170 }
1171 PopStack();
1172
1173 if (fValue.Length())
1174 return fValue;
1175
1176 if (!memberClass || (member->GetArrayDim() > 0) || (arraylen > 0))
1177 return "<not supported>";
1178
1180}
1181
1182////////////////////////////////////////////////////////////////////////////////
1183/// add new level to the structures stack
1184
1186{
1187 auto next = new TJSONStackObj();
1188 next->fLevel = inclevel;
1189 if (IsReading()) {
1190 next->fNode = (nlohmann::json *)readnode;
1191 } else if (fStack.size() > 0) {
1192 auto prev = Stack();
1193 next->fLevel += prev->fLevel;
1194 next->fMemberPtr = prev->fMemberPtr;
1195 }
1196 fStack.emplace_back(next);
1197 return next;
1198}
1199
1200////////////////////////////////////////////////////////////////////////////////
1201/// remove one level from stack
1202
1204{
1205 if (fStack.size() > 0)
1206 fStack.pop_back();
1207
1208 return fStack.size() > 0 ? fStack.back().get() : nullptr;
1209}
1210
1211////////////////////////////////////////////////////////////////////////////////
1212/// Append two string to the output JSON, normally separate by line break
1213
1214void TBufferJSON::AppendOutput(const char *line0, const char *line1)
1215{
1216 if (line0)
1218
1219 if (line1) {
1220 if (fCompact < 2)
1221 fOutput->Append("\n");
1222
1223 if (strlen(line1) > 0) {
1224 if (fCompact < 1) {
1225 if (Stack()->fLevel > 0)
1226 fOutput->Append(' ', Stack()->fLevel);
1227 }
1228 fOutput->Append(line1);
1229 }
1230 }
1231}
1232
1233////////////////////////////////////////////////////////////////////////////////
1234/// Start object element with typeinfo
1235
1237{
1238 auto stack = PushStack(2);
1239
1240 // new object started - assign own member counter
1241 stack->fMemberPtr = &stack->fMemberCnt;
1242
1243 if ((fTypeNameTag.Length() > 0) && !IsSkipClassInfo(obj_class)) {
1244 // stack->fMemberCnt = 1; // default value, comment out here
1245 AppendOutput("{", "\"");
1247 AppendOutput("\"");
1249 AppendOutput("\"");
1250 AppendOutput(obj_class->GetName());
1251 AppendOutput("\"");
1252 if (fTypeVersionTag.Length() > 0) {
1253 AppendOutput(stack->NextMemberSeparator(), "\"");
1255 AppendOutput("\"");
1257 AppendOutput(TString::Format("%d", (int)(info ? info->GetClassVersion() : obj_class->GetClassVersion())));
1258 }
1259 } else {
1260 stack->fMemberCnt = 0; // exclude typename
1261 AppendOutput("{");
1262 }
1263
1264 return stack;
1265}
1266
1267////////////////////////////////////////////////////////////////////////////////
1268/// Start new class member in JSON structures
1269
1271{
1272 const char *elem_name = nullptr;
1274
1275 switch (special_kind) {
1276 case 0:
1277 if (base_class) return;
1278 elem_name = elem->GetName();
1279 if (strcmp(elem_name,"fLineStyle") == 0)
1280 if ((strcmp(elem->GetTypeName(),"TString") == 0) && (strcmp(elem->GetFullName(),"fLineStyle[30]") == 0)) {
1281 auto st1 = fStack.at(fStack.size() - 2).get();
1282 if (st1->IsStreamerInfo() && st1->fInfo && (strcmp(st1->fInfo->GetName(),"TStyle") == 0))
1283 elem_name = "fLineStyles";
1284 }
1285 break;
1286 case ROOT::ESTLType::kSTLvector: elem_name = "fVector"; break;
1287 case ROOT::ESTLType::kSTLlist: elem_name = "fList"; break;
1288 case ROOT::ESTLType::kSTLforwardlist: elem_name = "fForwardlist"; break;
1289 case ROOT::ESTLType::kSTLdeque: elem_name = "fDeque"; break;
1290 case ROOT::ESTLType::kSTLmap: elem_name = "fMap"; break;
1291 case ROOT::ESTLType::kSTLmultimap: elem_name = "fMultiMap"; break;
1292 case ROOT::ESTLType::kSTLset: elem_name = "fSet"; break;
1293 case ROOT::ESTLType::kSTLmultiset: elem_name = "fMultiSet"; break;
1294 case ROOT::ESTLType::kSTLunorderedset: elem_name = "fUnorderedSet"; break;
1295 case ROOT::ESTLType::kSTLunorderedmultiset: elem_name = "fUnorderedMultiSet"; break;
1296 case ROOT::ESTLType::kSTLunorderedmap: elem_name = "fUnorderedMap"; break;
1297 case ROOT::ESTLType::kSTLunorderedmultimap: elem_name = "fUnorderedMultiMap"; break;
1298 case ROOT::ESTLType::kSTLbitset: elem_name = "fBitSet"; break;
1299 case json_TArray: elem_name = "fArray"; break;
1300 case json_TString:
1301 case json_stdstring: elem_name = "fString"; break;
1302 }
1303
1304 if (!elem_name)
1305 return;
1306
1307 if (IsReading()) {
1308 nlohmann::json *json = Stack()->fNode;
1309
1310 if (json->count(elem_name) != 1) {
1311 Error("JsonStartElement", "Missing JSON structure for element %s", elem_name);
1312 } else {
1313 Stack()->fNode = &((*json)[elem_name]);
1314 if (special_kind == json_TArray) {
1315 Int_t len = Stack()->IsJsonArray();
1316 Stack()->PushIntValue(len > 0 ? len : 0);
1317 if (len < 0)
1318 Error("JsonStartElement", "Missing array when reading TArray class for element %s", elem->GetName());
1319 }
1320 if ((gDebug > 1) && base_class)
1321 Info("JsonStartElement", "Reading baseclass %s from element %s", base_class->GetName(), elem_name);
1322 }
1323
1324 } else {
1325 AppendOutput(Stack()->NextMemberSeparator(), "\"");
1327 AppendOutput("\"");
1329 }
1330}
1331
1332////////////////////////////////////////////////////////////////////////////////
1333/// disable post-processing of the code
1338
1339////////////////////////////////////////////////////////////////////////////////
1340/// return non-zero value when class has special handling in JSON
1341/// it is TCollection (-130), TArray (100), TString (110), std::string (120) and STL containers (1..6)
1342
1344{
1345 if (!cl)
1346 return 0;
1347
1348 Bool_t isarray = strncmp("TArray", cl->GetName(), 6) == 0;
1349 if (isarray)
1350 isarray = (const_cast<TClass *>(cl))->GetBaseClassOffset(TArray::Class()) == 0;
1351 if (isarray)
1352 return json_TArray;
1353
1354 // negative value used to indicate that collection stored as object
1355 if ((const_cast<TClass *>(cl))->GetBaseClassOffset(TCollection::Class()) == 0)
1356 return json_TCollection;
1357
1358 // special case for TString - it is saved as string in JSON
1359 if (cl == TString::Class())
1360 return json_TString;
1361
1362 bool isstd = TClassEdit::IsStdClass(cl->GetName());
1364 if (isstd)
1366 if (isstlcont > 0)
1367 return isstlcont;
1368
1369 // also special handling for STL string, which handled similar to TString
1370 if (isstd && !strcmp(cl->GetName(), "string"))
1371 return json_stdstring;
1372
1373 return 0;
1374}
1375
1376////////////////////////////////////////////////////////////////////////////////
1377/// Write object to buffer
1378/// If object was written before, only pointer will be stored
1379/// If check_map==kFALSE, object will be stored in any case and pointer will not be registered in the map
1380
1381void TBufferJSON::JsonWriteObject(const void *obj, const TClass *cl, Bool_t check_map)
1382{
1383 if (!cl)
1384 obj = nullptr;
1385
1386 if (gDebug > 0)
1387 Info("JsonWriteObject", "Object %p class %s check_map %s", obj, cl ? cl->GetName() : "null",
1388 check_map ? "true" : "false");
1389
1391
1393
1394 TJSONStackObj *stack = Stack();
1395
1396 if (stack && stack->fAccObjects && ((fValue.Length() > 0) || (stack->fValues.size() > 0))) {
1397 // accumulate data of super-object in stack
1398
1399 if (fValue.Length() > 0)
1400 stack->PushValue(fValue);
1401
1402 // redirect output to local buffer, use it later as value
1405 } else if ((special_kind <= 0) || (special_kind > json_TArray)) {
1406 // FIXME: later post processing should be active for all special classes, while they all keep output in the value
1411
1412 if ((fMapAsObject && (fStack.size()==1)) || (stack && stack->fElem && strstr(stack->fElem->GetTitle(), "JSON_object")))
1413 map_convert = 2; // mapped into normal object
1414 else
1415 map_convert = 1;
1416
1417 if (!cl->HasDictionary()) {
1418 Error("JsonWriteObject", "Cannot stream class %s without dictionary", cl->GetName());
1419 AppendOutput(map_convert == 1 ? "[]" : "null");
1420 goto post_process;
1421 }
1422 }
1423
1424 if (!obj) {
1425 AppendOutput("null");
1426 goto post_process;
1427 }
1428
1429 if (special_kind <= 0) {
1430 // add element name which should correspond to the object
1431 if (check_map) {
1433 if (refid > 0) {
1434 // old-style refs, coded into string like "$ref12"
1435 // AppendOutput(TString::Format("\"$ref:%u\"", iter->second));
1436 // new-style refs, coded into extra object {"$ref":12}, auto-detected by JSROOT 4.8 and higher
1437 AppendOutput(TString::Format("{\"$ref\":%u}", (unsigned)(refid - 1)));
1438 goto post_process;
1439 }
1440 MapObject(obj, cl, fJsonrCnt + 1); // +1 used
1441 }
1442
1443 fJsonrCnt++; // object counts required in dereferencing part
1444
1445 stack = JsonStartObjectWrite(cl);
1446
1447 } else if (map_convert == 2) {
1448 // special handling of map - it is object, but stored in the fValue
1449
1450 if (check_map) {
1452 if (refid > 0) {
1453 fValue.Form("{\"$ref\":%u}", (unsigned)(refid - 1));
1454 goto post_process;
1455 }
1456 MapObject(obj, cl, fJsonrCnt + 1); // +1 used
1457 }
1458
1459 fJsonrCnt++; // object counts required in dereferencing part
1460 stack = PushStack(0);
1461
1462 } else {
1463
1464 bool base64 = ((special_kind == ROOT::ESTLType::kSTLvector) && stack && stack->fElem &&
1465 strstr(stack->fElem->GetTitle(), "JSON_base64"));
1466
1467 // for array, string and STL collections different handling -
1468 // they not recognized at the end as objects in JSON
1469 stack = PushStack(0);
1470
1471 stack->fBase64 = base64;
1472 }
1473
1474 if (gDebug > 3)
1475 Info("JsonWriteObject", "Starting object %p write for class: %s", obj, cl->GetName());
1476
1478
1480 JsonWriteCollection((TCollection *)obj, cl);
1481 else
1482 (const_cast<TClass *>(cl))->Streamer((void *)obj, *this);
1483
1484 if (gDebug > 3)
1485 Info("JsonWriteObject", "Done object %p write for class: %s", obj, cl->GetName());
1486
1487 if (special_kind == json_TArray) {
1488 if (stack->fValues.size() != 1)
1489 Error("JsonWriteObject", "Problem when writing array");
1490 stack->fValues.clear();
1491 } else if ((special_kind == json_TString) || (special_kind == json_stdstring)) {
1492 if (stack->fValues.size() > 2)
1493 Error("JsonWriteObject", "Problem when writing TString or std::string");
1494 stack->fValues.clear();
1496 fValue.Clear();
1497 } else if ((special_kind > 0) && (special_kind < ROOT::kSTLend)) {
1498 // here make STL container processing
1499
1500 if (map_convert == 2) {
1501 // converting map into object
1502
1503 if (!stack->fValues.empty() && (fValue.Length() > 0))
1504 stack->PushValue(fValue);
1505
1506 const char *separ = (fCompact < 2) ? ", " : ",";
1507 const char *semi = (fCompact < 2) ? ": " : ":";
1508 bool first = true;
1509
1510 fValue = "{";
1511 if ((fTypeNameTag.Length() > 0) && !IsSkipClassInfo(cl)) {
1512 fValue.Append("\"");
1514 fValue.Append("\"");
1516 fValue.Append("\"");
1517 fValue.Append(cl->GetName());
1518 fValue.Append("\"");
1519 first = false;
1520 }
1521 for (Int_t k = 1; k < (int)stack->fValues.size() - 1; k += 2) {
1522 if (!first)
1524 first = false;
1525 fValue.Append(stack->fValues[k].c_str());
1527 fValue.Append(stack->fValues[k + 1].c_str());
1528 }
1529 fValue.Append("}");
1530 stack->fValues.clear();
1531 } else if (stack->fValues.empty()) {
1532 // empty container
1533 if (fValue != "0")
1534 Error("JsonWriteObject", "With empty stack fValue!=0");
1535 fValue = "[]";
1536 } else {
1537
1538 auto size = std::stoi(stack->fValues[0]);
1539
1540 bool trivial_format = false;
1541
1542 if ((stack->fValues.size() == 1) && ((size > 1) || ((fValue.Length() > 1) && (fValue[0]=='[')))) {
1543 // prevent case of vector<vector<value_class>>
1544 const auto proxy = cl->GetCollectionProxy();
1545 TClass *value_class = proxy ? proxy->GetValueClass() : nullptr;
1546 if (value_class && TClassEdit::IsStdClass(value_class->GetName()) && (value_class->GetCollectionType() != ROOT::kNotSTL))
1547 trivial_format = false;
1548 else
1549 trivial_format = true;
1550 }
1551
1552 if (trivial_format) {
1553 // case of simple vector, array already in the value
1554 stack->fValues.clear();
1555 if (fValue.Length() == 0) {
1556 Error("JsonWriteObject", "Empty value when it should contain something");
1557 fValue = "[]";
1558 }
1559
1560 } else {
1561 const char *separ = "[";
1562
1563 if (fValue.Length() > 0)
1564 stack->PushValue(fValue);
1565
1566 if ((size * 2 == (int) stack->fValues.size() - 1) && (map_convert > 0)) {
1567 // special handling for std::map.
1568 // Create entries like { '$pair': 'typename' , 'first' : key, 'second' : value }
1569 TString pairtype = cl->GetName();
1570 if (pairtype.Index("unordered_map<") == 0)
1571 pairtype.Replace(0, 14, "pair<");
1572 else if (pairtype.Index("unordered_multimap<") == 0)
1573 pairtype.Replace(0, 19, "pair<");
1574 else if (pairtype.Index("multimap<") == 0)
1575 pairtype.Replace(0, 9, "pair<");
1576 else if (pairtype.Index("map<") == 0)
1577 pairtype.Replace(0, 4, "pair<");
1578 else
1579 pairtype = "TPair";
1580 if (fTypeNameTag.Length() == 0)
1581 pairtype = "1";
1582 else
1583 pairtype = TString("\"") + pairtype + TString("\"");
1584 for (Int_t k = 1; k < (int) stack->fValues.size() - 1; k += 2) {
1587 // fJsonrCnt++; // do not add entry in the map, can conflict with objects inside values
1588 fValue.Append("{");
1589 fValue.Append("\"$pair\"");
1591 fValue.Append(pairtype.Data());
1593 fValue.Append("\"first\"");
1595 fValue.Append(stack->fValues[k].c_str());
1597 fValue.Append("\"second\"");
1599 fValue.Append(stack->fValues[k + 1].c_str());
1600 fValue.Append("}");
1601 }
1602 } else {
1603 // for most stl containers write just like blob, but skipping first element with size
1604 for (Int_t k = 1; k < (int) stack->fValues.size(); k++) {
1607 fValue.Append(stack->fValues[k].c_str());
1608 }
1609 }
1610
1611 fValue.Append("]");
1612 stack->fValues.clear();
1613 }
1614 }
1615 }
1616
1617 // reuse post-processing code for TObject or TRef
1618 PerformPostProcessing(stack, cl);
1619
1620 if ((special_kind == 0) && (!stack->fValues.empty() || (fValue.Length() > 0))) {
1621 if (gDebug > 0)
1622 Info("JsonWriteObject", "Create blob value for class %s", cl->GetName());
1623
1624 AppendOutput(fArraySepar.Data(), "\"_blob\"");
1626
1627 const char *separ = "[";
1628
1629 for (auto &elem: stack->fValues) {
1632 AppendOutput(elem.c_str());
1633 }
1634
1635 if (fValue.Length() > 0) {
1638 }
1639
1640 AppendOutput("]");
1641
1642 fValue.Clear();
1643 stack->fValues.clear();
1644 }
1645
1646 PopStack();
1647
1648 if ((special_kind <= 0))
1649 AppendOutput(nullptr, "}");
1650
1652
1653 if (fPrevOutput) {
1655 // for STL containers and TArray object in fValue itself
1656 if ((special_kind <= 0) || (special_kind > json_TArray))
1658 else if (fObjectOutput.Length() != 0)
1659 Error("JsonWriteObject", "Non-empty object output for special class %s", cl->GetName());
1660 }
1661}
1662
1663////////////////////////////////////////////////////////////////////////////////
1664/// store content of ROOT collection
1665
1667{
1668 AppendOutput(Stack()->NextMemberSeparator(), "\"name\"");
1670 AppendOutput("\"");
1671 AppendOutput(col->GetName());
1672 AppendOutput("\"");
1673 AppendOutput(Stack()->NextMemberSeparator(), "\"arr\"");
1675
1676 // collection treated as JS Array
1677 AppendOutput("[");
1678
1679 auto map = dynamic_cast<TMap *>(col);
1680 auto lst = dynamic_cast<TList *>(col);
1681
1682 TString sopt;
1683 Bool_t first = kTRUE;
1684
1685 if (lst) {
1686 // handle TList with extra options
1687 sopt.Capacity(500);
1688 sopt = "[";
1689
1690 auto lnk = lst->FirstLink();
1691 while (lnk) {
1692 if (!first) {
1694 sopt.Append(fArraySepar.Data());
1695 }
1696
1697 WriteObjectAny(lnk->GetObject(), TObject::Class());
1698
1699 if (dynamic_cast<TObjOptLink *>(lnk)) {
1700 sopt.Append("\"");
1701 sopt.Append(lnk->GetAddOption());
1702 sopt.Append("\"");
1703 } else
1704 sopt.Append("null");
1705
1706 lnk = lnk->Next();
1707 first = kFALSE;
1708 }
1709 } else if (map) {
1710 // handle TMap with artificial TPair object
1711 TIter iter(col);
1712 while (auto obj = iter()) {
1713 if (!first)
1715
1716 // fJsonrCnt++; // do not account map pair as JSON object
1717 AppendOutput("{", "\"$pair\"");
1719 AppendOutput("\"TPair\"");
1720 AppendOutput(fArraySepar.Data(), "\"first\"");
1722
1724
1725 AppendOutput(fArraySepar.Data(), "\"second\"");
1727 WriteObjectAny(map->GetValue(obj), TObject::Class());
1728 AppendOutput("", "}");
1729 first = kFALSE;
1730 }
1731 } else {
1732 TIter iter(col);
1733 while (auto obj = iter()) {
1734 if (!first)
1736
1738 first = kFALSE;
1739 }
1740 }
1741
1742 AppendOutput("]");
1743
1744 if (lst) {
1745 sopt.Append("]");
1746 AppendOutput(Stack()->NextMemberSeparator(), "\"opt\"");
1748 AppendOutput(sopt.Data());
1749 }
1750
1751 fValue.Clear();
1752}
1753
1754////////////////////////////////////////////////////////////////////////////////
1755/// read content of ROOT collection
1756
1758{
1759 if (!col)
1760 return;
1761
1762 TList *lst = nullptr;
1763 TMap *map = nullptr;
1764 TClonesArray *clones = nullptr;
1765 if (col->InheritsFrom(TList::Class()))
1766 lst = dynamic_cast<TList *>(col);
1767 else if (col->InheritsFrom(TMap::Class()))
1768 map = dynamic_cast<TMap *>(col);
1769 else if (col->InheritsFrom(TClonesArray::Class()))
1770 clones = dynamic_cast<TClonesArray *>(col);
1771
1772 nlohmann::json *json = Stack()->fNode;
1773
1774 std::string name = json->at("name");
1775 col->SetName(name.c_str());
1776
1777 nlohmann::json &arr = json->at("arr");
1778 int size = arr.size();
1779
1780 for (int n = 0; n < size; ++n) {
1781 nlohmann::json *subelem = &arr.at(n);
1782
1783 if (map)
1784 subelem = &subelem->at("first");
1785
1786 PushStack(0, subelem);
1787
1788 TClass *readClass = nullptr, *objClass = nullptr;
1789 void *subobj = nullptr;
1790
1791 if (clones) {
1792 if (n == 0) {
1793 if (!clones->GetClass() || (clones->GetSize() == 0)) {
1794 if (fTypeNameTag.Length() > 0) {
1795 clones->SetClass(subelem->at(fTypeNameTag.Data()).get<std::string>().c_str(), size);
1796 } else {
1797 Error("JsonReadCollection",
1798 "Cannot detect class name for TClonesArray - typename tag not configured");
1799 return;
1800 }
1801 } else if (size > clones->GetSize()) {
1802 Error("JsonReadCollection", "TClonesArray size %d smaller than required %d", clones->GetSize(), size);
1803 return;
1804 }
1805 }
1806 objClass = clones->GetClass();
1807 subobj = clones->ConstructedAt(n);
1808 }
1809
1811
1812 PopStack();
1813
1814 if (clones)
1815 continue;
1816
1817 if (!subobj || !readClass) {
1818 subobj = nullptr;
1819 } else if (readClass->GetBaseClassOffset(TObject::Class()) != 0) {
1820 Error("JsonReadCollection", "Try to add object %s not derived from TObject", readClass->GetName());
1821 subobj = nullptr;
1822 }
1823
1824 TObject *tobj = static_cast<TObject *>(subobj);
1825
1826 if (map) {
1827 PushStack(0, &arr.at(n).at("second"));
1828
1829 readClass = nullptr;
1830 void *subobj2 = JsonReadObject(nullptr, nullptr, &readClass);
1831
1832 PopStack();
1833
1834 if (!subobj2 || !readClass) {
1835 subobj2 = nullptr;
1836 } else if (readClass->GetBaseClassOffset(TObject::Class()) != 0) {
1837 Error("JsonReadCollection", "Try to add object %s not derived from TObject", readClass->GetName());
1838 subobj2 = nullptr;
1839 }
1840
1841 map->Add(tobj, static_cast<TObject *>(subobj2));
1842 } else if (lst) {
1843 auto &elem = json->at("opt").at(n);
1844 if (elem.is_null())
1845 lst->Add(tobj);
1846 else
1847 lst->Add(tobj, elem.get<std::string>().c_str());
1848 } else {
1849 // generic method, all kinds of TCollection should work
1850 col->Add(tobj);
1851 }
1852 }
1853}
1854
1855////////////////////////////////////////////////////////////////////////////////
1856/// Read object from current JSON node
1857
1859{
1860 if (readClass)
1861 *readClass = nullptr;
1862
1863 TJSONStackObj *stack = Stack();
1864
1865 Bool_t process_stl = stack->IsStl();
1866 nlohmann::json *json = stack->GetStlNode();
1867
1868 // check if null pointer
1869 if (json->is_null())
1870 return nullptr;
1871
1873
1874 // Extract pointer
1875 if (json->is_object() && (json->size() == 1) && (json->find("$ref") != json->end())) {
1876 unsigned refid = json->at("$ref").get<unsigned>();
1877
1878 void *ref_obj = nullptr;
1879 TClass *ref_cl = nullptr;
1880
1882
1883 if (!ref_obj || !ref_cl) {
1884 Error("JsonReadObject", "Fail to find object for reference %u", refid);
1885 return nullptr;
1886 }
1887
1888 if (readClass)
1889 *readClass = ref_cl;
1890
1891 if (gDebug > 2)
1892 Info("JsonReadObject", "Extract object reference %u %p cl:%s expects:%s", refid, ref_obj, ref_cl->GetName(),
1893 (objClass ? objClass->GetName() : "---"));
1894
1895 return ref_obj;
1896 }
1897
1898 // special case of strings - they do not create JSON object, but just string
1900 if (!obj)
1901 obj = objClass->New();
1902
1903 if (gDebug > 2)
1904 Info("JsonReadObject", "Read string from %s", json->dump().c_str());
1905
1907 *((std::string *)obj) = json->get<std::string>();
1908 else
1909 *((TString *)obj) = json->get<std::string>().c_str();
1910
1911 if (readClass)
1912 *readClass = const_cast<TClass *>(objClass);
1913
1914 return obj;
1915 }
1916
1917 Bool_t isBase = (stack->fElem && objClass) ? stack->fElem->IsBase() : kFALSE; // base class
1918
1919 if (isBase && (!obj || !objClass)) {
1920 Error("JsonReadObject", "No object when reading base class");
1921 return obj;
1922 }
1923
1924 Int_t map_convert = 0;
1927 map_convert = json->is_object() ? 2 : 1; // check if map was written as array or as object
1928
1929 if (objClass && !objClass->HasDictionary()) {
1930 Error("JsonReadObject", "Cannot stream class %s without dictionary", objClass->GetName());
1931 return obj;
1932 }
1933 }
1934
1935 // from now all operations performed with sub-element,
1936 // stack should be repaired at the end
1937 if (process_stl)
1938 stack = PushStack(0, json);
1939
1940 TClass *jsonClass = nullptr;
1942
1943 if ((special_kind == json_TArray) || ((special_kind > 0) && (special_kind < ROOT::kSTLend))) {
1944
1945 jsonClass = const_cast<TClass *>(objClass);
1946
1947 if (!obj)
1948 obj = jsonClass->New();
1949
1950 Int_t len = stack->IsJsonArray(json, map_convert == 2 ? fTypeNameTag.Data() : nullptr);
1951
1952 stack->PushIntValue(len > 0 ? len : 0);
1953
1954 if (len < 0) // should never happens
1955 Error("JsonReadObject", "Not array when expecting such %s", json->dump().c_str());
1956
1957 if (gDebug > 1)
1958 Info("JsonReadObject", "Reading special kind %d %s ptr %p", special_kind, objClass->GetName(), obj);
1959
1960 } else if (isBase) {
1961 // base class has special handling - no additional level and no extra refid
1962
1963 jsonClass = const_cast<TClass *>(objClass);
1964
1965 if (gDebug > 1)
1966 Info("JsonReadObject", "Reading baseclass %s ptr %p", objClass->GetName(), obj);
1967 } else {
1968
1969 if ((fTypeNameTag.Length() > 0) && (json->count(fTypeNameTag.Data()) > 0)) {
1970 std::string clname = json->at(fTypeNameTag.Data()).get<std::string>();
1972 if (!jsonClass)
1973 Error("JsonReadObject", "Cannot find class %s", clname.c_str());
1974 } else {
1975 // try to use class which is assigned by streamers - better than nothing
1976 jsonClass = const_cast<TClass *>(objClass);
1977 }
1978
1979 if (!jsonClass) {
1980 if (process_stl)
1981 PopStack();
1982 return obj;
1983 }
1984
1985 if ((fTypeVersionTag.Length() > 0) && (json->count(fTypeVersionTag.Data()) > 0))
1986 jsonClassVersion = json->at(fTypeVersionTag.Data()).get<int>();
1987
1988 if (objClass && (jsonClass != objClass)) {
1989 if (obj || (jsonClass->GetBaseClassOffset(objClass) != 0)) {
1990 if (jsonClass->GetBaseClassOffset(objClass) < 0)
1991 Error("JsonReadObject", "Not possible to read %s and casting to %s pointer as the two classes are unrelated",
1992 jsonClass->GetName(), objClass->GetName());
1993 else
1994 Error("JsonReadObject", "Reading %s and casting to %s pointer is currently not supported",
1995 jsonClass->GetName(), objClass->GetName());
1996 if (process_stl)
1997 PopStack();
1998 return obj;
1999 }
2000 }
2001
2002 if (!obj)
2003 obj = jsonClass->New();
2004
2005 if (gDebug > 1)
2006 Info("JsonReadObject", "Reading object of class %s refid %u ptr %p", jsonClass->GetName(), fJsonrCnt, obj);
2007
2008 if (!special_kind)
2010
2011 // add new element to the reading map
2012 MapObject(obj, jsonClass, ++fJsonrCnt);
2013 }
2014
2015 // there are two ways to handle custom streamers
2016 // either prepare data before streamer and tweak basic function which are reading values like UInt32_t
2017 // or try re-implement custom streamer here
2018
2019 if ((jsonClass == TObject::Class()) || (jsonClass == TRef::Class())) {
2020 // for TObject we re-implement custom streamer - it is much easier
2021
2023
2024 } else if (special_kind == json_TCollection) {
2025
2027
2028 } else {
2029
2031
2032 // special handling of STL which coded into arrays
2033 if ((special_kind > 0) && (special_kind < ROOT::kSTLend))
2035
2036 // if provided - use class version from JSON
2037 stack->fClVersion = jsonClassVersion ? jsonClassVersion : jsonClass->GetClassVersion();
2038
2039 if (gDebug > 3)
2040 Info("JsonReadObject", "Calling streamer of class %s", jsonClass->GetName());
2041
2042 if (isBase && (special_kind == 0))
2043 Error("JsonReadObject", "Should not be used for reading of base class %s", jsonClass->GetName());
2044
2045 if (do_read)
2046 jsonClass->Streamer((void *)obj, *this);
2047
2048 stack->fClVersion = 0;
2049
2050 stack->ClearStl(); // reset STL index for itself to prevent looping
2051 }
2052
2053 // return back stack position
2054 if (process_stl)
2055 PopStack();
2056
2057 if (gDebug > 1)
2058 Info("JsonReadObject", "Reading object of class %s done", jsonClass->GetName());
2059
2060 if (readClass)
2062
2063 return obj;
2064}
2065
2066////////////////////////////////////////////////////////////////////////////////
2067/// Read TObject data members from JSON.
2068/// Do not call TObject::Streamer() to avoid special tweaking of TBufferJSON interface
2069
2071{
2072 nlohmann::json *json = node ? (nlohmann::json *)node : Stack()->fNode;
2073
2074 UInt_t uid = json->at("fUniqueID").get<unsigned>();
2075 UInt_t bits = json->at("fBits").get<unsigned>();
2076 // UInt32_t pid = json->at("fPID").get<unsigned>(); // ignore PID for the moment
2077
2078 tobj->SetUniqueID(uid);
2079
2080 static auto tobj_fbits_offset = TObject::Class()->GetDataMemberOffset("fBits");
2081
2082 // there is no method to set all bits directly - do it differently
2083 if (tobj_fbits_offset > 0) {
2084 UInt_t *fbits = (UInt_t *) ((char* ) tobj + tobj_fbits_offset);
2086 }
2087}
2088
2089////////////////////////////////////////////////////////////////////////////////
2090/// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2091/// and indent new level in json structure.
2092/// This call indicates, that TStreamerInfo functions starts streaming
2093/// object data of correspondent class
2094
2096{
2097 if (gDebug > 2)
2098 Info("IncrementLevel", "Class: %s", (info ? info->GetClass()->GetName() : "custom"));
2099
2101}
2102
2103////////////////////////////////////////////////////////////////////////////////
2104/// Prepares buffer to stream data of specified class
2105
2107{
2108 if (sinfo)
2109 cl = sinfo->GetClass();
2110
2111 if (!cl)
2112 return;
2113
2114 if (gDebug > 3)
2115 Info("WorkWithClass", "Class: %s", cl->GetName());
2116
2117 TJSONStackObj *stack = Stack();
2118
2119 if (IsReading()) {
2120 stack = PushStack(0, stack->fNode);
2121 } else if (stack && stack->IsStreamerElement() && !stack->fIsObjStarted &&
2122 ((stack->fElem->GetType() == TStreamerInfo::kObject) ||
2123 (stack->fElem->GetType() == TStreamerInfo::kAny))) {
2124
2125 stack->fIsObjStarted = kTRUE;
2126
2127 fJsonrCnt++; // count object, but do not keep reference
2128
2129 stack = JsonStartObjectWrite(cl, sinfo);
2130 } else {
2131 stack = PushStack(0);
2132 }
2133
2134 stack->fInfo = sinfo;
2135 stack->fIsStreamerInfo = kTRUE;
2136}
2137
2138////////////////////////////////////////////////////////////////////////////////
2139/// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2140/// and decrease level in json structure.
2141
2143{
2144 if (gDebug > 2)
2145 Info("DecrementLevel", "Class: %s", (info ? info->GetClass()->GetName() : "custom"));
2146
2147 TJSONStackObj *stack = Stack();
2148
2149 if (stack->IsStreamerElement()) {
2150
2151 if (IsWriting()) {
2152 if (gDebug > 3)
2153 Info("DecrementLevel", " Perform post-processing elem: %s", stack->fElem->GetName());
2154
2155 PerformPostProcessing(stack);
2156 }
2157
2158 stack = PopStack(); // remove stack of last element
2159 }
2160
2161 if (stack->fInfo != (TStreamerInfo *)info)
2162 Error("DecrementLevel", " Mismatch of streamer info");
2163
2164 PopStack(); // back from data of stack info
2165
2166 if (gDebug > 3)
2167 Info("DecrementLevel", "Class: %s done", (info ? info->GetClass()->GetName() : "custom"));
2168}
2169
2170////////////////////////////////////////////////////////////////////////////////
2171/// Return current streamer info element
2172
2177
2178////////////////////////////////////////////////////////////////////////////////
2179/// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2180/// and add/verify next element of json structure
2181/// This calls allows separate data, correspondent to one class member, from another
2182
2184{
2185 if (gDebug > 3)
2186 Info("SetStreamerElementNumber", "Element name %s", elem->GetName());
2187
2189}
2190
2191////////////////////////////////////////////////////////////////////////////////
2192/// This is call-back from streamer which indicates
2193/// that class member will be streamed
2194/// Name of element used in JSON
2195
2197{
2198 TJSONStackObj *stack = Stack();
2199 if (!stack) {
2200 Error("WorkWithElement", "stack is empty");
2201 return;
2202 }
2203
2204 if (gDebug > 0)
2205 Info("WorkWithElement", " Start element %s type %d typename %s", elem ? elem->GetName() : "---",
2206 elem ? elem->GetType() : -1, elem ? elem->GetTypeName() : "---");
2207
2208 if (stack->IsStreamerElement()) {
2209 // this is post processing
2210
2211 if (IsWriting()) {
2212 if (gDebug > 3)
2213 Info("WorkWithElement", " Perform post-processing elem: %s", stack->fElem->GetName());
2214 PerformPostProcessing(stack);
2215 }
2216
2217 stack = PopStack(); // go level back
2218 }
2219
2220 fValue.Clear();
2221
2222 if (!stack) {
2223 Error("WorkWithElement", "Lost of stack");
2224 return;
2225 }
2226
2227 TStreamerInfo *info = stack->fInfo;
2228 if (!stack->IsStreamerInfo()) {
2229 Error("WorkWithElement", "Problem in Inc/Dec level");
2230 return;
2231 }
2232
2233 Int_t number = info ? info->GetElements()->IndexOf(elem) : -1;
2234
2235 if (!elem) {
2236 Error("WorkWithElement", "streamer info returns elem = nullptr");
2237 return;
2238 }
2239
2240 TClass *base_class = elem->IsBase() ? elem->GetClassPointer() : nullptr;
2241
2242 stack = PushStack(0, stack->fNode);
2243 stack->fElem = elem;
2244 stack->fIsElemOwner = (number < 0);
2245
2247
2248 if (base_class && IsReading())
2249 stack->fClVersion = base_class->GetClassVersion();
2250
2251 if ((elem->GetType() == TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop) && (elem->GetArrayDim() > 0)) {
2252 // array of array, start handling here
2253 stack->fIndx = std::make_unique<TArrayIndexProducer>(elem, -1, fArraySepar.Data());
2254 if (IsWriting())
2255 AppendOutput(stack->fIndx->GetBegin());
2256 }
2257
2258 if (IsReading() && (elem->GetType() > TStreamerInfo::kOffsetP) && (elem->GetType() < TStreamerInfo::kOffsetP + 20)) {
2259 // reading of such array begins with reading of single Char_t value
2260 // it indicates if array should be read or not
2261 stack->PushIntValue(stack->IsJsonString() || (stack->IsJsonArray() > 0) ? 1 : 0);
2262 }
2263}
2264
2265////////////////////////////////////////////////////////////////////////////////
2266/// Should be called in the beginning of custom class streamer.
2267/// Informs buffer data about class which will be streamed now.
2268///
2269/// ClassBegin(), ClassEnd() and ClassMember() should be used in
2270/// custom class streamers to specify which kind of data are
2271/// now streamed. Such information is used to correctly
2272/// convert class data to JSON. Without that functions calls
2273/// classes with custom streamers cannot be used with TBufferJSON
2274
2276{
2277 WorkWithClass(nullptr, cl);
2278}
2279
2280////////////////////////////////////////////////////////////////////////////////
2281/// Should be called at the end of custom streamer
2282/// See TBufferJSON::ClassBegin for more details
2283
2285{
2286 DecrementLevel(0);
2287}
2288
2289////////////////////////////////////////////////////////////////////////////////
2290/// Method indicates name and typename of class member,
2291/// which should be now streamed in custom streamer
2292/// Following combinations are supported:
2293/// 1. name = "ClassName", typeName = 0 or typename==ClassName
2294/// This is a case, when data of parent class "ClassName" should be streamed.
2295/// For instance, if class directly inherited from TObject, custom
2296/// streamer should include following code:
2297/// ~~~{.cpp}
2298/// b.ClassMember("TObject");
2299/// TObject::Streamer(b);
2300/// ~~~
2301/// 2. Basic data type
2302/// ~~~{.cpp}
2303/// b.ClassMember("fInt","Int_t");
2304/// b >> fInt;
2305/// ~~~
2306/// 3. Array of basic data types
2307/// ~~~{.cpp}
2308/// b.ClassMember("fArr","Int_t", 5);
2309/// b.ReadFastArray(fArr, 5);
2310/// ~~~
2311/// 4. Object as data member
2312/// ~~~{.cpp}
2313/// b.ClassMember("fName","TString");
2314/// fName.Streamer(b);
2315/// ~~~
2316/// 5. Pointer on object as data member
2317/// ~~~{.cpp}
2318/// b.ClassMember("fObj","TObject*");
2319/// b.StreamObject(fObj);
2320/// ~~~
2321///
2322/// arrsize1 and arrsize2 arguments (when specified) indicate first and
2323/// second dimension of array. Can be used for array of basic types.
2324/// See ClassBegin() method for more details.
2325
2326void TBufferJSON::ClassMember(const char *name, const char *typeName, Int_t arrsize1, Int_t arrsize2)
2327{
2328 if (!typeName)
2329 typeName = name;
2330
2331 if (!name || (strlen(name) == 0)) {
2332 Error("ClassMember", "Invalid member name");
2333 return;
2334 }
2335
2336 TString tname = typeName;
2337
2338 Int_t typ_id = -1;
2339
2340 if (strcmp(typeName, "raw:data") == 0)
2342
2343 if (typ_id < 0) {
2344 TDataType *dt = gROOT->GetType(typeName);
2345 if (dt && (dt->GetType() > 0) && (dt->GetType() < 20))
2346 typ_id = dt->GetType();
2347 }
2348
2349 if (typ_id < 0)
2350 if (strcmp(name, typeName) == 0) {
2351 TClass *cl = TClass::GetClass(tname.Data());
2352 if (cl)
2354 }
2355
2356 if (typ_id < 0) {
2358 if (tname[tname.Length() - 1] == '*') {
2359 tname.Resize(tname.Length() - 1);
2360 isptr = kTRUE;
2361 }
2362 TClass *cl = TClass::GetClass(tname.Data());
2363 if (!cl) {
2364 Error("ClassMember", "Invalid class specifier %s", typeName);
2365 return;
2366 }
2367
2368 if (cl->IsTObject())
2370 else
2372
2373 if ((cl == TString::Class()) && !isptr)
2375 }
2376
2377 TStreamerElement *elem = nullptr;
2378
2380 elem = new TStreamerElement(name, "title", 0, typ_id, "raw:data");
2381 } else if (typ_id == TStreamerInfo::kBase) {
2382 TClass *cl = TClass::GetClass(tname.Data());
2383 if (cl) {
2384 TStreamerBase *b = new TStreamerBase(tname.Data(), "title", 0);
2385 b->SetBaseVersion(cl->GetClassVersion());
2386 elem = b;
2387 }
2388 } else if ((typ_id > 0) && (typ_id < 20)) {
2389 elem = new TStreamerBasicType(name, "title", 0, typ_id, typeName);
2392 elem = new TStreamerObject(name, "title", 0, tname.Data());
2393 } else if (typ_id == TStreamerInfo::kObjectp) {
2394 elem = new TStreamerObjectPointer(name, "title", 0, tname.Data());
2395 } else if (typ_id == TStreamerInfo::kAny) {
2396 elem = new TStreamerObjectAny(name, "title", 0, tname.Data());
2397 } else if (typ_id == TStreamerInfo::kAnyp) {
2398 elem = new TStreamerObjectAnyPointer(name, "title", 0, tname.Data());
2399 } else if (typ_id == TStreamerInfo::kTString) {
2400 elem = new TStreamerString(name, "title", 0);
2401 }
2402
2403 if (!elem) {
2404 Error("ClassMember", "Invalid combination name = %s type = %s", name, typeName);
2405 return;
2406 }
2407
2408 if (arrsize1 > 0) {
2409 elem->SetArrayDim(arrsize2 > 0 ? 2 : 1);
2410 elem->SetMaxIndex(0, arrsize1);
2411 if (arrsize2 > 0)
2412 elem->SetMaxIndex(1, arrsize2);
2413 }
2414
2415 // we indicate that there is no streamerinfo
2416 WorkWithElement(elem, -1);
2417}
2418
2419////////////////////////////////////////////////////////////////////////////////
2420/// Function is converts TObject and TString structures to more compact representation
2421
2423{
2424 if (stack->fIsPostProcessed)
2425 return;
2426
2427 const TStreamerElement *elem = stack->fElem;
2428
2429 if (!elem && !obj_cl)
2430 return;
2431
2432 stack->fIsPostProcessed = kTRUE;
2433
2434 // when element was written as separate object, close only braces and exit
2435 if (stack->fIsObjStarted) {
2436 AppendOutput("", "}");
2437 return;
2438 }
2439
2442
2443 if (obj_cl) {
2444 if (obj_cl == TObject::Class())
2445 isTObject = kTRUE;
2446 else if (obj_cl == TRef::Class())
2447 isTRef = kTRUE;
2448 else
2449 return;
2450 } else {
2451 const char *typname = elem->IsBase() ? elem->GetName() : elem->GetTypeName();
2452 isTObject = (elem->GetType() == TStreamerInfo::kTObject) || (strcmp("TObject", typname) == 0);
2453 isTString = elem->GetType() == TStreamerInfo::kTString;
2455 isOffsetPArray = (elem->GetType() > TStreamerInfo::kOffsetP) && (elem->GetType() < TStreamerInfo::kOffsetP + 20);
2456 isTArray = (strncmp("TArray", typname, 6) == 0);
2457 }
2458
2459 if (isTString || isSTLstring) {
2460 // just remove all kind of string length information
2461
2462 if (gDebug > 3)
2463 Info("PerformPostProcessing", "reformat string value = '%s'", fValue.Data());
2464
2465 stack->fValues.clear();
2466 } else if (isOffsetPArray) {
2467 // basic array with [fN] comment
2468
2469 if (stack->fValues.empty() && (fValue == "0")) {
2470 fValue = "[]";
2471 } else if ((stack->fValues.size() == 1) && (stack->fValues[0] == "1")) {
2472 stack->fValues.clear();
2473 } else {
2474 Error("PerformPostProcessing", "Wrong values for kOffsetP element %s", (elem ? elem->GetName() : "---"));
2475 stack->fValues.clear();
2476 fValue = "[]";
2477 }
2478 } else if (isTObject || isTRef) {
2479 // complex workaround for TObject/TRef streamer
2480 // would be nice if other solution can be found
2481 // Here is not supported TRef on TRef (double reference)
2482
2483 Int_t cnt = stack->fValues.size();
2484 if (fValue.Length() > 0)
2485 cnt++;
2486
2487 if (cnt < 2 || cnt > 3) {
2488 if (gDebug > 0)
2489 Error("PerformPostProcessing", "When storing TObject/TRef, strange number of items %d", cnt);
2490 AppendOutput(stack->NextMemberSeparator(), "\"dummy\"");
2492 } else {
2493 AppendOutput(stack->NextMemberSeparator(), "\"fUniqueID\"");
2495 AppendOutput(stack->fValues[0].c_str());
2496 AppendOutput(stack->NextMemberSeparator(), "\"fBits\"");
2498 auto tbits = std::atol((stack->fValues.size() > 1) ? stack->fValues[1].c_str() : fValue.Data());
2499 AppendOutput(std::to_string(tbits & ~TObject::kNotDeleted & ~TObject::kIsOnHeap).c_str());
2500 if (cnt == 3) {
2501 AppendOutput(stack->NextMemberSeparator(), "\"fPID\"");
2503 AppendOutput((stack->fValues.size() > 2) ? stack->fValues[2].c_str() : fValue.Data());
2504 }
2505
2506 stack->fValues.clear();
2507 fValue.Clear();
2508 return;
2509 }
2510
2511 } else if (isTArray) {
2512 // for TArray one deletes complete stack
2513 stack->fValues.clear();
2514 }
2515
2516 if (elem && elem->IsBase() && (fValue.Length() == 0)) {
2517 // here base class data already completely stored
2518 return;
2519 }
2520
2521 if (!stack->fValues.empty()) {
2522 // append element blob data just as abstract array, user is responsible to decode it
2523 AppendOutput("[");
2524 for (auto &blob: stack->fValues) {
2525 AppendOutput(blob.c_str());
2527 }
2528 }
2529
2530 if (fValue.Length() == 0) {
2531 AppendOutput("null");
2532 } else {
2534 fValue.Clear();
2535 }
2536
2537 if (!stack->fValues.empty())
2538 AppendOutput("]");
2539}
2540
2541////////////////////////////////////////////////////////////////////////////////
2542/// suppressed function of TBuffer
2543
2545{
2546 return nullptr;
2547}
2548
2549////////////////////////////////////////////////////////////////////////////////
2550/// suppressed function of TBuffer
2551
2553
2554////////////////////////////////////////////////////////////////////////////////
2555/// read version value from buffer
2556
2558{
2559 Version_t res = cl ? cl->GetClassVersion() : 0;
2560
2561 if (start)
2562 *start = 0;
2563 if (bcnt)
2564 *bcnt = 0;
2565
2566 if (!cl && Stack()->fClVersion) {
2567 res = Stack()->fClVersion;
2568 Stack()->fClVersion = 0;
2569 }
2570
2571 if (gDebug > 3)
2572 Info("ReadVersion", "Result: %d Class: %s", res, (cl ? cl->GetName() : "---"));
2573
2574 return res;
2575}
2576
2577////////////////////////////////////////////////////////////////////////////////
2578/// Ignored in TBufferJSON
2579
2580UInt_t TBufferJSON::WriteVersion(const TClass * /*cl*/, Bool_t /* useBcnt */)
2581{
2582 return 0;
2583}
2584
2585////////////////////////////////////////////////////////////////////////////////
2586/// Read object from buffer. Only used from TBuffer
2587
2589{
2590 if (gDebug > 2)
2591 Info("ReadObjectAny", "From current JSON node");
2592 void *res = JsonReadObject(nullptr, expectedClass);
2593 return res;
2594}
2595
2596////////////////////////////////////////////////////////////////////////////////
2597/// Skip any kind of object from buffer
2598
2600
2601////////////////////////////////////////////////////////////////////////////////
2602/// Write object to buffer. Only used from TBuffer
2603
2605{
2606 if (gDebug > 3)
2607 Info("WriteObjectClass", "Class %s", (actualClass ? actualClass->GetName() : " null"));
2608
2610}
2611
2612////////////////////////////////////////////////////////////////////////////////
2613/// If value exists, push in the current stack for post-processing
2614
2616{
2617 if (fValue.Length() > 0)
2619}
2620
2621////////////////////////////////////////////////////////////////////////////////
2622/// Read array of Bool_t from buffer
2623
2628
2629////////////////////////////////////////////////////////////////////////////////
2630/// Read array of Char_t from buffer
2631
2636
2637////////////////////////////////////////////////////////////////////////////////
2638/// Read array of UChar_t from buffer
2639
2644
2645////////////////////////////////////////////////////////////////////////////////
2646/// Read array of Short_t from buffer
2647
2652
2653////////////////////////////////////////////////////////////////////////////////
2654/// Read array of UShort_t from buffer
2655
2660
2661////////////////////////////////////////////////////////////////////////////////
2662/// Read array of Int_t from buffer
2663
2665{
2666 return JsonReadArray(i);
2667}
2668
2669////////////////////////////////////////////////////////////////////////////////
2670/// Read array of UInt_t from buffer
2671
2673{
2674 return JsonReadArray(i);
2675}
2676
2677////////////////////////////////////////////////////////////////////////////////
2678/// Read array of Long_t from buffer
2679
2684
2685////////////////////////////////////////////////////////////////////////////////
2686/// Read array of ULong_t from buffer
2687
2692
2693////////////////////////////////////////////////////////////////////////////////
2694/// Read array of Long64_t from buffer
2695
2700
2701////////////////////////////////////////////////////////////////////////////////
2702/// Read array of ULong64_t from buffer
2703
2708
2709////////////////////////////////////////////////////////////////////////////////
2710/// Read array of Float_t from buffer
2711
2716
2717////////////////////////////////////////////////////////////////////////////////
2718/// Read array of Double_t from buffer
2719
2724
2725////////////////////////////////////////////////////////////////////////////////
2726/// Read static array from JSON - not used
2727
2728template <typename T>
2730{
2731 Info("ReadArray", "Not implemented");
2732 return value ? 1 : 0;
2733}
2734
2735////////////////////////////////////////////////////////////////////////////////
2736/// Read array of Bool_t from buffer
2737
2742
2743////////////////////////////////////////////////////////////////////////////////
2744/// Read array of Char_t from buffer
2745
2750
2751////////////////////////////////////////////////////////////////////////////////
2752/// Read array of UChar_t from buffer
2753
2758
2759////////////////////////////////////////////////////////////////////////////////
2760/// Read array of Short_t from buffer
2761
2766
2767////////////////////////////////////////////////////////////////////////////////
2768/// Read array of UShort_t from buffer
2769
2774
2775////////////////////////////////////////////////////////////////////////////////
2776/// Read array of Int_t from buffer
2777
2782
2783////////////////////////////////////////////////////////////////////////////////
2784/// Read array of UInt_t from buffer
2785
2790
2791////////////////////////////////////////////////////////////////////////////////
2792/// Read array of Long_t from buffer
2793
2798
2799////////////////////////////////////////////////////////////////////////////////
2800/// Read array of ULong_t from buffer
2801
2806
2807////////////////////////////////////////////////////////////////////////////////
2808/// Read array of Long64_t from buffer
2809
2814
2815////////////////////////////////////////////////////////////////////////////////
2816/// Read array of ULong64_t from buffer
2817
2822
2823////////////////////////////////////////////////////////////////////////////////
2824/// Read array of Float_t from buffer
2825
2830
2831////////////////////////////////////////////////////////////////////////////////
2832/// Read array of Double_t from buffer
2833
2838
2839////////////////////////////////////////////////////////////////////////////////
2840/// Template method to read array from the JSON
2841
2842template <typename T>
2844{
2845 if (!arr || (arrsize <= 0))
2846 return;
2847 nlohmann::json *json = Stack()->fNode;
2848 if (gDebug > 2)
2849 Info("ReadFastArray", "Reading array sz %d from JSON %s", arrsize, json->dump().substr(0, 30).c_str());
2850 auto indexes = Stack()->MakeReadIndexes();
2851 if (indexes) { /* at least two dims */
2852 TArrayI &indx = indexes->GetIndices();
2853 Int_t lastdim = indx.GetSize() - 1;
2854 if (indexes->TotalLength() != arrsize)
2855 Error("ReadFastArray", "Mismatch %d-dim array sizes %d %d", lastdim + 1, arrsize, (int)indexes->TotalLength());
2856 for (int cnt = 0; cnt < arrsize; ++cnt) {
2857 nlohmann::json *elem = &(json->at(indx[0]));
2858 for (int k = 1; k < lastdim; ++k)
2859 elem = &((*elem)[indx[k]]);
2860 arr[cnt] = (asstring && elem->is_string()) ? elem->get<std::string>()[indx[lastdim]] : (*elem)[indx[lastdim]].get<T>();
2861 indexes->NextSeparator();
2862 }
2863 } else if (asstring && json->is_string()) {
2864 std::string str = json->get<std::string>();
2865 for (int cnt = 0; cnt < arrsize; ++cnt)
2866 arr[cnt] = (cnt < (int)str.length()) ? str[cnt] : 0;
2867 } else if (json->is_object() && (json->count("$arr") == 1)) {
2868 if (json->at("len").get<int>() != arrsize)
2869 Error("ReadFastArray", "Mismatch compressed array size %d %d", arrsize, json->at("len").get<int>());
2870
2871 for (int cnt = 0; cnt < arrsize; ++cnt)
2872 arr[cnt] = 0;
2873
2874 if (json->count("b") == 1) {
2875 auto base64 = json->at("b").get<std::string>();
2876
2877 int offset = (json->count("o") == 1) ? json->at("o").get<int>() : 0;
2878
2879 // TODO: provide TBase64::Decode with direct write into target buffer
2880 auto decode = TBase64::Decode(base64.c_str());
2881
2882 if (arrsize * (long) sizeof(T) < (offset + decode.Length())) {
2883 Error("ReadFastArray", "Base64 data %ld larger than target array size %ld", (long) decode.Length() + offset, (long) (arrsize*sizeof(T)));
2884 } else if ((sizeof(T) > 1) && (decode.Length() % sizeof(T) != 0)) {
2885 Error("ReadFastArray", "Base64 data size %ld not matches with element size %ld", (long) decode.Length(), (long) sizeof(T));
2886 } else {
2887 memcpy((char *) arr + offset, decode.Data(), decode.Length());
2888 }
2889 return;
2890 }
2891
2892 int p = 0, id = 0;
2893 std::string idname = "", pname, vname, nname;
2894 while (p < arrsize) {
2895 pname = std::string("p") + idname;
2896 if (json->count(pname) == 1)
2897 p = json->at(pname).get<int>();
2898 vname = std::string("v") + idname;
2899 if (json->count(vname) != 1)
2900 break;
2901 nlohmann::json &v = json->at(vname);
2902 if (v.is_array()) {
2903 for (unsigned sub = 0; sub < v.size(); ++sub)
2904 arr[p++] = v[sub].get<T>();
2905 } else {
2906 nname = std::string("n") + idname;
2907 unsigned ncopy = (json->count(nname) == 1) ? json->at(nname).get<unsigned>() : 1;
2908 for (unsigned sub = 0; sub < ncopy; ++sub)
2909 arr[p++] = v.get<T>();
2910 }
2911 idname = std::to_string(++id);
2912 }
2913 } else {
2914 if ((int)json->size() != arrsize)
2915 Error("ReadFastArray", "Mismatch array sizes %d %d", arrsize, (int)json->size());
2916 for (int cnt = 0; cnt < arrsize; ++cnt)
2917 arr[cnt] = json->at(cnt).get<T>();
2918 }
2919}
2920
2921////////////////////////////////////////////////////////////////////////////////
2922/// read array of Bool_t from buffer
2923
2928
2929////////////////////////////////////////////////////////////////////////////////
2930/// read array of Char_t from buffer
2931
2933{
2934 JsonReadFastArray(c, n, true);
2935}
2936
2937////////////////////////////////////////////////////////////////////////////////
2938/// read array of Char_t from buffer
2939
2944
2945////////////////////////////////////////////////////////////////////////////////
2946/// read array of UChar_t from buffer
2947
2952
2953////////////////////////////////////////////////////////////////////////////////
2954/// read array of Short_t from buffer
2955
2960
2961////////////////////////////////////////////////////////////////////////////////
2962/// read array of UShort_t from buffer
2963
2968
2969////////////////////////////////////////////////////////////////////////////////
2970/// read array of Int_t from buffer
2971
2976
2977////////////////////////////////////////////////////////////////////////////////
2978/// read array of UInt_t from buffer
2979
2984
2985////////////////////////////////////////////////////////////////////////////////
2986/// read array of Long_t from buffer
2987
2992
2993////////////////////////////////////////////////////////////////////////////////
2994/// read array of ULong_t from buffer
2995
3000
3001////////////////////////////////////////////////////////////////////////////////
3002/// read array of Long64_t from buffer
3003
3008
3009////////////////////////////////////////////////////////////////////////////////
3010/// read array of ULong64_t from buffer
3011
3016
3017////////////////////////////////////////////////////////////////////////////////
3018/// read array of Float_t from buffer
3019
3024
3025////////////////////////////////////////////////////////////////////////////////
3026/// read array of Double_t from buffer
3027
3032
3033////////////////////////////////////////////////////////////////////////////////
3034/// Read an array of 'n' objects from the I/O buffer.
3035/// Stores the objects read starting at the address 'start'.
3036/// The objects in the array are assume to be of class 'cl'.
3037/// Copied code from TBufferFile
3038
3039void TBufferJSON::ReadFastArray(void *start, const TClass *cl, Int_t n, TMemberStreamer * /* streamer */,
3040 const TClass * /* onFileClass */)
3041{
3042 if (gDebug > 1)
3043 Info("ReadFastArray", "void* n:%d cl:%s", n, cl->GetName());
3044
3045 // if (streamer) {
3046 // Info("ReadFastArray", "(void*) Calling streamer - not handled correctly");
3047 // streamer->SetOnFileClass(onFileClass);
3048 // (*streamer)(*this, start, 0);
3049 // return;
3050 // }
3051
3052 int objectSize = cl->Size();
3053 char *obj = (char *)start;
3054
3055 TJSONStackObj *stack = Stack();
3056 nlohmann::json *topnode = stack->fNode, *subnode = topnode;
3057 if (stack->fIndx)
3058 subnode = stack->fIndx->ExtractNode(topnode);
3059
3060 TArrayIndexProducer indexes(stack->fElem, n, "");
3061
3062 if (gDebug > 1)
3063 Info("ReadFastArray", "Indexes ndim:%d totallen:%d", indexes.NumDimensions(), indexes.TotalLength());
3064
3065 for (Int_t j = 0; j < n; j++, obj += objectSize) {
3066
3067 stack->fNode = indexes.ExtractNode(subnode);
3068
3069 JsonReadObject(obj, cl);
3070 }
3071
3072 // restore top node - show we use stack here?
3073 stack->fNode = topnode;
3074}
3075
3076////////////////////////////////////////////////////////////////////////////////
3077/// redefined here to avoid warning message from gcc
3078
3080 TMemberStreamer * /* streamer */, const TClass * /* onFileClass */)
3081{
3082 if (gDebug > 1)
3083 Info("ReadFastArray", "void** n:%d cl:%s prealloc:%s", n, cl->GetName(), (isPreAlloc ? "true" : "false"));
3084
3085 // if (streamer) {
3086 // Info("ReadFastArray", "(void**) Calling streamer - not handled correctly");
3087 // if (isPreAlloc) {
3088 // for (Int_t j = 0; j < n; j++) {
3089 // if (!start[j])
3090 // start[j] = cl->New();
3091 // }
3092 // }
3093 // streamer->SetOnFileClass(onFileClass);
3094 // (*streamer)(*this, (void *)start, 0);
3095 // return;
3096 // }
3097
3098 TJSONStackObj *stack = Stack();
3099 nlohmann::json *topnode = stack->fNode, *subnode = topnode;
3100 if (stack->fIndx)
3101 subnode = stack->fIndx->ExtractNode(topnode);
3102
3103 TArrayIndexProducer indexes(stack->fElem, n, "");
3104
3105 for (Int_t j = 0; j < n; j++) {
3106
3107 stack->fNode = indexes.ExtractNode(subnode);
3108
3109 if (!isPreAlloc) {
3110 void *old = start[j];
3111 start[j] = JsonReadObject(nullptr, cl);
3112 if (old && old != start[j] && TStreamerInfo::CanDelete())
3113 (const_cast<TClass *>(cl))->Destructor(old, kFALSE); // call delete and destruct
3114 } else {
3115 if (!start[j])
3116 start[j] = (const_cast<TClass *>(cl))->New();
3117 JsonReadObject(start[j], cl);
3118 }
3119 }
3120
3121 stack->fNode = topnode;
3122}
3123
3124template <typename T>
3126{
3127 bool is_base64 = Stack()->fBase64 || (fArrayCompact == kBase64);
3128
3129 if (!is_base64 && ((fArrayCompact == 0) || (arrsize < 6))) {
3130 fValue.Append("[");
3131 for (Int_t indx = 0; indx < arrsize; indx++) {
3132 if (indx > 0)
3135 }
3136 fValue.Append("]");
3137 } else if (is_base64 && !arrsize) {
3138 fValue.Append("[]");
3139 } else {
3140 fValue.Append("{");
3141 fValue.Append(TString::Format("\"$arr\":\"%s\"%s\"len\":%d", typname, fArraySepar.Data(), arrsize));
3142 Int_t aindx(0), bindx(arrsize);
3143 while ((aindx < arrsize) && (vname[aindx] == 0))
3144 aindx++;
3145 while ((aindx < bindx) && (vname[bindx - 1] == 0))
3146 bindx--;
3147
3148 if (is_base64) {
3149 // small initial offset makes no sense - JSON code is large then size gain
3150 if ((aindx * sizeof(T) < 5) && (aindx < bindx))
3151 aindx = 0;
3152
3153 if ((aindx > 0) && (aindx < bindx))
3154 fValue.Append(TString::Format("%s\"o\":%ld", fArraySepar.Data(), (long) (aindx * (int) sizeof(T))));
3155
3157 fValue.Append("\"b\":\"");
3158
3159 if (aindx < bindx)
3160 fValue.Append(TBase64::Encode((const char *) (vname + aindx), (bindx - aindx) * sizeof(T)));
3161
3162 fValue.Append("\"");
3163 } else if (aindx < bindx) {
3164 TString suffix("");
3165 Int_t p(aindx), suffixcnt(-1), lastp(0);
3166 while (p < bindx) {
3167 if (vname[p] == 0) {
3168 p++;
3169 continue;
3170 }
3171 Int_t p0(p++), pp(0), nsame(1);
3173 pp = bindx;
3174 p = bindx + 1;
3175 nsame = 0;
3176 }
3177 for (; p <= bindx; ++p) {
3178 if ((p < bindx) && (vname[p] == vname[p - 1])) {
3179 nsame++;
3180 continue;
3181 }
3182 if (vname[p - 1] == 0) {
3183 if (nsame > 9) {
3184 nsame = 0;
3185 break;
3186 }
3187 } else if (nsame > 5) {
3188 if (pp) {
3189 p = pp;
3190 nsame = 0;
3191 } else
3192 pp = p;
3193 break;
3194 }
3195 pp = p;
3196 nsame = 1;
3197 }
3198 if (pp <= p0)
3199 continue;
3200 if (++suffixcnt > 0)
3201 suffix.Form("%d", suffixcnt);
3202 if (p0 != lastp)
3203 fValue.Append(TString::Format("%s\"p%s\":%d", fArraySepar.Data(), suffix.Data(), p0));
3204 lastp = pp; /* remember cursor, it may be the same */
3205 fValue.Append(TString::Format("%s\"v%s\":", fArraySepar.Data(), suffix.Data()));
3206 if ((nsame > 1) || (pp - p0 == 1)) {
3208 if (nsame > 1)
3209 fValue.Append(TString::Format("%s\"n%s\":%d", fArraySepar.Data(), suffix.Data(), nsame));
3210 } else {
3211 fValue.Append("[");
3212 for (Int_t indx = p0; indx < pp; indx++) {
3213 if (indx > p0)
3216 }
3217 fValue.Append("]");
3218 }
3219 }
3220 }
3221 fValue.Append("}");
3222 }
3223}
3224
3225////////////////////////////////////////////////////////////////////////////////
3226/// Write array of Bool_t to buffer
3227
3229{
3230 JsonPushValue();
3231 JsonWriteArrayCompress(b, n, "Bool");
3232}
3233
3234////////////////////////////////////////////////////////////////////////////////
3235/// Write array of Char_t to buffer
3236
3238{
3239 JsonPushValue();
3240 JsonWriteArrayCompress(c, n, "Int8");
3241}
3242
3243////////////////////////////////////////////////////////////////////////////////
3244/// Write array of UChar_t to buffer
3245
3247{
3248 JsonPushValue();
3249 JsonWriteArrayCompress(c, n, "Uint8");
3250}
3251
3252////////////////////////////////////////////////////////////////////////////////
3253/// Write array of Short_t to buffer
3254
3256{
3257 JsonPushValue();
3258 JsonWriteArrayCompress(h, n, "Int16");
3259}
3260
3261////////////////////////////////////////////////////////////////////////////////
3262/// Write array of UShort_t to buffer
3263
3265{
3266 JsonPushValue();
3267 JsonWriteArrayCompress(h, n, "Uint16");
3268}
3269
3270////////////////////////////////////////////////////////////////////////////////
3271/// Write array of Int_ to buffer
3272
3274{
3275 JsonPushValue();
3276 JsonWriteArrayCompress(i, n, "Int32");
3277}
3278
3279////////////////////////////////////////////////////////////////////////////////
3280/// Write array of UInt_t to buffer
3281
3283{
3284 JsonPushValue();
3285 JsonWriteArrayCompress(i, n, "Uint32");
3286}
3287
3288////////////////////////////////////////////////////////////////////////////////
3289/// Write array of Long_t to buffer
3290
3292{
3293 JsonPushValue();
3294 JsonWriteArrayCompress(l, n, "Int64");
3295}
3296
3297////////////////////////////////////////////////////////////////////////////////
3298/// Write array of ULong_t to buffer
3299
3301{
3302 JsonPushValue();
3303 JsonWriteArrayCompress(l, n, "Uint64");
3304}
3305
3306////////////////////////////////////////////////////////////////////////////////
3307/// Write array of Long64_t to buffer
3308
3310{
3311 JsonPushValue();
3312 JsonWriteArrayCompress(l, n, "Int64");
3313}
3314
3315////////////////////////////////////////////////////////////////////////////////
3316/// Write array of ULong64_t to buffer
3317
3319{
3320 JsonPushValue();
3321 JsonWriteArrayCompress(l, n, "Uint64");
3322}
3323
3324////////////////////////////////////////////////////////////////////////////////
3325/// Write array of Float_t to buffer
3326
3328{
3329 JsonPushValue();
3330 JsonWriteArrayCompress(f, n, "Float32");
3331}
3332
3333////////////////////////////////////////////////////////////////////////////////
3334/// Write array of Double_t to buffer
3335
3337{
3338 JsonPushValue();
3339 JsonWriteArrayCompress(d, n, "Float64");
3340}
3341
3342////////////////////////////////////////////////////////////////////////////////
3343/// Template method to write array of arbitrary dimensions
3344/// Different methods can be used for store last array dimension -
3345/// either JsonWriteArrayCompress<T>() or JsonWriteConstChar()
3346/// \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.
3347///
3348template <typename T>
3350 void (TBufferJSON::*method)(const T *, Int_t, const char *))
3351{
3352 JsonPushValue();
3353 if (arrsize <= 0) { /*fJsonrCnt++;*/
3354 fValue.Append("[]");
3355 return;
3356 }
3357 const Int_t maxElements = std::numeric_limits<Int_t>::max();
3358 if (arrsize > maxElements) {
3359 Fatal("JsonWriteFastArray", "Array larger than 2^31 elements cannot be stored in JSON");
3360 return; // In case the user re-routes the error handler to not die when Fatal is called
3361 }
3362
3364 if (elem && (elem->GetArrayDim() > 1) && (elem->GetArrayLength() == arrsize)) {
3365 TArrayI indexes(elem->GetArrayDim() - 1);
3366 indexes.Reset(0);
3367 Int_t cnt = 0, shift = 0, len = elem->GetMaxIndex(indexes.GetSize());
3368 while (cnt >= 0) {
3369 if (indexes[cnt] >= elem->GetMaxIndex(cnt)) {
3370 fValue.Append("]");
3371 indexes[cnt--] = 0;
3372 if (cnt >= 0)
3373 indexes[cnt]++;
3374 continue;
3375 }
3376 fValue.Append(indexes[cnt] == 0 ? "[" : fArraySepar.Data());
3377 if (++cnt == indexes.GetSize()) {
3378 (*this.*method)((arr + shift), len, typname);
3379 indexes[--cnt]++;
3380 shift += len;
3381 }
3382 }
3383 } else {
3384 (*this.*method)(arr, arrsize, typname);
3385 }
3386}
3387
3388////////////////////////////////////////////////////////////////////////////////
3389/// Write array of Bool_t to buffer
3390
3392{
3393 JsonWriteFastArray(b, n, "Bool", &TBufferJSON::JsonWriteArrayCompress<Bool_t>);
3394}
3395
3396////////////////////////////////////////////////////////////////////////////////
3397/// Write array of Char_t to buffer
3398///
3399/// Normally written as JSON string, but if string includes \0 in the middle
3400/// or some special characters, uses regular array. From array size 1000 it
3401/// will be automatically converted into base64 coding
3402
3404{
3405 Bool_t need_blob = false;
3406 Bool_t has_zero = false;
3407 for (Long64_t i=0;i<n;++i) {
3408 if (!c[i]) {
3409 has_zero = true; // might be terminal '\0'
3410 } else if (has_zero || !isprint(c[i])) {
3411 need_blob = true;
3412 break;
3413 }
3414 }
3415
3416 if (need_blob && (n >= 1000) && (!Stack()->fElem || (Stack()->fElem->GetArrayDim() < 2)))
3417 Stack()->fBase64 = true;
3418
3419 JsonWriteFastArray(c, n, "Int8", need_blob ? &TBufferJSON::JsonWriteArrayCompress<Char_t> : &TBufferJSON::JsonWriteConstChar);
3420}
3421
3422////////////////////////////////////////////////////////////////////////////////
3423/// Write array of Char_t to buffer
3424
3429
3430////////////////////////////////////////////////////////////////////////////////
3431/// Write array of UChar_t to buffer
3432
3434{
3435 JsonWriteFastArray(c, n, "Uint8", &TBufferJSON::JsonWriteArrayCompress<UChar_t>);
3436}
3437
3438////////////////////////////////////////////////////////////////////////////////
3439/// Write array of Short_t to buffer
3440
3442{
3443 JsonWriteFastArray(h, n, "Int16", &TBufferJSON::JsonWriteArrayCompress<Short_t>);
3444}
3445
3446////////////////////////////////////////////////////////////////////////////////
3447/// Write array of UShort_t to buffer
3448
3450{
3451 JsonWriteFastArray(h, n, "Uint16", &TBufferJSON::JsonWriteArrayCompress<UShort_t>);
3452}
3453
3454////////////////////////////////////////////////////////////////////////////////
3455/// Write array of Int_t to buffer
3456
3458{
3459 JsonWriteFastArray(i, n, "Int32", &TBufferJSON::JsonWriteArrayCompress<Int_t>);
3460}
3461
3462////////////////////////////////////////////////////////////////////////////////
3463/// Write array of UInt_t to buffer
3464
3466{
3467 JsonWriteFastArray(i, n, "Uint32", &TBufferJSON::JsonWriteArrayCompress<UInt_t>);
3468}
3469
3470////////////////////////////////////////////////////////////////////////////////
3471/// Write array of Long_t to buffer
3472
3474{
3475 JsonWriteFastArray(l, n, "Int64", &TBufferJSON::JsonWriteArrayCompress<Long_t>);
3476}
3477
3478////////////////////////////////////////////////////////////////////////////////
3479/// Write array of ULong_t to buffer
3480
3482{
3483 JsonWriteFastArray(l, n, "Uint64", &TBufferJSON::JsonWriteArrayCompress<ULong_t>);
3484}
3485
3486////////////////////////////////////////////////////////////////////////////////
3487/// Write array of Long64_t to buffer
3488
3490{
3491 JsonWriteFastArray(l, n, "Int64", &TBufferJSON::JsonWriteArrayCompress<Long64_t>);
3492}
3493
3494////////////////////////////////////////////////////////////////////////////////
3495/// Write array of ULong64_t to buffer
3496
3498{
3499 JsonWriteFastArray(l, n, "Uint64", &TBufferJSON::JsonWriteArrayCompress<ULong64_t>);
3500}
3501
3502////////////////////////////////////////////////////////////////////////////////
3503/// Write array of Float_t to buffer
3504
3506{
3507 JsonWriteFastArray(f, n, "Float32", &TBufferJSON::JsonWriteArrayCompress<Float_t>);
3508}
3509
3510////////////////////////////////////////////////////////////////////////////////
3511/// Write array of Double_t to buffer
3512
3514{
3515 JsonWriteFastArray(d, n, "Float64", &TBufferJSON::JsonWriteArrayCompress<Double_t>);
3516}
3517
3518////////////////////////////////////////////////////////////////////////////////
3519/// Recall TBuffer function to avoid gcc warning message
3520
3521void TBufferJSON::WriteFastArray(void *start, const TClass *cl, Long64_t n, TMemberStreamer * /* streamer */)
3522{
3523 if (gDebug > 2)
3524 Info("WriteFastArray", "void *start cl:%s n:%lld", cl ? cl->GetName() : "---", n);
3525
3526 // if (streamer) {
3527 // JsonDisablePostprocessing();
3528 // (*streamer)(*this, start, 0);
3529 // return;
3530 // }
3531
3532 if (n < 0) {
3533 // special handling of empty StreamLoop
3534 AppendOutput("null");
3536 } else {
3537
3538 char *obj = (char *)start;
3539 if (!n)
3540 n = 1;
3541 int size = cl->Size();
3542
3544
3545 if (indexes.IsArray()) {
3547 AppendOutput(indexes.GetBegin());
3548 }
3549
3550 for (Long64_t j = 0; j < n; j++, obj += size) {
3551
3552 if (j > 0)
3553 AppendOutput(indexes.NextSeparator());
3554
3555 JsonWriteObject(obj, cl, kFALSE);
3556
3557 if (indexes.IsArray() && (fValue.Length() > 0)) {
3559 fValue.Clear();
3560 }
3561 }
3562
3563 if (indexes.IsArray())
3564 AppendOutput(indexes.GetEnd());
3565 }
3566
3567 if (Stack()->fIndx)
3568 AppendOutput(Stack()->fIndx->NextSeparator());
3569}
3570
3571////////////////////////////////////////////////////////////////////////////////
3572/// Recall TBuffer function to avoid gcc warning message
3573
3575 TMemberStreamer * /* streamer */)
3576{
3577 if (gDebug > 2)
3578 Info("WriteFastArray", "void **startp cl:%s n:%lld", cl->GetName(), n);
3579
3580 // if (streamer) {
3581 // JsonDisablePostprocessing();
3582 // (*streamer)(*this, (void *)start, 0);
3583 // return 0;
3584 // }
3585
3586 if (n <= 0)
3587 return 0;
3588
3589 Int_t res = 0;
3590
3592
3593 if (indexes.IsArray()) {
3595 AppendOutput(indexes.GetBegin());
3596 }
3597
3598 for (Long64_t j = 0; j < n; j++) {
3599
3600 if (j > 0)
3601 AppendOutput(indexes.NextSeparator());
3602
3603 if (!isPreAlloc) {
3604 res |= WriteObjectAny(start[j], cl);
3605 } else {
3606 if (!start[j])
3607 start[j] = (const_cast<TClass *>(cl))->New();
3608 // ((TClass*)cl)->Streamer(start[j],*this);
3609 JsonWriteObject(start[j], cl, kFALSE);
3610 }
3611
3612 if (indexes.IsArray() && (fValue.Length() > 0)) {
3614 fValue.Clear();
3615 }
3616 }
3617
3618 if (indexes.IsArray())
3619 AppendOutput(indexes.GetEnd());
3620
3621 if (Stack()->fIndx)
3622 AppendOutput(Stack()->fIndx->NextSeparator());
3623
3624 return res;
3625}
3626
3627////////////////////////////////////////////////////////////////////////////////
3628/// stream object to/from buffer
3629
3630void TBufferJSON::StreamObject(void *obj, const TClass *cl, const TClass * /* onfileClass */)
3631{
3632 if (gDebug > 3)
3633 Info("StreamObject", "Class: %s", (cl ? cl->GetName() : "none"));
3634
3635 if (IsWriting())
3636 JsonWriteObject(obj, cl);
3637 else
3638 JsonReadObject(obj, cl);
3639}
3640
3641////////////////////////////////////////////////////////////////////////////////
3642/// Template function to read basic value from JSON
3643
3644template <typename T>
3646{
3647 value = Stack()->GetStlNode()->get<T>();
3648}
3649
3650////////////////////////////////////////////////////////////////////////////////
3651/// Reads Bool_t value from buffer
3652
3654{
3655 JsonReadBasic(val);
3656}
3657
3658////////////////////////////////////////////////////////////////////////////////
3659/// Reads Char_t value from buffer
3660
3662{
3663 if (!Stack()->fValues.empty())
3664 val = (Char_t)Stack()->PopIntValue();
3665 else
3666 val = Stack()->GetStlNode()->get<Char_t>();
3667}
3668
3669////////////////////////////////////////////////////////////////////////////////
3670/// Reads UChar_t value from buffer
3671
3673{
3674 JsonReadBasic(val);
3675}
3676
3677////////////////////////////////////////////////////////////////////////////////
3678/// Reads Short_t value from buffer
3679
3681{
3682 JsonReadBasic(val);
3683}
3684
3685////////////////////////////////////////////////////////////////////////////////
3686/// Reads UShort_t value from buffer
3687
3689{
3690 JsonReadBasic(val);
3691}
3692
3693////////////////////////////////////////////////////////////////////////////////
3694/// Reads Int_t value from buffer
3695
3697{
3698 if (!Stack()->fValues.empty())
3699 val = Stack()->PopIntValue();
3700 else
3701 JsonReadBasic(val);
3702}
3703
3704////////////////////////////////////////////////////////////////////////////////
3705/// Reads UInt_t value from buffer
3706
3708{
3709 JsonReadBasic(val);
3710}
3711
3712////////////////////////////////////////////////////////////////////////////////
3713/// Reads Long_t value from buffer
3714
3716{
3717 JsonReadBasic(val);
3718}
3719
3720////////////////////////////////////////////////////////////////////////////////
3721/// Reads ULong_t value from buffer
3722
3724{
3725 JsonReadBasic(val);
3726}
3727
3728////////////////////////////////////////////////////////////////////////////////
3729/// Reads Long64_t value from buffer
3730
3732{
3733 JsonReadBasic(val);
3734}
3735
3736////////////////////////////////////////////////////////////////////////////////
3737/// Reads ULong64_t value from buffer
3738
3740{
3741 JsonReadBasic(val);
3742}
3743
3744////////////////////////////////////////////////////////////////////////////////
3745/// Reads Float_t value from buffer
3746
3748{
3749 nlohmann::json *json = Stack()->GetStlNode();
3750 if (json->is_null())
3751 val = std::numeric_limits<Float_t>::quiet_NaN();
3752 else
3753 try {
3754 val = json->get<Float_t>();
3755 } catch (nlohmann::detail::type_error &e) {
3756 auto aux = json->get<std::string>();
3757 if (aux == "nanf") {
3758 val = std::numeric_limits<Float_t>::quiet_NaN();
3759 } else if (aux == "inff") {
3760 val = std::numeric_limits<Float_t>::infinity();
3761 } else if (aux == "-inff") {
3762 val = -std::numeric_limits<Float_t>::infinity();
3763 } else {
3764 Error("ReadFloat", "%s '%s'", e.what(), aux.c_str());
3765 val = std::numeric_limits<Float_t>::quiet_NaN();
3766 }
3767 }
3768}
3769
3770////////////////////////////////////////////////////////////////////////////////
3771/// Reads Double_t value from buffer
3772
3774{
3775 nlohmann::json *json = Stack()->GetStlNode();
3776 if (json->is_null())
3777 val = std::numeric_limits<Double_t>::quiet_NaN();
3778 else
3779 try {
3780 val = json->get<Double_t>();
3781 } catch (nlohmann::detail::type_error &e) {
3782 auto aux = json->get<std::string>();
3783 if (aux == "nan") {
3784 val = std::numeric_limits<Double_t>::quiet_NaN();
3785 } else if (aux == "inf") {
3786 val = std::numeric_limits<Double_t>::infinity();
3787 } else if (aux == "-inf") {
3788 val = -std::numeric_limits<Double_t>::infinity();
3789 } else {
3790 Error("ReadDouble", "%s '%s'", e.what(), aux.c_str());
3791 val = std::numeric_limits<Double_t>::quiet_NaN();
3792 }
3793 }
3794}
3795
3796////////////////////////////////////////////////////////////////////////////////
3797/// Reads array of characters from buffer
3798
3800{
3801 Error("ReadCharP", "Not implemented");
3802}
3803
3804////////////////////////////////////////////////////////////////////////////////
3805/// Reads a TString
3806
3808{
3809 std::string str;
3810 JsonReadBasic(str);
3811 val = str.c_str();
3812}
3813
3814////////////////////////////////////////////////////////////////////////////////
3815/// Reads a std::string
3816
3817void TBufferJSON::ReadStdString(std::string *val)
3818{
3819 JsonReadBasic(*val);
3820}
3821
3822////////////////////////////////////////////////////////////////////////////////
3823/// Reads a char* string
3824
3826{
3827 std::string str;
3828 JsonReadBasic(str);
3829
3830 if (s) {
3831 delete[] s;
3832 s = nullptr;
3833 }
3834
3835 std::size_t nch = str.length();
3836 if (nch > 0) {
3837 s = new char[nch + 1];
3838 memcpy(s, str.c_str(), nch);
3839 s[nch] = 0;
3840 }
3841}
3842
3843////////////////////////////////////////////////////////////////////////////////
3844/// Writes Bool_t value to buffer
3845
3851
3852////////////////////////////////////////////////////////////////////////////////
3853/// Writes Char_t value to buffer
3854
3860
3861////////////////////////////////////////////////////////////////////////////////
3862/// Writes UChar_t value to buffer
3863
3869
3870////////////////////////////////////////////////////////////////////////////////
3871/// Writes Short_t value to buffer
3872
3878
3879////////////////////////////////////////////////////////////////////////////////
3880/// Writes UShort_t value to buffer
3881
3887
3888////////////////////////////////////////////////////////////////////////////////
3889/// Writes Int_t value to buffer
3890
3892{
3893 JsonPushValue();
3894 JsonWriteBasic(i);
3895}
3896
3897////////////////////////////////////////////////////////////////////////////////
3898/// Writes UInt_t value to buffer
3899
3901{
3902 JsonPushValue();
3903 JsonWriteBasic(i);
3904}
3905
3906////////////////////////////////////////////////////////////////////////////////
3907/// Writes Long_t value to buffer
3908
3914
3915////////////////////////////////////////////////////////////////////////////////
3916/// Writes ULong_t value to buffer
3917
3923
3924////////////////////////////////////////////////////////////////////////////////
3925/// Writes Long64_t value to buffer
3926
3932
3933////////////////////////////////////////////////////////////////////////////////
3934/// Writes ULong64_t value to buffer
3935
3941
3942////////////////////////////////////////////////////////////////////////////////
3943/// Writes Float_t value to buffer
3944
3950
3951////////////////////////////////////////////////////////////////////////////////
3952/// Writes Double_t value to buffer
3953
3959
3960////////////////////////////////////////////////////////////////////////////////
3961/// Writes array of characters to buffer
3962
3964{
3965 JsonPushValue();
3966
3968}
3969
3970////////////////////////////////////////////////////////////////////////////////
3971/// Writes a TString
3972
3974{
3975 JsonPushValue();
3976
3977 JsonWriteConstChar(s.Data(), s.Length());
3978}
3979
3980////////////////////////////////////////////////////////////////////////////////
3981/// Writes a std::string
3982
3983void TBufferJSON::WriteStdString(const std::string *s)
3984{
3985 JsonPushValue();
3986
3987 if (s)
3988 JsonWriteConstChar(s->c_str(), s->length());
3989 else
3990 JsonWriteConstChar("", 0);
3991}
3992
3993////////////////////////////////////////////////////////////////////////////////
3994/// Writes a char*
3995
3997{
3998 JsonPushValue();
3999
4001}
4002
4003////////////////////////////////////////////////////////////////////////////////
4004/// converts Char_t to string and add to json value buffer
4005
4007{
4008 char buf[50];
4009 snprintf(buf, sizeof(buf), "%d", value);
4010 fValue.Append(buf);
4011}
4012
4013////////////////////////////////////////////////////////////////////////////////
4014/// converts Short_t to string and add to json value buffer
4015
4017{
4018 char buf[50];
4019 snprintf(buf, sizeof(buf), "%hd", value);
4020 fValue.Append(buf);
4021}
4022
4023////////////////////////////////////////////////////////////////////////////////
4024/// converts Int_t to string and add to json value buffer
4025
4027{
4028 char buf[50];
4029 snprintf(buf, sizeof(buf), "%d", value);
4030 fValue.Append(buf);
4031}
4032
4033////////////////////////////////////////////////////////////////////////////////
4034/// converts Long_t to string and add to json value buffer
4035
4037{
4038 char buf[50];
4039 snprintf(buf, sizeof(buf), "%ld", value);
4040 fValue.Append(buf);
4041}
4042
4043////////////////////////////////////////////////////////////////////////////////
4044/// converts Long64_t to string and add to json value buffer
4045
4047{
4048 fValue.Append(std::to_string(value).c_str());
4049}
4050
4051////////////////////////////////////////////////////////////////////////////////
4052/// converts Float_t to string and add to json value buffer
4053
4055{
4056 if (std::isinf(value)) {
4057 if (!fStoreInfNaN)
4058 fValue.Append((value < 0.) ? "-2e308" : "2e308"); // JavaScript Number.MAX_VALUE is approx 1.79e308
4059 else
4060 fValue.Append((value < 0.) ? "\"-inff\"" : "\"inff\"");
4061 } else if (std::isnan(value)) {
4062 if (!fStoreInfNaN)
4063 fValue.Append("null");
4064 else
4065 fValue.Append("\"nanf\"");
4066 } else {
4067 char buf[200];
4068 ConvertFloat(value, buf, sizeof(buf));
4069 fValue.Append(buf);
4070 }
4071}
4072
4073////////////////////////////////////////////////////////////////////////////////
4074/// converts Double_t to string and add to json value buffer
4075
4077{
4078 if (std::isinf(value)) {
4079 if (!fStoreInfNaN)
4080 fValue.Append((value < 0.) ? "-2e308" : "2e308"); // JavaScript Number.MAX_VALUE is approx 1.79e308
4081 else
4082 fValue.Append((value < 0.) ? "\"-inf\"" : "\"inf\"");
4083 } else if (std::isnan(value)) {
4084 if (!fStoreInfNaN)
4085 fValue.Append("null");
4086 else
4087 fValue.Append("\"nan\"");
4088 } else {
4089 char buf[200];
4090 ConvertDouble(value, buf, sizeof(buf));
4091 fValue.Append(buf);
4092 }
4093}
4094
4095////////////////////////////////////////////////////////////////////////////////
4096/// converts Bool_t to string and add to json value buffer
4097
4099{
4100 fValue.Append(value ? "true" : "false");
4101}
4102
4103////////////////////////////////////////////////////////////////////////////////
4104/// converts UChar_t to string and add to json value buffer
4105
4107{
4108 char buf[50];
4109 snprintf(buf, sizeof(buf), "%u", value);
4110 fValue.Append(buf);
4111}
4112
4113////////////////////////////////////////////////////////////////////////////////
4114/// converts UShort_t to string and add to json value buffer
4115
4117{
4118 char buf[50];
4119 snprintf(buf, sizeof(buf), "%hu", value);
4120 fValue.Append(buf);
4121}
4122
4123////////////////////////////////////////////////////////////////////////////////
4124/// converts UInt_t to string and add to json value buffer
4125
4127{
4128 char buf[50];
4129 snprintf(buf, sizeof(buf), "%u", value);
4130 fValue.Append(buf);
4131}
4132
4133////////////////////////////////////////////////////////////////////////////////
4134/// converts ULong_t to string and add to json value buffer
4135
4137{
4138 char buf[50];
4139 snprintf(buf, sizeof(buf), "%lu", value);
4140 fValue.Append(buf);
4141}
4142
4143////////////////////////////////////////////////////////////////////////////////
4144/// converts ULong64_t to string and add to json value buffer
4145
4147{
4148 fValue.Append(std::to_string(value).c_str());
4149}
4150
4151////////////////////////////////////////////////////////////////////////////////
4152/// writes string value, processing all kind of special characters
4153
4154void TBufferJSON::JsonWriteConstChar(const char *value, Int_t len, const char * /* typname */)
4155{
4156 if (!value) {
4157
4158 fValue.Append("\"\"");
4159
4160 } else {
4161
4162 fValue.Append("\"");
4163
4164 if (len < 0)
4165 len = strlen(value);
4166
4167 for (Int_t n = 0; n < len; n++) {
4168 unsigned char c = value[n];
4169 switch (c) {
4170 case 0: n = len; break;
4171 case '\n': fValue.Append("\\n"); break;
4172 case '\t': fValue.Append("\\t"); break;
4173 case '\"': fValue.Append("\\\""); break;
4174 case '\\': fValue.Append("\\\\"); break;
4175 case '\b': fValue.Append("\\b"); break;
4176 case '\f': fValue.Append("\\f"); break;
4177 case '\r': fValue.Append("\\r"); break;
4178 case '/': fValue.Append("\\/"); break;
4179 default:
4180 if (c < 31) {
4181 fValue.Append(TString::Format("\\u%04x", (unsigned)c));
4182 } else if (c < 0x80) {
4183 fValue.Append(c);
4184 } else if ((n < len - 1) && ((c & 0xe0) == 0xc0) && ((value[n+1] & 0xc0) == 0x80)) {
4185 unsigned code = ((unsigned)value[n+1] & 0x3f) | (((unsigned) c & 0x1f) << 6);
4186 fValue.Append(TString::Format("\\u%04x", code));
4187 n++;
4188 } else if ((n < len - 2) && ((c & 0xf0) == 0xe0) && ((value[n+1] & 0xc0) == 0x80) && ((value[n+2] & 0xc0) == 0x80)) {
4189 unsigned code = ((unsigned)value[n+2] & 0x3f) | (((unsigned) value[n+1] & 0x3f) << 6) | (((unsigned) c & 0x0f) << 12);
4190 fValue.Append(TString::Format("\\u%04x", code));
4191 n+=2;
4192 } else if ((n < len - 3) && ((c & 0xf8) == 0xf0) && ((value[n+1] & 0xc0) == 0x80) && ((value[n+2] & 0xc0) == 0x80) && ((value[n+3] & 0xc0) == 0x80)) {
4193 unsigned code = ((unsigned)value[n+3] & 0x3f) | (((unsigned) value[n+2] & 0x3f) << 6) | (((unsigned) value[n+1] & 0x3f) << 12) | (((unsigned) c & 0x07) << 18);
4194 // TODO: no idea how to add codes which are higher then 0xFFFF
4195 fValue.Append(TString::Format("\\u%04x\\u%04x", code & 0xffff, code >> 16));
4196 n+=3;
4197 } else {
4198 fValue.Append(TString::Format("\\u%04x", (unsigned)c));
4199 }
4200 }
4201 }
4202
4203 fValue.Append("\"");
4204 }
4205}
4206
4207////////////////////////////////////////////////////////////////////////////////
4208/// Read data of base class.
4209
4211{
4212 if (elem->GetClassPointer() == TObject::Class()) {
4214 } else {
4216 }
4217}
nlohmann::json json
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define h(i)
Definition RSha256.hxx:106
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
unsigned short UShort_t
Unsigned Short integer 2 bytes (unsigned short)
Definition RtypesCore.h:55
long Longptr_t
Integer large enough to hold a pointer (platform-dependent)
Definition RtypesCore.h:90
short Version_t
Class version identifier (short)
Definition RtypesCore.h:80
unsigned char UChar_t
Unsigned Character 1 byte (unsigned char)
Definition RtypesCore.h:53
char Char_t
Character 1 byte (char)
Definition RtypesCore.h:52
unsigned long ULong_t
Unsigned long integer 4 bytes (unsigned long). Size depends on architecture.
Definition RtypesCore.h:70
long Long_t
Signed long integer 4 bytes (long). Size depends on architecture.
Definition RtypesCore.h:69
float Float_t
Float 4 bytes (float)
Definition RtypesCore.h:72
short Short_t
Signed Short integer 2 bytes (short)
Definition RtypesCore.h:54
constexpr Bool_t kFALSE
Definition RtypesCore.h:109
long long Long64_t
Portable signed long integer 8 bytes.
Definition RtypesCore.h:84
unsigned long long ULong64_t
Portable unsigned long integer 8 bytes.
Definition RtypesCore.h:85
constexpr Bool_t kTRUE
Definition RtypesCore.h:108
@ json_stdstring
@ json_TCollection
@ json_TString
@ json_TArray
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
@ kNoType_t
Definition TDataType.h:33
@ kFloat_t
Definition TDataType.h:31
@ kULong64_t
Definition TDataType.h:32
@ kInt_t
Definition TDataType.h:30
@ kchar
Definition TDataType.h:31
@ kLong_t
Definition TDataType.h:30
@ kDouble32_t
Definition TDataType.h:31
@ kShort_t
Definition TDataType.h:29
@ kBool_t
Definition TDataType.h:32
@ kBits
Definition TDataType.h:34
@ kULong_t
Definition TDataType.h:30
@ kLong64_t
Definition TDataType.h:32
@ kVoid_t
Definition TDataType.h:35
@ kUShort_t
Definition TDataType.h:29
@ kDouble_t
Definition TDataType.h:31
@ kCharStar
Definition TDataType.h:34
@ kChar_t
Definition TDataType.h:29
@ kUChar_t
Definition TDataType.h:29
@ kCounter
Definition TDataType.h:34
@ kUInt_t
Definition TDataType.h:30
@ kFloat16_t
Definition TDataType.h:33
@ kOther_t
Definition TDataType.h:32
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:208
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t UChar_t len
Option_t Option_t TPoint TPoint const char mode
char name[80]
Definition TGX11.cxx: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...
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
@ 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 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:1082
static TClass * Class()
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:548
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1096
virtual void Fatal(const char *method, const char *msgfmt,...) const
Issue fatal error message.
Definition TObject.cxx:1124
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1070
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:427
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:2068
void Clear()
Clear string without changing its capacity.
Definition TString.cxx:1241
const char * Data() const
Definition TString.h:386
Ssiz_t Capacity() const
Definition TString.h:374
TString & Append(const char *cs)
Definition TString.h:583
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2459
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition TString.cxx:2437
static TClass * Class()
Abstract Interface class describing Streamer information for one class.
static Bool_t CanDelete()
static function returning true if ReadBuffer can delete object
const Int_t n
Definition legend1.C:16
@ kSTLbitset
Definition ESTLType.h:37
@ kSTLmap
Definition ESTLType.h:33
@ kSTLunorderedmultiset
Definition ESTLType.h:43
@ kSTLend
Definition ESTLType.h:47
@ kSTLset
Definition ESTLType.h:35
@ kSTLmultiset
Definition ESTLType.h:36
@ kSTLdeque
Definition ESTLType.h:32
@ kSTLvector
Definition ESTLType.h:30
@ kSTLunorderedmultimap
Definition ESTLType.h:45
@ kSTLunorderedset
Definition ESTLType.h:42
@ kSTLlist
Definition ESTLType.h:31
@ kSTLforwardlist
Definition ESTLType.h:41
@ kSTLunorderedmap
Definition ESTLType.h:44
@ kNotSTL
Definition ESTLType.h:29
@ kSTLmultimap
Definition ESTLType.h:34
bool IsStdClass(const char *type)
return true if the class belongs to the std namespace
@ kDefaultZLIB
Compression level reserved for ZLIB compression algorithm (fastest compression)
Definition Compression.h:74
const char * fTypeTag
! type tag used for std::map stored as JSON object
nlohmann::json fValue
! temporary value reading std::map as JSON
Bool_t fFirst
! is first or second element is used in the pair
nlohmann::json * GetStlNode(nlohmann::json *prnt)
nlohmann::json::iterator fIter
! iterator for std::map stored as JSON object
Int_t fIndx
! index of object in STL container
Int_t fMap
! special iterator over STL map::key members
TLine l
Definition textangle.C:4