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