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