JSON encoder: align keys to camelCase and support flat/nested formats#6
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the JSON encoder/decoder to use camelCase field names and camelCase PDU type strings, and introduces support for decoding both “flat” JSON messages (payload at root) and “nested” messages (payload under a data wrapper), with the encoder attempting to mirror the last decoded format.
Changes:
- Renames JSON envelope keys and payload keys to camelCase (e.g.,
type,id,dAppName,ranFunctionList) and updates PDU type string values to camelCase. - Updates decoding to accept both flat and nested (
data-wrapped) formats and makes encoding optionally emit nested payloads based on the last decode. - Adjusts CMake
find_packageminimum version fornlohmann_jsonand updates JSON encoder tests for new PDU type strings.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
tests/test_json_encoder.cpp |
Updates assertions to match new camelCase PDU type strings in encoded JSON. |
src/encoder/json_encoder.hpp |
Adds nested_mode_ state used to choose flat vs nested encoding format. |
src/encoder/json_encoder.cpp |
Implements camelCase keys, new PDU type string mapping, and flat/nested decode + stateful encode mirroring. |
cmake/libe3Dependencies.cmake |
Lowers the minimum nlohmann_json version requested via find_package. |
Comments suppressed due to low confidence (1)
cmake/libe3Dependencies.cmake:21
- CMake now accepts an installed
nlohmann_jsonas old as 3.10, but the FetchContent fallback still pinsv3.11.3. This can lead to different behavior/ABI depending on environment.
Consider aligning the minimum find_package version with the fetched tag (or update the fetched tag to a 3.10.x release if 3.10 support is the goal).
find_package(nlohmann_json 3.10 QUIET)
if(NOT nlohmann_json_FOUND)
include(FetchContent)
FetchContent_Declare(
nlohmann_json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.3
GIT_SHALLOW TRUE
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. |
b73fb51 to
4ec3cb0
Compare
📊 MPMC Queue Benchmark Results (commit
|
| Queue Capacity | P50 (ns) | P95 (ns) | P99 (ns) | P99.9 (ns) |
|---|---|---|---|---|
| 16 | 647 | 687 | 708 | 24000 |
| 64 | 2441 | 2561 | 2671 | 27207 |
| 256 | 6889 | 9945 | 30634 | 34420 |
| 1024 | 13151 | 13381 | 32466 | 32517 |
| 4096 | 18418 | 18830 | 36612 | 36633 |
ResponseQueue End-to-End Latency (SPSC, 30000 items per queue size)
| Queue Capacity | P50 (ns) | P95 (ns) | P99 (ns) |
|---|---|---|---|
| 16 | 1078 | 1810 | 92680 |
| 64 | 15820 | 22593 | 37831 |
| 256 | 38963 | 60162 | 84528 |
| 1024 | 130515 | 153958 | 182912 |
| 4096 | 514372 | 585956 | 586518 |
Throughput (400000 items per configuration)
| Configuration | Queue Cap | Throughput (Mops/s) |
|---|---|---|
| SPSC (1P×1C) | 256 | 33.81 |
| MPSC (4P×1C) | 1024 | 40.58 |
| MPMC (4P×4C) | 4096 | 54.81 |
| MPMC (8P×4C) | 4096 | 26.86 |
Stress / Correctness (100000 items each)
| Test | Items | Result |
|---|---|---|
| SPSC (1P×1C) | 100000 | ✅ PASS |
| MPSC (4P×1C) | 100000 | ✅ PASS |
| MPMC (4P×4C) | 100000 | ✅ PASS |
| MPMC (8P×8C) | 100000 | ✅ PASS |
Benchmarked on
ubuntu-latest, Release build
|
Once this is ready to be merged i will review it again. one minor note: if possible let's keep json at the 3.11 version. Feel free to set this as ready once we can review it |
|
OSRB has been approved. I have added dvilla in CONTRIBUTORS, and reverted the change of JSON to 3.11. The MR is now ready to be reviewed and merged. |
⏱️ Full-loop Latency Benchmark (commit
|
| Phase | mean | p50 | p99 | max |
|---|---|---|---|---|
| 1. Collect indication data | 0 | 0 | 0 | 7 |
| 2. Create & encode indication | 0 | 0 | 0 | 8 |
| 3. Deliver indication (RAN -> dApp) | 83 | 78 | 150 | 518 |
| 4. Decode indication | 0 | 0 | 0 | 8 |
| 5. Process data | 0 | 0 | 0 | 0 |
| 6. Create & encode control | 0 | 0 | 0 | 1 |
| 7. Deliver control (dApp -> RAN) | 101 | 103 | 158 | 170 |
| 8. Decode & handle control | 0 | 0 | 0 | 6 |
| Total round-trip | 187 | 174 | 299 | 634 |
Benchmarked on
ubuntu-latest, Release build, ZMQ + IPC, ASN.1 APER.
🔄 E2E dApp Integration Results (commit
|
🔀 E2E Topologies — multi-dApp / multi-RAN (commit
|
Thecave3
left a comment
There was a problem hiding this comment.
Minor comments, if possible can you run and paste the test for the json one or can you update the template include the json in the tests done by the CI?
This would ensure that changes are preserved over the updates
|
Encoder threads are all resolved and CI is green. Two items left before merge: Blocking
Cleanup
Resolved by author's answers (no code change)
|
|
Great work, thanks for this contribution. One final thing, please rebase and bump the version to 0.0.6. Afterwards, I will approve the PR and include it with squash and merge. |
…ct unknown PDU types - Derive camelCase PDU type strings from pdu_type_to_string() instead of separate pdu_type_to_json_string() switch - Remove backward-compat fallbacks for legacy key names (pdu_type, message_id) - Return std::optional from string_to_pdu_type; fail decode on unrecognized types - Add Doxygen documentation on JsonE3Encoder class and nested_mode_ member - Add tests: nested format decode, encode mirrors nested format, PascalCase rejection
Drop nested_mode_ member. Encoder always merges payload fields at root. Decoder rejects messages containing a "data" wrapper with DECODE_FAILED. Update indication-message tests to use valid JSON in protocol_data so they exercise the json::parse() path the encoder now requires.
Add per-subscription control_ids alongside telemetry_ids, threaded through add_subscription() and exposed via get_subscription_control_ids() and the matching C API. SubscriptionRequest already carries control_identifier_list on the wire, so no encoder change. Also drop dapp_id/ran_function_id from SubscriptionDetails since they duplicate the composite map key and can drift.
Done and tests run with no issues. |
Thecave3
left a comment
There was a problem hiding this comment.
Great work, thanks for this contribution.
dAppName,responseCode,ranFunctionList)setupRequestinstead ofSetupRequest)datawrapper, payload is read from it; otherwise, payload is read from root