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 bool islist = col->InheritsFrom(TList::Class());
1614 TMap *map = nullptr;
1615 if (col->InheritsFrom(TMap::Class()))
1616 map = dynamic_cast<TMap *>(col);
1617
1618 TString sopt;
1619 if (islist) {
1620 sopt.Capacity(500);
1621 sopt = "[";
1622 }
1623
1624 TIter iter(col);
1625 TObject *obj;
1626 Bool_t first = kTRUE;
1627 while ((obj = iter()) != nullptr) {
1628 if (!first)
1630
1631 if (map) {
1632 // fJsonrCnt++; // do not account map pair as JSON object
1633 AppendOutput("{", "\"$pair\"");
1635 AppendOutput("\"TPair\"");
1636 AppendOutput(fArraySepar.Data(), "\"first\"");
1638 }
1639
1641
1642 if (map) {
1643 AppendOutput(fArraySepar.Data(), "\"second\"");
1646 AppendOutput("", "}");
1647 }
1648
1649 if (islist) {
1650 if (!first)
1651 sopt.Append(fArraySepar.Data());
1652 sopt.Append("\"");
1653 sopt.Append(iter.GetOption());
1654 sopt.Append("\"");
1655 }
1656
1657 first = kFALSE;
1658 }
1659
1660 AppendOutput("]");
1661
1662 if (islist) {
1663 sopt.Append("]");
1664 AppendOutput(Stack()->NextMemberSeparator(), "\"opt\"");
1666 AppendOutput(sopt.Data());
1667 }
1668 fValue.Clear();
1669}
1670
1671////////////////////////////////////////////////////////////////////////////////
1672/// read content of ROOT collection
1673
1675{
1676 if (!col)
1677 return;
1678
1679 TList *lst = nullptr;
1680 TMap *map = nullptr;
1681 TClonesArray *clones = nullptr;
1682 if (col->InheritsFrom(TList::Class()))
1683 lst = dynamic_cast<TList *>(col);
1684 else if (col->InheritsFrom(TMap::Class()))
1685 map = dynamic_cast<TMap *>(col);
1686 else if (col->InheritsFrom(TClonesArray::Class()))
1687 clones = dynamic_cast<TClonesArray *>(col);
1688
1689 nlohmann::json *json = Stack()->fNode;
1690
1691 std::string name = json->at("name");
1692 col->SetName(name.c_str());
1693
1694 nlohmann::json &arr = json->at("arr");
1695 int size = arr.size();
1696
1697 for (int n = 0; n < size; ++n) {
1698 nlohmann::json *subelem = &arr.at(n);
1699
1700 if (map)
1701 subelem = &subelem->at("first");
1702
1703 PushStack(0, subelem);
1704
1705 TClass *readClass = nullptr, *objClass = nullptr;
1706 void *subobj = nullptr;
1707
1708 if (clones) {
1709 if (n == 0) {
1710 if (!clones->GetClass() || (clones->GetSize() == 0)) {
1711 if (fTypeNameTag.Length() > 0) {
1712 clones->SetClass(subelem->at(fTypeNameTag.Data()).get<std::string>().c_str(), size);
1713 } else {
1714 Error("JsonReadCollection",
1715 "Cannot detect class name for TClonesArray - typename tag not configured");
1716 return;
1717 }
1718 } else if (size > clones->GetSize()) {
1719 Error("JsonReadCollection", "TClonesArray size %d smaller than required %d", clones->GetSize(), size);
1720 return;
1721 }
1722 }
1723 objClass = clones->GetClass();
1724 subobj = clones->ConstructedAt(n);
1725 }
1726
1727 subobj = JsonReadObject(subobj, objClass, &readClass);
1728
1729 PopStack();
1730
1731 if (clones)
1732 continue;
1733
1734 if (!subobj || !readClass) {
1735 subobj = nullptr;
1736 } else if (readClass->GetBaseClassOffset(TObject::Class()) != 0) {
1737 Error("JsonReadCollection", "Try to add object %s not derived from TObject", readClass->GetName());
1738 subobj = nullptr;
1739 }
1740
1741 TObject *tobj = static_cast<TObject *>(subobj);
1742
1743 if (map) {
1744 PushStack(0, &arr.at(n).at("second"));
1745
1746 readClass = nullptr;
1747 void *subobj2 = JsonReadObject(nullptr, nullptr, &readClass);
1748
1749 PopStack();
1750
1751 if (!subobj2 || !readClass) {
1752 subobj2 = nullptr;
1753 } else if (readClass->GetBaseClassOffset(TObject::Class()) != 0) {
1754 Error("JsonReadCollection", "Try to add object %s not derived from TObject", readClass->GetName());
1755 subobj2 = nullptr;
1756 }
1757
1758 map->Add(tobj, static_cast<TObject *>(subobj2));
1759 } else if (lst) {
1760 std::string opt = json->at("opt").at(n).get<std::string>();
1761 lst->Add(tobj, opt.c_str());
1762 } else {
1763 // generic method, all kinds of TCollection should work
1764 col->Add(tobj);
1765 }
1766 }
1767}
1768
1769////////////////////////////////////////////////////////////////////////////////
1770/// Read object from current JSON node
1771
1772void *TBufferJSON::JsonReadObject(void *obj, const TClass *objClass, TClass **readClass)
1773{
1774 if (readClass)
1775 *readClass = nullptr;
1776
1777 TJSONStackObj *stack = Stack();
1778
1779 Bool_t process_stl = stack->IsStl();
1780 nlohmann::json *json = stack->GetStlNode();
1781
1782 // check if null pointer
1783 if (json->is_null())
1784 return nullptr;
1785
1786 Int_t special_kind = JsonSpecialClass(objClass);
1787
1788 // Extract pointer
1789 if (json->is_object() && (json->size() == 1) && (json->find("$ref") != json->end())) {
1790 unsigned refid = json->at("$ref").get<unsigned>();
1791
1792 void *ref_obj = nullptr;
1793 TClass *ref_cl = nullptr;
1794
1795 GetMappedObject(refid + 1, ref_obj, ref_cl);
1796
1797 if (!ref_obj || !ref_cl) {
1798 Error("JsonReadObject", "Fail to find object for reference %u", refid);
1799 return nullptr;
1800 }
1801
1802 if (readClass)
1803 *readClass = ref_cl;
1804
1805 if (gDebug > 2)
1806 Info("JsonReadObject", "Extract object reference %u %p cl:%s expects:%s", refid, ref_obj, ref_cl->GetName(),
1807 (objClass ? objClass->GetName() : "---"));
1808
1809 return ref_obj;
1810 }
1811
1812 // special case of strings - they do not create JSON object, but just string
1813 if ((special_kind == json_stdstring) || (special_kind == json_TString)) {
1814 if (!obj)
1815 obj = objClass->New();
1816
1817 if (gDebug > 2)
1818 Info("JsonReadObject", "Read string from %s", json->dump().c_str());
1819
1820 if (special_kind == json_stdstring)
1821 *((std::string *)obj) = json->get<std::string>();
1822 else
1823 *((TString *)obj) = json->get<std::string>().c_str();
1824
1825 if (readClass)
1826 *readClass = const_cast<TClass *>(objClass);
1827
1828 return obj;
1829 }
1830
1831 Bool_t isBase = (stack->fElem && objClass) ? stack->fElem->IsBase() : kFALSE; // base class
1832
1833 if (isBase && (!obj || !objClass)) {
1834 Error("JsonReadObject", "No object when reading base class");
1835 return obj;
1836 }
1837
1838 Int_t map_convert = 0;
1839 if ((special_kind == TClassEdit::kMap) || (special_kind == TClassEdit::kMultiMap) ||
1840 (special_kind == TClassEdit::kUnorderedMap) || (special_kind == TClassEdit::kUnorderedMultiMap)) {
1841 map_convert = json->is_object() ? 2 : 1; // check if map was written as array or as object
1842 }
1843
1844 // from now all operations performed with sub-element,
1845 // stack should be repaired at the end
1846 if (process_stl)
1847 stack = PushStack(0, json);
1848
1849 TClass *jsonClass = nullptr;
1850 Int_t jsonClassVersion = 0;
1851
1852 if ((special_kind == json_TArray) || ((special_kind > 0) && (special_kind < ROOT::kSTLend))) {
1853
1854 jsonClass = const_cast<TClass *>(objClass);
1855
1856 if (!obj)
1857 obj = jsonClass->New();
1858
1859 Int_t len = stack->IsJsonArray(json, map_convert == 2 ? fTypeNameTag.Data() : nullptr);
1860
1861 stack->PushIntValue(len > 0 ? len : 0);
1862
1863 if (len < 0) // should never happens
1864 Error("JsonReadObject", "Not array when expecting such %s", json->dump().c_str());
1865
1866 if (gDebug > 1)
1867 Info("JsonReadObject", "Reading special kind %d %s ptr %p", special_kind, objClass->GetName(), obj);
1868
1869 } else if (isBase) {
1870 // base class has special handling - no additional level and no extra refid
1871
1872 jsonClass = const_cast<TClass *>(objClass);
1873
1874 if (gDebug > 1)
1875 Info("JsonReadObject", "Reading baseclass %s ptr %p", objClass->GetName(), obj);
1876 } else {
1877
1878 if ((fTypeNameTag.Length() > 0) && (json->count(fTypeNameTag.Data()) > 0)) {
1879 std::string clname = json->at(fTypeNameTag.Data()).get<std::string>();
1880 jsonClass = TClass::GetClass(clname.c_str());
1881 if (!jsonClass)
1882 Error("JsonReadObject", "Cannot find class %s", clname.c_str());
1883 } else {
1884 // try to use class which is assigned by streamers - better than nothing
1885 jsonClass = const_cast<TClass *>(objClass);
1886 }
1887
1888 if (!jsonClass) {
1889 if (process_stl)
1890 PopStack();
1891 return obj;
1892 }
1893
1894 if ((fTypeVersionTag.Length() > 0) && (json->count(fTypeVersionTag.Data()) > 0))
1895 jsonClassVersion = json->at(fTypeVersionTag.Data()).get<int>();
1896
1897 if (objClass && (jsonClass != objClass)) {
1898 if (obj || (jsonClass->GetBaseClassOffset(objClass) != 0)) {
1899 if (jsonClass->GetBaseClassOffset(objClass) < 0)
1900 Error("JsonReadObject", "Not possible to read %s and casting to %s pointer as the two classes are unrelated",
1901 jsonClass->GetName(), objClass->GetName());
1902 else
1903 Error("JsonReadObject", "Reading %s and casting to %s pointer is currently not supported",
1904 jsonClass->GetName(), objClass->GetName());
1905 if (process_stl)
1906 PopStack();
1907 return obj;
1908 }
1909 }
1910
1911 if (!obj)
1912 obj = jsonClass->New();
1913
1914 if (gDebug > 1)
1915 Info("JsonReadObject", "Reading object of class %s refid %u ptr %p", jsonClass->GetName(), fJsonrCnt, obj);
1916
1917 if (!special_kind)
1918 special_kind = JsonSpecialClass(jsonClass);
1919
1920 // add new element to the reading map
1921 MapObject(obj, jsonClass, ++fJsonrCnt);
1922 }
1923
1924 // there are two ways to handle custom streamers
1925 // either prepare data before streamer and tweak basic function which are reading values like UInt32_t
1926 // or try re-implement custom streamer here
1927
1928 if ((jsonClass == TObject::Class()) || (jsonClass == TRef::Class())) {
1929 // for TObject we re-implement custom streamer - it is much easier
1930
1932
1933 } else if (special_kind == json_TCollection) {
1934
1935 JsonReadCollection((TCollection *)obj, jsonClass);
1936
1937 } else {
1938
1939 Bool_t do_read = kTRUE;
1940
1941 // special handling of STL which coded into arrays
1942 if ((special_kind > 0) && (special_kind < ROOT::kSTLend))
1943 do_read = stack->AssignStl(jsonClass, map_convert, fTypeNameTag.Data());
1944
1945 // if provided - use class version from JSON
1946 stack->fClVersion = jsonClassVersion ? jsonClassVersion : jsonClass->GetClassVersion();
1947
1948 if (gDebug > 3)
1949 Info("JsonReadObject", "Calling streamer of class %s", jsonClass->GetName());
1950
1951 if (isBase && (special_kind == 0))
1952 Error("JsonReadObject", "Should not be used for reading of base class %s", jsonClass->GetName());
1953
1954 if (do_read)
1955 jsonClass->Streamer((void *)obj, *this);
1956
1957 stack->fClVersion = 0;
1958
1959 stack->ClearStl(); // reset STL index for itself to prevent looping
1960 }
1961
1962 // return back stack position
1963 if (process_stl)
1964 PopStack();
1965
1966 if (gDebug > 1)
1967 Info("JsonReadObject", "Reading object of class %s done", jsonClass->GetName());
1968
1969 if (readClass)
1970 *readClass = jsonClass;
1971
1972 return obj;
1973}
1974
1975////////////////////////////////////////////////////////////////////////////////
1976/// Read TObject data members from JSON.
1977/// Do not call TObject::Streamer() to avoid special tweaking of TBufferJSON interface
1978
1980{
1981 nlohmann::json *json = node ? (nlohmann::json *)node : Stack()->fNode;
1982
1983 UInt_t uid = json->at("fUniqueID").get<unsigned>();
1984 UInt_t bits = json->at("fBits").get<unsigned>();
1985 // UInt32_t pid = json->at("fPID").get<unsigned>(); // ignore PID for the moment
1986
1987 tobj->SetUniqueID(uid);
1988
1989 static auto tobj_fbits_offset = TObject::Class()->GetDataMemberOffset("fBits");
1990
1991 // there is no method to set all bits directly - do it differently
1992 if (tobj_fbits_offset > 0) {
1993 UInt_t *fbits = (UInt_t *) ((char* ) tobj + tobj_fbits_offset);
1994 *fbits = (*fbits & (TObject::kIsOnHeap | TObject::kNotDeleted)) | bits;
1995 }
1996}
1997
1998////////////////////////////////////////////////////////////////////////////////
1999/// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2000/// and indent new level in json structure.
2001/// This call indicates, that TStreamerInfo functions starts streaming
2002/// object data of correspondent class
2003
2005{
2006 if (gDebug > 2)
2007 Info("IncrementLevel", "Class: %s", (info ? info->GetClass()->GetName() : "custom"));
2008
2010}
2011
2012////////////////////////////////////////////////////////////////////////////////
2013/// Prepares buffer to stream data of specified class
2014
2016{
2017 if (sinfo)
2018 cl = sinfo->GetClass();
2019
2020 if (!cl)
2021 return;
2022
2023 if (gDebug > 3)
2024 Info("WorkWithClass", "Class: %s", cl->GetName());
2025
2026 TJSONStackObj *stack = Stack();
2027
2028 if (IsReading()) {
2029 stack = PushStack(0, stack->fNode);
2030 } else if (stack && stack->IsStreamerElement() && !stack->fIsObjStarted &&
2031 ((stack->fElem->GetType() == TStreamerInfo::kObject) ||
2032 (stack->fElem->GetType() == TStreamerInfo::kAny))) {
2033
2034 stack->fIsObjStarted = kTRUE;
2035
2036 fJsonrCnt++; // count object, but do not keep reference
2037
2038 stack = JsonStartObjectWrite(cl, sinfo);
2039 } else {
2040 stack = PushStack(0);
2041 }
2042
2043 stack->fInfo = sinfo;
2044 stack->fIsStreamerInfo = kTRUE;
2045}
2046
2047////////////////////////////////////////////////////////////////////////////////
2048/// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2049/// and decrease level in json structure.
2050
2052{
2053 if (gDebug > 2)
2054 Info("DecrementLevel", "Class: %s", (info ? info->GetClass()->GetName() : "custom"));
2055
2056 TJSONStackObj *stack = Stack();
2057
2058 if (stack->IsStreamerElement()) {
2059
2060 if (IsWriting()) {
2061 if (gDebug > 3)
2062 Info("DecrementLevel", " Perform post-processing elem: %s", stack->fElem->GetName());
2063
2064 PerformPostProcessing(stack);
2065 }
2066
2067 stack = PopStack(); // remove stack of last element
2068 }
2069
2070 if (stack->fInfo != (TStreamerInfo *)info)
2071 Error("DecrementLevel", " Mismatch of streamer info");
2072
2073 PopStack(); // back from data of stack info
2074
2075 if (gDebug > 3)
2076 Info("DecrementLevel", "Class: %s done", (info ? info->GetClass()->GetName() : "custom"));
2077}
2078
2079////////////////////////////////////////////////////////////////////////////////
2080/// Return current streamer info element
2081
2083{
2084 return Stack()->fInfo;
2085}
2086
2087////////////////////////////////////////////////////////////////////////////////
2088/// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2089/// and add/verify next element of json structure
2090/// This calls allows separate data, correspondent to one class member, from another
2091
2093{
2094 if (gDebug > 3)
2095 Info("SetStreamerElementNumber", "Element name %s", elem->GetName());
2096
2097 WorkWithElement(elem, comp_type);
2098}
2099
2100////////////////////////////////////////////////////////////////////////////////
2101/// This is call-back from streamer which indicates
2102/// that class member will be streamed
2103/// Name of element used in JSON
2104
2106{
2107 TJSONStackObj *stack = Stack();
2108 if (!stack) {
2109 Error("WorkWithElement", "stack is empty");
2110 return;
2111 }
2112
2113 if (gDebug > 0)
2114 Info("WorkWithElement", " Start element %s type %d typename %s", elem ? elem->GetName() : "---",
2115 elem ? elem->GetType() : -1, elem ? elem->GetTypeName() : "---");
2116
2117 if (stack->IsStreamerElement()) {
2118 // this is post processing
2119
2120 if (IsWriting()) {
2121 if (gDebug > 3)
2122 Info("WorkWithElement", " Perform post-processing elem: %s", stack->fElem->GetName());
2123 PerformPostProcessing(stack);
2124 }
2125
2126 stack = PopStack(); // go level back
2127 }
2128
2129 fValue.Clear();
2130
2131 if (!stack) {
2132 Error("WorkWithElement", "Lost of stack");
2133 return;
2134 }
2135
2136 TStreamerInfo *info = stack->fInfo;
2137 if (!stack->IsStreamerInfo()) {
2138 Error("WorkWithElement", "Problem in Inc/Dec level");
2139 return;
2140 }
2141
2142 Int_t number = info ? info->GetElements()->IndexOf(elem) : -1;
2143
2144 if (!elem) {
2145 Error("WorkWithElement", "streamer info returns elem = nullptr");
2146 return;
2147 }
2148
2149 TClass *base_class = elem->IsBase() ? elem->GetClassPointer() : nullptr;
2150
2151 stack = PushStack(0, stack->fNode);
2152 stack->fElem = elem;
2153 stack->fIsElemOwner = (number < 0);
2154
2155 JsonStartElement(elem, base_class);
2156
2157 if (base_class && IsReading())
2158 stack->fClVersion = base_class->GetClassVersion();
2159
2160 if ((elem->GetType() == TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop) && (elem->GetArrayDim() > 0)) {
2161 // array of array, start handling here
2162 stack->fIndx = std::make_unique<TArrayIndexProducer>(elem, -1, fArraySepar.Data());
2163 if (IsWriting())
2164 AppendOutput(stack->fIndx->GetBegin());
2165 }
2166
2167 if (IsReading() && (elem->GetType() > TStreamerInfo::kOffsetP) && (elem->GetType() < TStreamerInfo::kOffsetP + 20)) {
2168 // reading of such array begins with reading of single Char_t value
2169 // it indicates if array should be read or not
2170 stack->PushIntValue(stack->IsJsonString() || (stack->IsJsonArray() > 0) ? 1 : 0);
2171 }
2172}
2173
2174////////////////////////////////////////////////////////////////////////////////
2175/// Should be called in the beginning of custom class streamer.
2176/// Informs buffer data about class which will be streamed now.
2177///
2178/// ClassBegin(), ClassEnd() and ClassMember() should be used in
2179/// custom class streamers to specify which kind of data are
2180/// now streamed. Such information is used to correctly
2181/// convert class data to JSON. Without that functions calls
2182/// classes with custom streamers cannot be used with TBufferJSON
2183
2185{
2186 WorkWithClass(nullptr, cl);
2187}
2188
2189////////////////////////////////////////////////////////////////////////////////
2190/// Should be called at the end of custom streamer
2191/// See TBufferJSON::ClassBegin for more details
2192
2194{
2195 DecrementLevel(0);
2196}
2197
2198////////////////////////////////////////////////////////////////////////////////
2199/// Method indicates name and typename of class member,
2200/// which should be now streamed in custom streamer
2201/// Following combinations are supported:
2202/// 1. name = "ClassName", typeName = 0 or typename==ClassName
2203/// This is a case, when data of parent class "ClassName" should be streamed.
2204/// For instance, if class directly inherited from TObject, custom
2205/// streamer should include following code:
2206/// ~~~{.cpp}
2207/// b.ClassMember("TObject");
2208/// TObject::Streamer(b);
2209/// ~~~
2210/// 2. Basic data type
2211/// ~~~{.cpp}
2212/// b.ClassMember("fInt","Int_t");
2213/// b >> fInt;
2214/// ~~~
2215/// 3. Array of basic data types
2216/// ~~~{.cpp}
2217/// b.ClassMember("fArr","Int_t", 5);
2218/// b.ReadFastArray(fArr, 5);
2219/// ~~~
2220/// 4. Object as data member
2221/// ~~~{.cpp}
2222/// b.ClassMember("fName","TString");
2223/// fName.Streamer(b);
2224/// ~~~
2225/// 5. Pointer on object as data member
2226/// ~~~{.cpp}
2227/// b.ClassMember("fObj","TObject*");
2228/// b.StreamObject(fObj);
2229/// ~~~
2230///
2231/// arrsize1 and arrsize2 arguments (when specified) indicate first and
2232/// second dimension of array. Can be used for array of basic types.
2233/// See ClassBegin() method for more details.
2234
2235void TBufferJSON::ClassMember(const char *name, const char *typeName, Int_t arrsize1, Int_t arrsize2)
2236{
2237 if (!typeName)
2238 typeName = name;
2239
2240 if (!name || (strlen(name) == 0)) {
2241 Error("ClassMember", "Invalid member name");
2242 return;
2243 }
2244
2245 TString tname = typeName;
2246
2247 Int_t typ_id = -1;
2248
2249 if (strcmp(typeName, "raw:data") == 0)
2250 typ_id = TStreamerInfo::kMissing;
2251
2252 if (typ_id < 0) {
2253 TDataType *dt = gROOT->GetType(typeName);
2254 if (dt && (dt->GetType() > 0) && (dt->GetType() < 20))
2255 typ_id = dt->GetType();
2256 }
2257
2258 if (typ_id < 0)
2259 if (strcmp(name, typeName) == 0) {
2260 TClass *cl = TClass::GetClass(tname.Data());
2261 if (cl)
2262 typ_id = TStreamerInfo::kBase;
2263 }
2264
2265 if (typ_id < 0) {
2266 Bool_t isptr = kFALSE;
2267 if (tname[tname.Length() - 1] == '*') {
2268 tname.Resize(tname.Length() - 1);
2269 isptr = kTRUE;
2270 }
2271 TClass *cl = TClass::GetClass(tname.Data());
2272 if (!cl) {
2273 Error("ClassMember", "Invalid class specifier %s", typeName);
2274 return;
2275 }
2276
2277 if (cl->IsTObject())
2279 else
2280 typ_id = isptr ? TStreamerInfo::kAnyp : TStreamerInfo::kAny;
2281
2282 if ((cl == TString::Class()) && !isptr)
2283 typ_id = TStreamerInfo::kTString;
2284 }
2285
2286 TStreamerElement *elem = nullptr;
2287
2288 if (typ_id == TStreamerInfo::kMissing) {
2289 elem = new TStreamerElement(name, "title", 0, typ_id, "raw:data");
2290 } else if (typ_id == TStreamerInfo::kBase) {
2291 TClass *cl = TClass::GetClass(tname.Data());
2292 if (cl) {
2293 TStreamerBase *b = new TStreamerBase(tname.Data(), "title", 0);
2294 b->SetBaseVersion(cl->GetClassVersion());
2295 elem = b;
2296 }
2297 } else if ((typ_id > 0) && (typ_id < 20)) {
2298 elem = new TStreamerBasicType(name, "title", 0, typ_id, typeName);
2299 } else if ((typ_id == TStreamerInfo::kObject) || (typ_id == TStreamerInfo::kTObject) ||
2300 (typ_id == TStreamerInfo::kTNamed)) {
2301 elem = new TStreamerObject(name, "title", 0, tname.Data());
2302 } else if (typ_id == TStreamerInfo::kObjectp) {
2303 elem = new TStreamerObjectPointer(name, "title", 0, tname.Data());
2304 } else if (typ_id == TStreamerInfo::kAny) {
2305 elem = new TStreamerObjectAny(name, "title", 0, tname.Data());
2306 } else if (typ_id == TStreamerInfo::kAnyp) {
2307 elem = new TStreamerObjectAnyPointer(name, "title", 0, tname.Data());
2308 } else if (typ_id == TStreamerInfo::kTString) {
2309 elem = new TStreamerString(name, "title", 0);
2310 }
2311
2312 if (!elem) {
2313 Error("ClassMember", "Invalid combination name = %s type = %s", name, typeName);
2314 return;
2315 }
2316
2317 if (arrsize1 > 0) {
2318 elem->SetArrayDim(arrsize2 > 0 ? 2 : 1);
2319 elem->SetMaxIndex(0, arrsize1);
2320 if (arrsize2 > 0)
2321 elem->SetMaxIndex(1, arrsize2);
2322 }
2323
2324 // we indicate that there is no streamerinfo
2325 WorkWithElement(elem, -1);
2326}
2327
2328////////////////////////////////////////////////////////////////////////////////
2329/// Function is converts TObject and TString structures to more compact representation
2330
2332{
2333 if (stack->fIsPostProcessed)
2334 return;
2335
2336 const TStreamerElement *elem = stack->fElem;
2337
2338 if (!elem && !obj_cl)
2339 return;
2340
2341 stack->fIsPostProcessed = kTRUE;
2342
2343 // when element was written as separate object, close only braces and exit
2344 if (stack->fIsObjStarted) {
2345 AppendOutput("", "}");
2346 return;
2347 }
2348
2349 Bool_t isTObject(kFALSE), isTRef(kFALSE), isTString(kFALSE), isSTLstring(kFALSE), isOffsetPArray(kFALSE),
2350 isTArray(kFALSE);
2351
2352 if (obj_cl) {
2353 if (obj_cl == TObject::Class())
2354 isTObject = kTRUE;
2355 else if (obj_cl == TRef::Class())
2356 isTRef = kTRUE;
2357 else
2358 return;
2359 } else {
2360 const char *typname = elem->IsBase() ? elem->GetName() : elem->GetTypeName();
2361 isTObject = (elem->GetType() == TStreamerInfo::kTObject) || (strcmp("TObject", typname) == 0);
2362 isTString = elem->GetType() == TStreamerInfo::kTString;
2363 isSTLstring = elem->GetType() == TStreamerInfo::kSTLstring;
2364 isOffsetPArray = (elem->GetType() > TStreamerInfo::kOffsetP) && (elem->GetType() < TStreamerInfo::kOffsetP + 20);
2365 isTArray = (strncmp("TArray", typname, 6) == 0);
2366 }
2367
2368 if (isTString || isSTLstring) {
2369 // just remove all kind of string length information
2370
2371 if (gDebug > 3)
2372 Info("PerformPostProcessing", "reformat string value = '%s'", fValue.Data());
2373
2374 stack->fValues.clear();
2375 } else if (isOffsetPArray) {
2376 // basic array with [fN] comment
2377
2378 if (stack->fValues.empty() && (fValue == "0")) {
2379 fValue = "[]";
2380 } else if ((stack->fValues.size() == 1) && (stack->fValues[0] == "1")) {
2381 stack->fValues.clear();
2382 } else {
2383 Error("PerformPostProcessing", "Wrong values for kOffsetP element %s", (elem ? elem->GetName() : "---"));
2384 stack->fValues.clear();
2385 fValue = "[]";
2386 }
2387 } else if (isTObject || isTRef) {
2388 // complex workaround for TObject/TRef streamer
2389 // would be nice if other solution can be found
2390 // Here is not supported TRef on TRef (double reference)
2391
2392 Int_t cnt = stack->fValues.size();
2393 if (fValue.Length() > 0)
2394 cnt++;
2395
2396 if (cnt < 2 || cnt > 3) {
2397 if (gDebug > 0)
2398 Error("PerformPostProcessing", "When storing TObject/TRef, strange number of items %d", cnt);
2399 AppendOutput(stack->NextMemberSeparator(), "\"dummy\"");
2401 } else {
2402 AppendOutput(stack->NextMemberSeparator(), "\"fUniqueID\"");
2404 AppendOutput(stack->fValues[0].c_str());
2405 AppendOutput(stack->NextMemberSeparator(), "\"fBits\"");
2407 auto tbits = std::atol((stack->fValues.size() > 1) ? stack->fValues[1].c_str() : fValue.Data());
2408 AppendOutput(std::to_string(tbits & ~TObject::kNotDeleted & ~TObject::kIsOnHeap).c_str());
2409 if (cnt == 3) {
2410 AppendOutput(stack->NextMemberSeparator(), "\"fPID\"");
2412 AppendOutput((stack->fValues.size() > 2) ? stack->fValues[2].c_str() : fValue.Data());
2413 }
2414
2415 stack->fValues.clear();
2416 fValue.Clear();
2417 return;
2418 }
2419
2420 } else if (isTArray) {
2421 // for TArray one deletes complete stack
2422 stack->fValues.clear();
2423 }
2424
2425 if (elem && elem->IsBase() && (fValue.Length() == 0)) {
2426 // here base class data already completely stored
2427 return;
2428 }
2429
2430 if (!stack->fValues.empty()) {
2431 // append element blob data just as abstract array, user is responsible to decode it
2432 AppendOutput("[");
2433 for (auto &blob: stack->fValues) {
2434 AppendOutput(blob.c_str());
2436 }
2437 }
2438
2439 if (fValue.Length() == 0) {
2440 AppendOutput("null");
2441 } else {
2443 fValue.Clear();
2444 }
2445
2446 if (!stack->fValues.empty())
2447 AppendOutput("]");
2448}
2449
2450////////////////////////////////////////////////////////////////////////////////
2451/// suppressed function of TBuffer
2452
2454{
2455 return nullptr;
2456}
2457
2458////////////////////////////////////////////////////////////////////////////////
2459/// suppressed function of TBuffer
2460
2462
2463////////////////////////////////////////////////////////////////////////////////
2464/// read version value from buffer
2465
2467{
2468 Version_t res = cl ? cl->GetClassVersion() : 0;
2469
2470 if (start)
2471 *start = 0;
2472 if (bcnt)
2473 *bcnt = 0;
2474
2475 if (!cl && Stack()->fClVersion) {
2476 res = Stack()->fClVersion;
2477 Stack()->fClVersion = 0;
2478 }
2479
2480 if (gDebug > 3)
2481 Info("ReadVersion", "Result: %d Class: %s", res, (cl ? cl->GetName() : "---"));
2482
2483 return res;
2484}
2485
2486////////////////////////////////////////////////////////////////////////////////
2487/// Ignored in TBufferJSON
2488
2489UInt_t TBufferJSON::WriteVersion(const TClass * /*cl*/, Bool_t /* useBcnt */)
2490{
2491 return 0;
2492}
2493
2494////////////////////////////////////////////////////////////////////////////////
2495/// Read object from buffer. Only used from TBuffer
2496
2497void *TBufferJSON::ReadObjectAny(const TClass *expectedClass)
2498{
2499 if (gDebug > 2)
2500 Info("ReadObjectAny", "From current JSON node");
2501 void *res = JsonReadObject(nullptr, expectedClass);
2502 return res;
2503}
2504
2505////////////////////////////////////////////////////////////////////////////////
2506/// Skip any kind of object from buffer
2507
2509
2510////////////////////////////////////////////////////////////////////////////////
2511/// Write object to buffer. Only used from TBuffer
2512
2513void TBufferJSON::WriteObjectClass(const void *actualObjStart, const TClass *actualClass, Bool_t cacheReuse)
2514{
2515 if (gDebug > 3)
2516 Info("WriteObjectClass", "Class %s", (actualClass ? actualClass->GetName() : " null"));
2517
2518 JsonWriteObject(actualObjStart, actualClass, cacheReuse);
2519}
2520
2521////////////////////////////////////////////////////////////////////////////////
2522/// If value exists, push in the current stack for post-processing
2523
2525{
2526 if (fValue.Length() > 0)
2528}
2529
2530////////////////////////////////////////////////////////////////////////////////
2531/// Read array of Bool_t from buffer
2532
2534{
2535 return JsonReadArray(b);
2536}
2537
2538////////////////////////////////////////////////////////////////////////////////
2539/// Read array of Char_t from buffer
2540
2542{
2543 return JsonReadArray(c);
2544}
2545
2546////////////////////////////////////////////////////////////////////////////////
2547/// Read array of UChar_t from buffer
2548
2550{
2551 return JsonReadArray(c);
2552}
2553
2554////////////////////////////////////////////////////////////////////////////////
2555/// Read array of Short_t from buffer
2556
2558{
2559 return JsonReadArray(h);
2560}
2561
2562////////////////////////////////////////////////////////////////////////////////
2563/// Read array of UShort_t from buffer
2564
2566{
2567 return JsonReadArray(h);
2568}
2569
2570////////////////////////////////////////////////////////////////////////////////
2571/// Read array of Int_t from buffer
2572
2574{
2575 return JsonReadArray(i);
2576}
2577
2578////////////////////////////////////////////////////////////////////////////////
2579/// Read array of UInt_t from buffer
2580
2582{
2583 return JsonReadArray(i);
2584}
2585
2586////////////////////////////////////////////////////////////////////////////////
2587/// Read array of Long_t from buffer
2588
2590{
2591 return JsonReadArray(l);
2592}
2593
2594////////////////////////////////////////////////////////////////////////////////
2595/// Read array of ULong_t from buffer
2596
2598{
2599 return JsonReadArray(l);
2600}
2601
2602////////////////////////////////////////////////////////////////////////////////
2603/// Read array of Long64_t from buffer
2604
2606{
2607 return JsonReadArray(l);
2608}
2609
2610////////////////////////////////////////////////////////////////////////////////
2611/// Read array of ULong64_t from buffer
2612
2614{
2615 return JsonReadArray(l);
2616}
2617
2618////////////////////////////////////////////////////////////////////////////////
2619/// Read array of Float_t from buffer
2620
2622{
2623 return JsonReadArray(f);
2624}
2625
2626////////////////////////////////////////////////////////////////////////////////
2627/// Read array of Double_t from buffer
2628
2630{
2631 return JsonReadArray(d);
2632}
2633
2634////////////////////////////////////////////////////////////////////////////////
2635/// Read static array from JSON - not used
2636
2637template <typename T>
2639{
2640 Info("ReadArray", "Not implemented");
2641 return value ? 1 : 0;
2642}
2643
2644////////////////////////////////////////////////////////////////////////////////
2645/// Read array of Bool_t from buffer
2646
2648{
2649 return JsonReadArray(b);
2650}
2651
2652////////////////////////////////////////////////////////////////////////////////
2653/// Read array of Char_t from buffer
2654
2656{
2657 return JsonReadArray(c);
2658}
2659
2660////////////////////////////////////////////////////////////////////////////////
2661/// Read array of UChar_t from buffer
2662
2664{
2665 return JsonReadArray(c);
2666}
2667
2668////////////////////////////////////////////////////////////////////////////////
2669/// Read array of Short_t from buffer
2670
2672{
2673 return JsonReadArray(h);
2674}
2675
2676////////////////////////////////////////////////////////////////////////////////
2677/// Read array of UShort_t from buffer
2678
2680{
2681 return JsonReadArray(h);
2682}
2683
2684////////////////////////////////////////////////////////////////////////////////
2685/// Read array of Int_t from buffer
2686
2688{
2689 return JsonReadArray(i);
2690}
2691
2692////////////////////////////////////////////////////////////////////////////////
2693/// Read array of UInt_t from buffer
2694
2696{
2697 return JsonReadArray(i);
2698}
2699
2700////////////////////////////////////////////////////////////////////////////////
2701/// Read array of Long_t from buffer
2702
2704{
2705 return JsonReadArray(l);
2706}
2707
2708////////////////////////////////////////////////////////////////////////////////
2709/// Read array of ULong_t from buffer
2710
2712{
2713 return JsonReadArray(l);
2714}
2715
2716////////////////////////////////////////////////////////////////////////////////
2717/// Read array of Long64_t from buffer
2718
2720{
2721 return JsonReadArray(l);
2722}
2723
2724////////////////////////////////////////////////////////////////////////////////
2725/// Read array of ULong64_t from buffer
2726
2728{
2729 return JsonReadArray(l);
2730}
2731
2732////////////////////////////////////////////////////////////////////////////////
2733/// Read array of Float_t from buffer
2734
2736{
2737 return JsonReadArray(f);
2738}
2739
2740////////////////////////////////////////////////////////////////////////////////
2741/// Read array of Double_t from buffer
2742
2744{
2745 return JsonReadArray(d);
2746}
2747
2748////////////////////////////////////////////////////////////////////////////////
2749/// Template method to read array from the JSON
2750
2751template <typename T>
2752void TBufferJSON::JsonReadFastArray(T *arr, Int_t arrsize, bool asstring)
2753{
2754 if (!arr || (arrsize <= 0))
2755 return;
2756 nlohmann::json *json = Stack()->fNode;
2757 if (gDebug > 2)
2758 Info("ReadFastArray", "Reading array sz %d from JSON %s", arrsize, json->dump().substr(0, 30).c_str());
2759 auto indexes = Stack()->MakeReadIndexes();
2760 if (indexes) { /* at least two dims */
2761 TArrayI &indx = indexes->GetIndices();
2762 Int_t lastdim = indx.GetSize() - 1;
2763 if (indexes->TotalLength() != arrsize)
2764 Error("ReadFastArray", "Mismatch %d-dim array sizes %d %d", lastdim + 1, arrsize, (int)indexes->TotalLength());
2765 for (int cnt = 0; cnt < arrsize; ++cnt) {
2766 nlohmann::json *elem = &(json->at(indx[0]));
2767 for (int k = 1; k < lastdim; ++k)
2768 elem = &((*elem)[indx[k]]);
2769 arr[cnt] = (asstring && elem->is_string()) ? elem->get<std::string>()[indx[lastdim]] : (*elem)[indx[lastdim]].get<T>();
2770 indexes->NextSeparator();
2771 }
2772 } else if (asstring && json->is_string()) {
2773 std::string str = json->get<std::string>();
2774 for (int cnt = 0; cnt < arrsize; ++cnt)
2775 arr[cnt] = (cnt < (int)str.length()) ? str[cnt] : 0;
2776 } else if (json->is_object() && (json->count("$arr") == 1)) {
2777 if (json->at("len").get<int>() != arrsize)
2778 Error("ReadFastArray", "Mismatch compressed array size %d %d", arrsize, json->at("len").get<int>());
2779
2780 for (int cnt = 0; cnt < arrsize; ++cnt)
2781 arr[cnt] = 0;
2782
2783 if (json->count("b") == 1) {
2784 auto base64 = json->at("b").get<std::string>();
2785
2786 int offset = (json->count("o") == 1) ? json->at("o").get<int>() : 0;
2787
2788 // TODO: provide TBase64::Decode with direct write into target buffer
2789 auto decode = TBase64::Decode(base64.c_str());
2790
2791 if (arrsize * (long) sizeof(T) < (offset + decode.Length())) {
2792 Error("ReadFastArray", "Base64 data %ld larger than target array size %ld", (long) decode.Length() + offset, (long) (arrsize*sizeof(T)));
2793 } else if ((sizeof(T) > 1) && (decode.Length() % sizeof(T) != 0)) {
2794 Error("ReadFastArray", "Base64 data size %ld not matches with element size %ld", (long) decode.Length(), (long) sizeof(T));
2795 } else {
2796 memcpy((char *) arr + offset, decode.Data(), decode.Length());
2797 }
2798 return;
2799 }
2800
2801 int p = 0, id = 0;
2802 std::string idname = "", pname, vname, nname;
2803 while (p < arrsize) {
2804 pname = std::string("p") + idname;
2805 if (json->count(pname) == 1)
2806 p = json->at(pname).get<int>();
2807 vname = std::string("v") + idname;
2808 if (json->count(vname) != 1)
2809 break;
2810 nlohmann::json &v = json->at(vname);
2811 if (v.is_array()) {
2812 for (unsigned sub = 0; sub < v.size(); ++sub)
2813 arr[p++] = v[sub].get<T>();
2814 } else {
2815 nname = std::string("n") + idname;
2816 unsigned ncopy = (json->count(nname) == 1) ? json->at(nname).get<unsigned>() : 1;
2817 for (unsigned sub = 0; sub < ncopy; ++sub)
2818 arr[p++] = v.get<T>();
2819 }
2820 idname = std::to_string(++id);
2821 }
2822 } else {
2823 if ((int)json->size() != arrsize)
2824 Error("ReadFastArray", "Mismatch array sizes %d %d", arrsize, (int)json->size());
2825 for (int cnt = 0; cnt < arrsize; ++cnt)
2826 arr[cnt] = json->at(cnt).get<T>();
2827 }
2828}
2829
2830////////////////////////////////////////////////////////////////////////////////
2831/// read array of Bool_t from buffer
2832
2834{
2836}
2837
2838////////////////////////////////////////////////////////////////////////////////
2839/// read array of Char_t from buffer
2840
2842{
2843 JsonReadFastArray(c, n, true);
2844}
2845
2846////////////////////////////////////////////////////////////////////////////////
2847/// read array of Char_t from buffer
2848
2850{
2851 JsonReadFastArray(c, n, true);
2852}
2853
2854////////////////////////////////////////////////////////////////////////////////
2855/// read array of UChar_t from buffer
2856
2858{
2860}
2861
2862////////////////////////////////////////////////////////////////////////////////
2863/// read array of Short_t from buffer
2864
2866{
2868}
2869
2870////////////////////////////////////////////////////////////////////////////////
2871/// read array of UShort_t from buffer
2872
2874{
2876}
2877
2878////////////////////////////////////////////////////////////////////////////////
2879/// read array of Int_t from buffer
2880
2882{
2883 JsonReadFastArray(i, n);
2884}
2885
2886////////////////////////////////////////////////////////////////////////////////
2887/// read array of UInt_t from buffer
2888
2890{
2891 JsonReadFastArray(i, n);
2892}
2893
2894////////////////////////////////////////////////////////////////////////////////
2895/// read array of Long_t from buffer
2896
2898{
2900}
2901
2902////////////////////////////////////////////////////////////////////////////////
2903/// read array of ULong_t from buffer
2904
2906{
2908}
2909
2910////////////////////////////////////////////////////////////////////////////////
2911/// read array of Long64_t from buffer
2912
2914{
2916}
2917
2918////////////////////////////////////////////////////////////////////////////////
2919/// read array of ULong64_t from buffer
2920
2922{
2924}
2925
2926////////////////////////////////////////////////////////////////////////////////
2927/// read array of Float_t from buffer
2928
2930{
2932}
2933
2934////////////////////////////////////////////////////////////////////////////////
2935/// read array of Double_t from buffer
2936
2938{
2940}
2941
2942////////////////////////////////////////////////////////////////////////////////
2943/// Read an array of 'n' objects from the I/O buffer.
2944/// Stores the objects read starting at the address 'start'.
2945/// The objects in the array are assume to be of class 'cl'.
2946/// Copied code from TBufferFile
2947
2948void TBufferJSON::ReadFastArray(void *start, const TClass *cl, Int_t n, TMemberStreamer * /* streamer */,
2949 const TClass * /* onFileClass */)
2950{
2951 if (gDebug > 1)
2952 Info("ReadFastArray", "void* n:%d cl:%s", n, cl->GetName());
2953
2954 // if (streamer) {
2955 // Info("ReadFastArray", "(void*) Calling streamer - not handled correctly");
2956 // streamer->SetOnFileClass(onFileClass);
2957 // (*streamer)(*this, start, 0);
2958 // return;
2959 // }
2960
2961 int objectSize = cl->Size();
2962 char *obj = (char *)start;
2963
2964 TJSONStackObj *stack = Stack();
2965 nlohmann::json *topnode = stack->fNode, *subnode = topnode;
2966 if (stack->fIndx)
2967 subnode = stack->fIndx->ExtractNode(topnode);
2968
2969 TArrayIndexProducer indexes(stack->fElem, n, "");
2970
2971 if (gDebug > 1)
2972 Info("ReadFastArray", "Indexes ndim:%d totallen:%d", indexes.NumDimensions(), indexes.TotalLength());
2973
2974 for (Int_t j = 0; j < n; j++, obj += objectSize) {
2975
2976 stack->fNode = indexes.ExtractNode(subnode);
2977
2978 JsonReadObject(obj, cl);
2979 }
2980
2981 // restore top node - show we use stack here?
2982 stack->fNode = topnode;
2983}
2984
2985////////////////////////////////////////////////////////////////////////////////
2986/// redefined here to avoid warning message from gcc
2987
2988void TBufferJSON::ReadFastArray(void **start, const TClass *cl, Int_t n, Bool_t isPreAlloc,
2989 TMemberStreamer * /* streamer */, const TClass * /* onFileClass */)
2990{
2991 if (gDebug > 1)
2992 Info("ReadFastArray", "void** n:%d cl:%s prealloc:%s", n, cl->GetName(), (isPreAlloc ? "true" : "false"));
2993
2994 // if (streamer) {
2995 // Info("ReadFastArray", "(void**) Calling streamer - not handled correctly");
2996 // if (isPreAlloc) {
2997 // for (Int_t j = 0; j < n; j++) {
2998 // if (!start[j])
2999 // start[j] = cl->New();
3000 // }
3001 // }
3002 // streamer->SetOnFileClass(onFileClass);
3003 // (*streamer)(*this, (void *)start, 0);
3004 // return;
3005 // }
3006
3007 TJSONStackObj *stack = Stack();
3008 nlohmann::json *topnode = stack->fNode, *subnode = topnode;
3009 if (stack->fIndx)
3010 subnode = stack->fIndx->ExtractNode(topnode);
3011
3012 TArrayIndexProducer indexes(stack->fElem, n, "");
3013
3014 for (Int_t j = 0; j < n; j++) {
3015
3016 stack->fNode = indexes.ExtractNode(subnode);
3017
3018 if (!isPreAlloc) {
3019 void *old = start[j];
3020 start[j] = JsonReadObject(nullptr, cl);
3021 if (old && old != start[j] && TStreamerInfo::CanDelete())
3022 (const_cast<TClass *>(cl))->Destructor(old, kFALSE); // call delete and destruct
3023 } else {
3024 if (!start[j])
3025 start[j] = (const_cast<TClass *>(cl))->New();
3026 JsonReadObject(start[j], cl);
3027 }
3028 }
3029
3030 stack->fNode = topnode;
3031}
3032
3033template <typename T>
3034void TBufferJSON::JsonWriteArrayCompress(const T *vname, Int_t arrsize, const char *typname)
3035{
3036 bool is_base64 = Stack()->fBase64 || (fArrayCompact == kBase64);
3037
3038 if (!is_base64 && ((fArrayCompact == 0) || (arrsize < 6))) {
3039 fValue.Append("[");
3040 for (Int_t indx = 0; indx < arrsize; indx++) {
3041 if (indx > 0)
3043 JsonWriteBasic(vname[indx]);
3044 }
3045 fValue.Append("]");
3046 } else if (is_base64 && !arrsize) {
3047 fValue.Append("[]");
3048 } else {
3049 fValue.Append("{");
3050 fValue.Append(TString::Format("\"$arr\":\"%s\"%s\"len\":%d", typname, fArraySepar.Data(), arrsize));
3051 Int_t aindx(0), bindx(arrsize);
3052 while ((aindx < arrsize) && (vname[aindx] == 0))
3053 aindx++;
3054 while ((aindx < bindx) && (vname[bindx - 1] == 0))
3055 bindx--;
3056
3057 if (is_base64) {
3058 // small initial offset makes no sense - JSON code is large then size gain
3059 if ((aindx * sizeof(T) < 5) && (aindx < bindx))
3060 aindx = 0;
3061
3062 if ((aindx > 0) && (aindx < bindx))
3063 fValue.Append(TString::Format("%s\"o\":%ld", fArraySepar.Data(), (long) (aindx * (int) sizeof(T))));
3064
3066 fValue.Append("\"b\":\"");
3067
3068 if (aindx < bindx)
3069 fValue.Append(TBase64::Encode((const char *) (vname + aindx), (bindx - aindx) * sizeof(T)));
3070
3071 fValue.Append("\"");
3072 } else if (aindx < bindx) {
3073 TString suffix("");
3074 Int_t p(aindx), suffixcnt(-1), lastp(0);
3075 while (p < bindx) {
3076 if (vname[p] == 0) {
3077 p++;
3078 continue;
3079 }
3080 Int_t p0(p++), pp(0), nsame(1);
3082 pp = bindx;
3083 p = bindx + 1;
3084 nsame = 0;
3085 }
3086 for (; p <= bindx; ++p) {
3087 if ((p < bindx) && (vname[p] == vname[p - 1])) {
3088 nsame++;
3089 continue;
3090 }
3091 if (vname[p - 1] == 0) {
3092 if (nsame > 9) {
3093 nsame = 0;
3094 break;
3095 }
3096 } else if (nsame > 5) {
3097 if (pp) {
3098 p = pp;
3099 nsame = 0;
3100 } else
3101 pp = p;
3102 break;
3103 }
3104 pp = p;
3105 nsame = 1;
3106 }
3107 if (pp <= p0)
3108 continue;
3109 if (++suffixcnt > 0)
3110 suffix.Form("%d", suffixcnt);
3111 if (p0 != lastp)
3112 fValue.Append(TString::Format("%s\"p%s\":%d", fArraySepar.Data(), suffix.Data(), p0));
3113 lastp = pp; /* remember cursor, it may be the same */
3114 fValue.Append(TString::Format("%s\"v%s\":", fArraySepar.Data(), suffix.Data()));
3115 if ((nsame > 1) || (pp - p0 == 1)) {
3116 JsonWriteBasic(vname[p0]);
3117 if (nsame > 1)
3118 fValue.Append(TString::Format("%s\"n%s\":%d", fArraySepar.Data(), suffix.Data(), nsame));
3119 } else {
3120 fValue.Append("[");
3121 for (Int_t indx = p0; indx < pp; indx++) {
3122 if (indx > p0)
3124 JsonWriteBasic(vname[indx]);
3125 }
3126 fValue.Append("]");
3127 }
3128 }
3129 }
3130 fValue.Append("}");
3131 }
3132}
3133
3134////////////////////////////////////////////////////////////////////////////////
3135/// Write array of Bool_t to buffer
3136
3138{
3139 JsonPushValue();
3140 JsonWriteArrayCompress(b, n, "Bool");
3141}
3142
3143////////////////////////////////////////////////////////////////////////////////
3144/// Write array of Char_t to buffer
3145
3147{
3148 JsonPushValue();
3149 JsonWriteArrayCompress(c, n, "Int8");
3150}
3151
3152////////////////////////////////////////////////////////////////////////////////
3153/// Write array of UChar_t to buffer
3154
3156{
3157 JsonPushValue();
3158 JsonWriteArrayCompress(c, n, "Uint8");
3159}
3160
3161////////////////////////////////////////////////////////////////////////////////
3162/// Write array of Short_t to buffer
3163
3165{
3166 JsonPushValue();
3167 JsonWriteArrayCompress(h, n, "Int16");
3168}
3169
3170////////////////////////////////////////////////////////////////////////////////
3171/// Write array of UShort_t to buffer
3172
3174{
3175 JsonPushValue();
3176 JsonWriteArrayCompress(h, n, "Uint16");
3177}
3178
3179////////////////////////////////////////////////////////////////////////////////
3180/// Write array of Int_ to buffer
3181
3183{
3184 JsonPushValue();
3185 JsonWriteArrayCompress(i, n, "Int32");
3186}
3187
3188////////////////////////////////////////////////////////////////////////////////
3189/// Write array of UInt_t to buffer
3190
3192{
3193 JsonPushValue();
3194 JsonWriteArrayCompress(i, n, "Uint32");
3195}
3196
3197////////////////////////////////////////////////////////////////////////////////
3198/// Write array of Long_t to buffer
3199
3201{
3202 JsonPushValue();
3203 JsonWriteArrayCompress(l, n, "Int64");
3204}
3205
3206////////////////////////////////////////////////////////////////////////////////
3207/// Write array of ULong_t to buffer
3208
3210{
3211 JsonPushValue();
3212 JsonWriteArrayCompress(l, n, "Uint64");
3213}
3214
3215////////////////////////////////////////////////////////////////////////////////
3216/// Write array of Long64_t to buffer
3217
3219{
3220 JsonPushValue();
3221 JsonWriteArrayCompress(l, n, "Int64");
3222}
3223
3224////////////////////////////////////////////////////////////////////////////////
3225/// Write array of ULong64_t to buffer
3226
3228{
3229 JsonPushValue();
3230 JsonWriteArrayCompress(l, n, "Uint64");
3231}
3232
3233////////////////////////////////////////////////////////////////////////////////
3234/// Write array of Float_t to buffer
3235
3237{
3238 JsonPushValue();
3239 JsonWriteArrayCompress(f, n, "Float32");
3240}
3241
3242////////////////////////////////////////////////////////////////////////////////
3243/// Write array of Double_t to buffer
3244
3246{
3247 JsonPushValue();
3248 JsonWriteArrayCompress(d, n, "Float64");
3249}
3250
3251////////////////////////////////////////////////////////////////////////////////
3252/// Template method to write array of arbitrary dimensions
3253/// Different methods can be used for store last array dimension -
3254/// either JsonWriteArrayCompress<T>() or JsonWriteConstChar()
3255/// \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.
3256///
3257template <typename T>
3258void TBufferJSON::JsonWriteFastArray(const T *arr, Long64_t arrsize, const char *typname,
3259 void (TBufferJSON::*method)(const T *, Int_t, const char *))
3260{
3261 JsonPushValue();
3262 if (arrsize <= 0) { /*fJsonrCnt++;*/
3263 fValue.Append("[]");
3264 return;
3265 }
3266 constexpr Int_t dataWidth = 1; // at least 1
3267 const Int_t maxElements = (std::numeric_limits<Int_t>::max() - Length())/dataWidth;
3268 if (arrsize > maxElements)
3269 {
3270 Fatal("JsonWriteFastArray", "Not enough space left in the buffer (1GB limit). %lld elements is greater than the max left of %d", arrsize, maxElements);
3271 return; // In case the user re-routes the error handler to not die when Fatal is called
3272 }
3273
3274 TStreamerElement *elem = Stack()->fElem;
3275 if (elem && (elem->GetArrayDim() > 1) && (elem->GetArrayLength() == arrsize)) {
3276 TArrayI indexes(elem->GetArrayDim() - 1);
3277 indexes.Reset(0);
3278 Int_t cnt = 0, shift = 0, len = elem->GetMaxIndex(indexes.GetSize());
3279 while (cnt >= 0) {
3280 if (indexes[cnt] >= elem->GetMaxIndex(cnt)) {
3281 fValue.Append("]");
3282 indexes[cnt--] = 0;
3283 if (cnt >= 0)
3284 indexes[cnt]++;
3285 continue;
3286 }
3287 fValue.Append(indexes[cnt] == 0 ? "[" : fArraySepar.Data());
3288 if (++cnt == indexes.GetSize()) {
3289 (*this.*method)((arr + shift), len, typname);
3290 indexes[--cnt]++;
3291 shift += len;
3292 }
3293 }
3294 } else {
3295 (*this.*method)(arr, arrsize, typname);
3296 }
3297}
3298
3299////////////////////////////////////////////////////////////////////////////////
3300/// Write array of Bool_t to buffer
3301
3303{
3304 JsonWriteFastArray(b, n, "Bool", &TBufferJSON::JsonWriteArrayCompress<Bool_t>);
3305}
3306
3307////////////////////////////////////////////////////////////////////////////////
3308/// Write array of Char_t to buffer
3309///
3310/// Normally written as JSON string, but if string includes \0 in the middle
3311/// or some special characters, uses regular array. From array size 1000 it
3312/// will be automatically converted into base64 coding
3313
3315{
3316 Bool_t need_blob = false;
3317 Bool_t has_zero = false;
3318 for (Long64_t i=0;i<n;++i) {
3319 if (!c[i]) {
3320 has_zero = true; // might be terminal '\0'
3321 } else if (has_zero || !isprint(c[i])) {
3322 need_blob = true;
3323 break;
3324 }
3325 }
3326
3327 if (need_blob && (n >= 1000) && (!Stack()->fElem || (Stack()->fElem->GetArrayDim() < 2)))
3328 Stack()->fBase64 = true;
3329
3330 JsonWriteFastArray(c, n, "Int8", need_blob ? &TBufferJSON::JsonWriteArrayCompress<Char_t> : &TBufferJSON::JsonWriteConstChar);
3331}
3332
3333////////////////////////////////////////////////////////////////////////////////
3334/// Write array of Char_t to buffer
3335
3337{
3339}
3340
3341////////////////////////////////////////////////////////////////////////////////
3342/// Write array of UChar_t to buffer
3343
3345{
3346 JsonWriteFastArray(c, n, "Uint8", &TBufferJSON::JsonWriteArrayCompress<UChar_t>);
3347}
3348
3349////////////////////////////////////////////////////////////////////////////////
3350/// Write array of Short_t to buffer
3351
3353{
3354 JsonWriteFastArray(h, n, "Int16", &TBufferJSON::JsonWriteArrayCompress<Short_t>);
3355}
3356
3357////////////////////////////////////////////////////////////////////////////////
3358/// Write array of UShort_t to buffer
3359
3361{
3362 JsonWriteFastArray(h, n, "Uint16", &TBufferJSON::JsonWriteArrayCompress<UShort_t>);
3363}
3364
3365////////////////////////////////////////////////////////////////////////////////
3366/// Write array of Int_t to buffer
3367
3369{
3370 JsonWriteFastArray(i, n, "Int32", &TBufferJSON::JsonWriteArrayCompress<Int_t>);
3371}
3372
3373////////////////////////////////////////////////////////////////////////////////
3374/// Write array of UInt_t to buffer
3375
3377{
3378 JsonWriteFastArray(i, n, "Uint32", &TBufferJSON::JsonWriteArrayCompress<UInt_t>);
3379}
3380
3381////////////////////////////////////////////////////////////////////////////////
3382/// Write array of Long_t to buffer
3383
3385{
3386 JsonWriteFastArray(l, n, "Int64", &TBufferJSON::JsonWriteArrayCompress<Long_t>);
3387}
3388
3389////////////////////////////////////////////////////////////////////////////////
3390/// Write array of ULong_t to buffer
3391
3393{
3394 JsonWriteFastArray(l, n, "Uint64", &TBufferJSON::JsonWriteArrayCompress<ULong_t>);
3395}
3396
3397////////////////////////////////////////////////////////////////////////////////
3398/// Write array of Long64_t to buffer
3399
3401{
3402 JsonWriteFastArray(l, n, "Int64", &TBufferJSON::JsonWriteArrayCompress<Long64_t>);
3403}
3404
3405////////////////////////////////////////////////////////////////////////////////
3406/// Write array of ULong64_t to buffer
3407
3409{
3410 JsonWriteFastArray(l, n, "Uint64", &TBufferJSON::JsonWriteArrayCompress<ULong64_t>);
3411}
3412
3413////////////////////////////////////////////////////////////////////////////////
3414/// Write array of Float_t to buffer
3415
3417{
3418 JsonWriteFastArray(f, n, "Float32", &TBufferJSON::JsonWriteArrayCompress<Float_t>);
3419}
3420
3421////////////////////////////////////////////////////////////////////////////////
3422/// Write array of Double_t to buffer
3423
3425{
3426 JsonWriteFastArray(d, n, "Float64", &TBufferJSON::JsonWriteArrayCompress<Double_t>);
3427}
3428
3429////////////////////////////////////////////////////////////////////////////////
3430/// Recall TBuffer function to avoid gcc warning message
3431
3432void TBufferJSON::WriteFastArray(void *start, const TClass *cl, Long64_t n, TMemberStreamer * /* streamer */)
3433{
3434 if (gDebug > 2)
3435 Info("WriteFastArray", "void *start cl:%s n:%lld", cl ? cl->GetName() : "---", n);
3436
3437 // if (streamer) {
3438 // JsonDisablePostprocessing();
3439 // (*streamer)(*this, start, 0);
3440 // return;
3441 // }
3442
3443 if (n < 0) {
3444 // special handling of empty StreamLoop
3445 AppendOutput("null");
3447 } else {
3448
3449 char *obj = (char *)start;
3450 if (!n)
3451 n = 1;
3452 int size = cl->Size();
3453
3454 TArrayIndexProducer indexes(Stack()->fElem, n, fArraySepar.Data());
3455
3456 if (indexes.IsArray()) {
3458 AppendOutput(indexes.GetBegin());
3459 }
3460
3461 for (Long64_t j = 0; j < n; j++, obj += size) {
3462
3463 if (j > 0)
3464 AppendOutput(indexes.NextSeparator());
3465
3466 JsonWriteObject(obj, cl, kFALSE);
3467
3468 if (indexes.IsArray() && (fValue.Length() > 0)) {
3470 fValue.Clear();
3471 }
3472 }
3473
3474 if (indexes.IsArray())
3475 AppendOutput(indexes.GetEnd());
3476 }
3477
3478 if (Stack()->fIndx)
3479 AppendOutput(Stack()->fIndx->NextSeparator());
3480}
3481
3482////////////////////////////////////////////////////////////////////////////////
3483/// Recall TBuffer function to avoid gcc warning message
3484
3485Int_t TBufferJSON::WriteFastArray(void **start, const TClass *cl, Long64_t n, Bool_t isPreAlloc,
3486 TMemberStreamer * /* streamer */)
3487{
3488 if (gDebug > 2)
3489 Info("WriteFastArray", "void **startp cl:%s n:%lld", cl->GetName(), n);
3490
3491 // if (streamer) {
3492 // JsonDisablePostprocessing();
3493 // (*streamer)(*this, (void *)start, 0);
3494 // return 0;
3495 // }
3496
3497 if (n <= 0)
3498 return 0;
3499
3500 Int_t res = 0;
3501
3502 TArrayIndexProducer indexes(Stack()->fElem, n, fArraySepar.Data());
3503
3504 if (indexes.IsArray()) {
3506 AppendOutput(indexes.GetBegin());
3507 }
3508
3509 for (Long64_t j = 0; j < n; j++) {
3510
3511 if (j > 0)
3512 AppendOutput(indexes.NextSeparator());
3513
3514 if (!isPreAlloc) {
3515 res |= WriteObjectAny(start[j], cl);
3516 } else {
3517 if (!start[j])
3518 start[j] = (const_cast<TClass *>(cl))->New();
3519 // ((TClass*)cl)->Streamer(start[j],*this);
3520 JsonWriteObject(start[j], cl, kFALSE);
3521 }
3522
3523 if (indexes.IsArray() && (fValue.Length() > 0)) {
3525 fValue.Clear();
3526 }
3527 }
3528
3529 if (indexes.IsArray())
3530 AppendOutput(indexes.GetEnd());
3531
3532 if (Stack()->fIndx)
3533 AppendOutput(Stack()->fIndx->NextSeparator());
3534
3535 return res;
3536}
3537
3538////////////////////////////////////////////////////////////////////////////////
3539/// stream object to/from buffer
3540
3541void TBufferJSON::StreamObject(void *obj, const TClass *cl, const TClass * /* onfileClass */)
3542{
3543 if (gDebug > 3)
3544 Info("StreamObject", "Class: %s", (cl ? cl->GetName() : "none"));
3545
3546 if (IsWriting())
3547 JsonWriteObject(obj, cl);
3548 else
3549 JsonReadObject(obj, cl);
3550}
3551
3552////////////////////////////////////////////////////////////////////////////////
3553/// Template function to read basic value from JSON
3554
3555template <typename T>
3557{
3558 value = Stack()->GetStlNode()->get<T>();
3559}
3560
3561////////////////////////////////////////////////////////////////////////////////
3562/// Reads Bool_t value from buffer
3563
3565{
3566 JsonReadBasic(val);
3567}
3568
3569////////////////////////////////////////////////////////////////////////////////
3570/// Reads Char_t value from buffer
3571
3573{
3574 if (!Stack()->fValues.empty())
3575 val = (Char_t)Stack()->PopIntValue();
3576 else
3577 val = Stack()->GetStlNode()->get<Char_t>();
3578}
3579
3580////////////////////////////////////////////////////////////////////////////////
3581/// Reads UChar_t value from buffer
3582
3584{
3585 JsonReadBasic(val);
3586}
3587
3588////////////////////////////////////////////////////////////////////////////////
3589/// Reads Short_t value from buffer
3590
3592{
3593 JsonReadBasic(val);
3594}
3595
3596////////////////////////////////////////////////////////////////////////////////
3597/// Reads UShort_t value from buffer
3598
3600{
3601 JsonReadBasic(val);
3602}
3603
3604////////////////////////////////////////////////////////////////////////////////
3605/// Reads Int_t value from buffer
3606
3608{
3609 if (!Stack()->fValues.empty())
3610 val = Stack()->PopIntValue();
3611 else
3612 JsonReadBasic(val);
3613}
3614
3615////////////////////////////////////////////////////////////////////////////////
3616/// Reads UInt_t value from buffer
3617
3619{
3620 JsonReadBasic(val);
3621}
3622
3623////////////////////////////////////////////////////////////////////////////////
3624/// Reads Long_t value from buffer
3625
3627{
3628 JsonReadBasic(val);
3629}
3630
3631////////////////////////////////////////////////////////////////////////////////
3632/// Reads ULong_t value from buffer
3633
3635{
3636 JsonReadBasic(val);
3637}
3638
3639////////////////////////////////////////////////////////////////////////////////
3640/// Reads Long64_t value from buffer
3641
3643{
3644 JsonReadBasic(val);
3645}
3646
3647////////////////////////////////////////////////////////////////////////////////
3648/// Reads ULong64_t value from buffer
3649
3651{
3652 JsonReadBasic(val);
3653}
3654
3655////////////////////////////////////////////////////////////////////////////////
3656/// Reads Float_t value from buffer
3657
3659{
3660 nlohmann::json *json = Stack()->GetStlNode();
3661 if (json->is_null())
3662 val = std::numeric_limits<Float_t>::quiet_NaN();
3663 else
3664 val = json->get<Float_t>();
3665}
3666
3667////////////////////////////////////////////////////////////////////////////////
3668/// Reads Double_t value from buffer
3669
3671{
3672 nlohmann::json *json = Stack()->GetStlNode();
3673 if (json->is_null())
3674 val = std::numeric_limits<Double_t>::quiet_NaN();
3675 else
3676 val = json->get<Double_t>();
3677}
3678
3679////////////////////////////////////////////////////////////////////////////////
3680/// Reads array of characters from buffer
3681
3683{
3684 Error("ReadCharP", "Not implemented");
3685}
3686
3687////////////////////////////////////////////////////////////////////////////////
3688/// Reads a TString
3689
3691{
3692 std::string str;
3693 JsonReadBasic(str);
3694 val = str.c_str();
3695}
3696
3697////////////////////////////////////////////////////////////////////////////////
3698/// Reads a std::string
3699
3700void TBufferJSON::ReadStdString(std::string *val)
3701{
3702 JsonReadBasic(*val);
3703}
3704
3705////////////////////////////////////////////////////////////////////////////////
3706/// Reads a char* string
3707
3709{
3710 std::string str;
3711 JsonReadBasic(str);
3712
3713 if (s) {
3714 delete[] s;
3715 s = nullptr;
3716 }
3717
3718 std::size_t nch = str.length();
3719 if (nch > 0) {
3720 s = new char[nch + 1];
3721 memcpy(s, str.c_str(), nch);
3722 s[nch] = 0;
3723 }
3724}
3725
3726////////////////////////////////////////////////////////////////////////////////
3727/// Writes Bool_t value to buffer
3728
3730{
3731 JsonPushValue();
3733}
3734
3735////////////////////////////////////////////////////////////////////////////////
3736/// Writes Char_t value to buffer
3737
3739{
3740 JsonPushValue();
3742}
3743
3744////////////////////////////////////////////////////////////////////////////////
3745/// Writes UChar_t value to buffer
3746
3748{
3749 JsonPushValue();
3751}
3752
3753////////////////////////////////////////////////////////////////////////////////
3754/// Writes Short_t value to buffer
3755
3757{
3758 JsonPushValue();
3760}
3761
3762////////////////////////////////////////////////////////////////////////////////
3763/// Writes UShort_t value to buffer
3764
3766{
3767 JsonPushValue();
3769}
3770
3771////////////////////////////////////////////////////////////////////////////////
3772/// Writes Int_t value to buffer
3773
3775{
3776 JsonPushValue();
3777 JsonWriteBasic(i);
3778}
3779
3780////////////////////////////////////////////////////////////////////////////////
3781/// Writes UInt_t value to buffer
3782
3784{
3785 JsonPushValue();
3786 JsonWriteBasic(i);
3787}
3788
3789////////////////////////////////////////////////////////////////////////////////
3790/// Writes Long_t value to buffer
3791
3793{
3794 JsonPushValue();
3796}
3797
3798////////////////////////////////////////////////////////////////////////////////
3799/// Writes ULong_t value to buffer
3800
3802{
3803 JsonPushValue();
3805}
3806
3807////////////////////////////////////////////////////////////////////////////////
3808/// Writes Long64_t value to buffer
3809
3811{
3812 JsonPushValue();
3814}
3815
3816////////////////////////////////////////////////////////////////////////////////
3817/// Writes ULong64_t value to buffer
3818
3820{
3821 JsonPushValue();
3823}
3824
3825////////////////////////////////////////////////////////////////////////////////
3826/// Writes Float_t value to buffer
3827
3829{
3830 JsonPushValue();
3832}
3833
3834////////////////////////////////////////////////////////////////////////////////
3835/// Writes Double_t value to buffer
3836
3838{
3839 JsonPushValue();
3841}
3842
3843////////////////////////////////////////////////////////////////////////////////
3844/// Writes array of characters to buffer
3845
3847{
3848 JsonPushValue();
3849
3851}
3852
3853////////////////////////////////////////////////////////////////////////////////
3854/// Writes a TString
3855
3857{
3858 JsonPushValue();
3859
3860 JsonWriteConstChar(s.Data(), s.Length());
3861}
3862
3863////////////////////////////////////////////////////////////////////////////////
3864/// Writes a std::string
3865
3866void TBufferJSON::WriteStdString(const std::string *s)
3867{
3868 JsonPushValue();
3869
3870 if (s)
3871 JsonWriteConstChar(s->c_str(), s->length());
3872 else
3873 JsonWriteConstChar("", 0);
3874}
3875
3876////////////////////////////////////////////////////////////////////////////////
3877/// Writes a char*
3878
3880{
3881 JsonPushValue();
3882
3884}
3885
3886////////////////////////////////////////////////////////////////////////////////
3887/// converts Char_t to string and add to json value buffer
3888
3890{
3891 char buf[50];
3892 snprintf(buf, sizeof(buf), "%d", value);
3893 fValue.Append(buf);
3894}
3895
3896////////////////////////////////////////////////////////////////////////////////
3897/// converts Short_t to string and add to json value buffer
3898
3900{
3901 char buf[50];
3902 snprintf(buf, sizeof(buf), "%hd", value);
3903 fValue.Append(buf);
3904}
3905
3906////////////////////////////////////////////////////////////////////////////////
3907/// converts Int_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 Long_t to string and add to json value buffer
3918
3920{
3921 char buf[50];
3922 snprintf(buf, sizeof(buf), "%ld", value);
3923 fValue.Append(buf);
3924}
3925
3926////////////////////////////////////////////////////////////////////////////////
3927/// converts Long64_t to string and add to json value buffer
3928
3930{
3931 fValue.Append(std::to_string(value).c_str());
3932}
3933
3934////////////////////////////////////////////////////////////////////////////////
3935/// converts Float_t to string and add to json value buffer
3936
3938{
3939 if (std::isinf(value)) {
3940 fValue.Append((value < 0.) ? "-2e308" : "2e308"); // Number.MAX_VALUE is approx 1.79e308
3941 } else if (std::isnan(value)) {
3942 fValue.Append("null");
3943 } else {
3944 char buf[200];
3945 ConvertFloat(value, buf, sizeof(buf));
3946 fValue.Append(buf);
3947 }
3948}
3949
3950////////////////////////////////////////////////////////////////////////////////
3951/// converts Double_t to string and add to json value buffer
3952
3954{
3955 if (std::isinf(value)) {
3956 fValue.Append((value < 0.) ? "-2e308" : "2e308"); // Number.MAX_VALUE is approx 1.79e308
3957 } else if (std::isnan(value)) {
3958 fValue.Append("null");
3959 } else {
3960 char buf[200];
3961 ConvertDouble(value, buf, sizeof(buf));
3962 fValue.Append(buf);
3963 }
3964}
3965
3966////////////////////////////////////////////////////////////////////////////////
3967/// converts Bool_t to string and add to json value buffer
3968
3970{
3971 fValue.Append(value ? "true" : "false");
3972}
3973
3974////////////////////////////////////////////////////////////////////////////////
3975/// converts UChar_t to string and add to json value buffer
3976
3978{
3979 char buf[50];
3980 snprintf(buf, sizeof(buf), "%u", value);
3981 fValue.Append(buf);
3982}
3983
3984////////////////////////////////////////////////////////////////////////////////
3985/// converts UShort_t to string and add to json value buffer
3986
3988{
3989 char buf[50];
3990 snprintf(buf, sizeof(buf), "%hu", value);
3991 fValue.Append(buf);
3992}
3993
3994////////////////////////////////////////////////////////////////////////////////
3995/// converts UInt_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 ULong_t to string and add to json value buffer
4006
4008{
4009 char buf[50];
4010 snprintf(buf, sizeof(buf), "%lu", value);
4011 fValue.Append(buf);
4012}
4013
4014////////////////////////////////////////////////////////////////////////////////
4015/// converts ULong64_t to string and add to json value buffer
4016
4018{
4019 fValue.Append(std::to_string(value).c_str());
4020}
4021
4022////////////////////////////////////////////////////////////////////////////////
4023/// writes string value, processing all kind of special characters
4024
4025void TBufferJSON::JsonWriteConstChar(const char *value, Int_t len, const char * /* typname */)
4026{
4027 if (!value) {
4028
4029 fValue.Append("\"\"");
4030
4031 } else {
4032
4033 fValue.Append("\"");
4034
4035 if (len < 0)
4036 len = strlen(value);
4037
4038 for (Int_t n = 0; n < len; n++) {
4039 unsigned char c = value[n];
4040 switch (c) {
4041 case 0: n = len; break;
4042 case '\n': fValue.Append("\\n"); break;
4043 case '\t': fValue.Append("\\t"); break;
4044 case '\"': fValue.Append("\\\""); break;
4045 case '\\': fValue.Append("\\\\"); break;
4046 case '\b': fValue.Append("\\b"); break;
4047 case '\f': fValue.Append("\\f"); break;
4048 case '\r': fValue.Append("\\r"); break;
4049 case '/': fValue.Append("\\/"); break;
4050 default:
4051 if (c < 31) {
4052 fValue.Append(TString::Format("\\u%04x", (unsigned)c));
4053 } else if (c < 0x80) {
4054 fValue.Append(c);
4055 } else if ((n < len - 1) && ((c & 0xe0) == 0xc0) && ((value[n+1] & 0xc0) == 0x80)) {
4056 unsigned code = ((unsigned)value[n+1] & 0x3f) | (((unsigned) c & 0x1f) << 6);
4057 fValue.Append(TString::Format("\\u%04x", code));
4058 n++;
4059 } else if ((n < len - 2) && ((c & 0xf0) == 0xe0) && ((value[n+1] & 0xc0) == 0x80) && ((value[n+2] & 0xc0) == 0x80)) {
4060 unsigned code = ((unsigned)value[n+2] & 0x3f) | (((unsigned) value[n+1] & 0x3f) << 6) | (((unsigned) c & 0x0f) << 12);
4061 fValue.Append(TString::Format("\\u%04x", code));
4062 n+=2;
4063 } else if ((n < len - 3) && ((c & 0xf8) == 0xf0) && ((value[n+1] & 0xc0) == 0x80) && ((value[n+2] & 0xc0) == 0x80) && ((value[n+3] & 0xc0) == 0x80)) {
4064 unsigned code = ((unsigned)value[n+3] & 0x3f) | (((unsigned) value[n+2] & 0x3f) << 6) | (((unsigned) value[n+1] & 0x3f) << 12) | (((unsigned) c & 0x07) << 18);
4065 // TODO: no idea how to add codes which are higher then 0xFFFF
4066 fValue.Append(TString::Format("\\u%04x\\u%04x", code & 0xffff, code >> 16));
4067 n+=3;
4068 } else {
4069 fValue.Append(TString::Format("\\u%04x", (unsigned)c));
4070 }
4071 }
4072 }
4073
4074 fValue.Append("\"");
4075 }
4076}
4077
4078////////////////////////////////////////////////////////////////////////////////
4079/// Read data of base class.
4080
4082{
4083 if (elem->GetClassPointer() == TObject::Class()) {
4085 } else {
4086 TBufferText::ReadBaseClass(start, elem);
4087 }
4088}
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:82
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:101
long long Long64_t
Definition RtypesCore.h:80
unsigned long long ULong64_t
Definition RtypesCore.h:81
constexpr Bool_t kTRUE
Definition RtypesCore.h:100
#define ClassImp(name)
Definition Rtypes.h:377
@ 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:607
ROOT::ESTLType GetCollectionType() const
Return the 'type' of the STL the TClass is representing.
Definition TClass.cxx:2886
void * New(ENewType defConstructor=kClassNew, Bool_t quiet=kFALSE) const
Return a pointer to a newly allocated object of this class.
Definition TClass.cxx:4978
void Destructor(void *obj, Bool_t dtorOnly=kFALSE)
Explicitly call destructor for object.
Definition TClass.cxx:5400
Longptr_t GetDataMemberOffset(const char *membername) const
return offset for member name.
Definition TClass.cxx:3477
TList * GetListOfRealData() const
Definition TClass.h:453
Int_t Size() const
Return size of object of this class.
Definition TClass.cxx:5704
Bool_t IsTObject() const
Return kTRUE is the class inherits from TObject.
Definition TClass.cxx:5938
Int_t GetBaseClassOffset(const TClass *toBase, void *address=nullptr, bool isDerivedObject=true)
Definition TClass.cxx:2791
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition TClass.cxx:2897
Version_t GetClassVersion() const
Definition TClass.h:420
TClass * GetActualClass(const void *object) const
Return a pointer to the real class of the object.
Definition TClass.cxx:2607
TRealData * GetRealData(const char *name) const
Return pointer to TRealData element with name "name".
Definition TClass.cxx:3503
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:2968
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
Option_t * GetOption() const
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:83
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()
TObject * GetValue(const char *keyname) const
Returns a pointer to the value associated with keyname as name of the key.
Definition TMap.cxx:236
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:973
static TClass * Class()
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:525
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:987
virtual void Fatal(const char *method, const char *msgfmt,...) const
Issue fatal error message.
Definition TObject.cxx:1015
virtual void SetUniqueID(UInt_t uid)
Set the unique object id.
Definition TObject.cxx:791
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:961
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
virtual Bool_t IsBase() const
Return kTRUE if the element represent a base class.
virtual void SetMaxIndex(Int_t dim, Int_t max)
set maximum index for array with dimension dim
Describes a persistent version of a class.
Int_t GetClassVersion() const override
TClass * GetClass() const override
TObjArray * GetElements() const override
Basic string class.
Definition TString.h:139
Ssiz_t Length() const
Definition TString.h:417
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1988
void Clear()
Clear string without changing its capacity.
Definition TString.cxx:1235
TString & Replace(Ssiz_t pos, Ssiz_t n, const char *s)
Definition TString.h:694
const char * Data() const
Definition TString.h:376
Ssiz_t Capacity() const
Definition TString.h:364
void Resize(Ssiz_t n)
Resize the string. Truncate or add blanks as necessary.
Definition TString.cxx:1152
TString & Append(const char *cs)
Definition TString.h:572
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2378
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition TString.cxx:2356
static TClass * Class()
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:651
Abstract Interface class describing Streamer information for one class.
static Bool_t CanDelete()
static function returning true if ReadBuffer can delete object
virtual TClass * GetClass() const =0
const Int_t n
Definition legend1.C:16
@ kSTLend
Definition ESTLType.h:47
@ kSTLvector
Definition ESTLType.h:30
@ kSTLlist
Definition ESTLType.h:31
@ kSTLforwardlist
Definition ESTLType.h:41
@ kNotSTL
Definition ESTLType.h:29
@ kUnorderedMultiSet
Definition TClassEdit.h:104
@ kUnorderedMultiMap
Definition TClassEdit.h:106
bool IsStdClass(const char *type)
return true if the class belongs to the std namespace
const char * fTypeTag
iterator for std::map stored as JSON object
nlohmann::json fValue
type tag used for std::map stored as JSON object
Bool_t fFirst
special iterator over STL map::key members
nlohmann::json * GetStlNode(nlohmann::json *prnt)
temporary value reading std::map as JSON
nlohmann::json::iterator fIter
is first or second element is used in the pair
Int_t fMap
index of object in STL container
TLine l
Definition textangle.C:4