Skip to content

Commit f6d4167

Browse files
authored
Merge pull request #148 from zhaozhiwen/fix/json-event-structure
JSON streamer: emit valid, correctly-nested event objects
2 parents bc46db2 + 314245b commit f6d4167

4 files changed

Lines changed: 49 additions & 55 deletions

File tree

gemc/gstreamer/factories/JSON/event/event.cc

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ bool GstreamerJsonFactory::startEventImpl(const std::shared_ptr<GEventDataCollec
2929
current_event_has_header = false;
3030
current_event_has_any_detector = false;
3131
current_event_has_generated = false;
32+
current_event_digitized_entries.clear();
3233

3334
// Cache the event number early so it is available to the root event object.
3435
event_number = event_data->getHeader()->getG4LocalEvn();
@@ -47,24 +48,37 @@ bool GstreamerJsonFactory::endEventImpl(const std::shared_ptr<GEventDataCollecti
4748
return false;
4849
}
4950

50-
// If the caller skipped the header step, emit a minimal fallback so the JSON remains valid.
51+
// If the caller skipped the header step, emit a minimal fallback and close the header
52+
// object (startEventImpl opened it; publishEventHeaderImpl normally writes its fields
53+
// and closes it).
5154
if (!current_event_has_header) {
52-
current_event << "\"timestamp\": \"\", \"thread_id\": -1";
55+
current_event << "\"timestamp\": \"\", \"thread_id\": -1}";
5356
}
5457

55-
// Detector publishers leave the "detectors" object open so digitized data
56-
// can be appended after true-information banks without rewriting strings.
57-
if (current_event_has_any_detector) {
58-
current_event << "}";
58+
// Ensure a "detectors" object exists so the schema stays predictable and any buffered
59+
// digitized data has a place to live, even when no true-information banks were published.
60+
bool detectors_has_content = current_event_has_any_detector;
61+
if (!current_event_has_any_detector) {
62+
current_event << ", \"detectors\": {";
63+
current_event_has_any_detector = true;
5964
}
60-
current_event << "}";
6165

62-
// Keep the event schema predictable even when no detector banks were published.
63-
if (!current_event_has_any_detector) {
64-
current_event << ", \"detectors\": {}";
66+
// Emit all buffered digitized detector arrays as a single, well-formed
67+
// "digitized_by_detector" object nested inside "detectors".
68+
if (!current_event_digitized_entries.empty()) {
69+
if (detectors_has_content) { current_event << ", "; }
70+
current_event << "\"digitized_by_detector\": {";
71+
bool first = true;
72+
for (const auto& entry : current_event_digitized_entries) {
73+
if (!first) { current_event << ", "; }
74+
first = false;
75+
current_event << entry;
76+
}
77+
current_event << "}";
6578
}
6679

67-
current_event << "}";
80+
current_event << "}"; // close "detectors"
81+
current_event << "}"; // close the event object
6882

6983
writeTopLevelEntry(current_event.str());
7084

gemc/gstreamer/factories/JSON/event/eventHeader.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ bool GstreamerJsonFactory::publishEventHeaderImpl(const std::unique_ptr<GEventHe
2626
current_event << "\"timestamp\": \"" << jsonEscape(timestamp) << "\""
2727
<< ", \"thread_id\": " << thread_id
2828
<< ", \"g4local_event\": " << gevent_header->getG4LocalEvn();
29+
current_event << "}"; // close the "header" object opened in startEventImpl
2930

3031
current_event_has_header = true;
3132
return true;

gemc/gstreamer/factories/JSON/event/publishDigitized.cc

Lines changed: 18 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,80 +17,54 @@ bool GstreamerJsonFactory::publishEventDigitizedDataImpl(const std::string&
1717
return false;
1818
}
1919

20-
// The current design appends digitized content into a reserved object nested under
21-
// "detectors". This avoids rewriting previously emitted detector JSON.
20+
// Build this detector's digitized array into a standalone entry and buffer it.
21+
// endEventImpl emits all buffered entries together as the "digitized_by_detector"
22+
// object, which keeps the JSON valid regardless of how true-info and digitized
23+
// publish calls interleave across detectors.
2224
if (digitizedData.empty()) return true;
2325

24-
static const char* marker = "\"digitized_by_detector\": {";
25-
const std::string assembled = current_event.str();
26-
27-
const bool has_digitized_container = (assembled.find(marker) != std::string::npos);
28-
29-
if (!has_digitized_container) {
30-
// Ensure the event already has a detectors object before reserving a nested map
31-
// for digitized collections keyed by detector name.
32-
if (!current_event_has_any_detector) {
33-
current_event << ", \"detectors\": {";
34-
current_event_has_any_detector = true;
35-
}
36-
else {
37-
current_event << ", ";
38-
}
39-
40-
current_event << "\"digitized_by_detector\": {";
41-
}
42-
43-
// If the reserved object already contains one detector entry, append a comma
44-
// before adding the next one.
45-
const std::string updated = current_event.str();
46-
if (!updated.empty()) {
47-
char last = updated.back();
48-
if (last != '{') current_event << ", ";
49-
}
50-
51-
current_event << "\"" << jsonEscape(detectorName) << "\": [";
26+
std::ostringstream entry;
27+
entry << "\"" << jsonEscape(detectorName) << "\": [";
5228

5329
bool wrote_first_hit = false;
5430
for (const auto* hit : digitizedData) {
5531
if (!hit) continue;
5632

57-
if (wrote_first_hit) current_event << ", ";
33+
if (wrote_first_hit) entry << ", ";
5834
wrote_first_hit = true;
5935

60-
current_event << "{";
36+
entry << "{";
6137

6238
auto addr = getIdentityString(hit->getIdentity());
6339

64-
current_event << "\"address\": \"" << jsonEscape(addr) << "\"";
40+
entry << "\"address\": \"" << jsonEscape(addr) << "\"";
6541

66-
current_event << ", \"vars\": {";
42+
entry << ", \"vars\": {";
6743

6844
bool wrote_first_var = false;
6945

7046
// Integer observables:
7147
// the argument 0 means "do not include SRO variables".
7248
for (const auto& [name, value] : hit->getIntObservablesMap(0)) {
73-
if (wrote_first_var) current_event << ", ";
49+
if (wrote_first_var) entry << ", ";
7450
wrote_first_var = true;
75-
current_event << "\"" << jsonEscape(name) << "\": " << value;
51+
entry << "\"" << jsonEscape(name) << "\": " << value;
7652
}
7753

7854
// Floating-point observables.
7955
for (const auto& [name, value] : hit->getDblObservablesMap(0)) {
80-
if (wrote_first_var) current_event << ", ";
56+
if (wrote_first_var) entry << ", ";
8157
wrote_first_var = true;
82-
current_event << "\"" << jsonEscape(name) << "\": " << value;
58+
entry << "\"" << jsonEscape(name) << "\": " << value;
8359
}
8460

85-
current_event << "}";
86-
current_event << "}";
61+
entry << "}";
62+
entry << "}";
8763
}
8864

89-
current_event << "]";
65+
entry << "]";
9066

91-
// Close the temporary digitized_by_detector object immediately so the enclosing
92-
// event remains structurally valid after this call.
93-
current_event << "}";
67+
current_event_digitized_entries.push_back(entry.str());
9468

9569
return true;
9670
}

gemc/gstreamer/factories/JSON/gstreamerJSONFactory.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ class GstreamerJsonFactory : public GStreamer {
185185
/// \brief Tracks whether the current event already contains generated-particle banks.
186186
bool current_event_has_generated = false;
187187

188+
/// \brief Buffered digitized detector arrays for the current event. endEventImpl emits
189+
/// them together as the "digitized_by_detector" object, which keeps the JSON valid
190+
/// regardless of how true-info and digitized publish calls interleave across detectors.
191+
std::vector<std::string> current_event_digitized_entries;
192+
188193
/// \brief Tracks whether the plugin is currently assembling a frame object.
189194
bool is_building_frame = false;
190195

0 commit comments

Comments
 (0)