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