Skip to content

core: switch JSON library from jsoncpp to nlohmann/json#2918

Merged
julianoes merged 6 commits into
mainfrom
switch-to-nlohmann-json
Jul 14, 2026
Merged

core: switch JSON library from jsoncpp to nlohmann/json#2918
julianoes merged 6 commits into
mainfrom
switch-to-nlohmann-json

Conversation

@julianoes

Copy link
Copy Markdown
Collaborator

What

Replace the compiled jsoncpp dependency with the header-only nlohmann/json (v3.11.3, already present transitively via libevents).

Why

  • One header-only JSON library instead of a compiled ExternalProject to build and link.
  • Unifies the two directions: previously jsoncpp was used only for parsing, while serialization was hand-rolled std::ostringstream concatenation.
  • Fixes a latent bug: the old MavlinkDirect serializer did not escape strings. nlohmann's dump() escapes correctly and renders NaN/Inf as null.

How

  • Exception safety (-fno-exceptions): parse via the non-throwing json::parse(..., allow_exceptions=false) + is_discarded(), with predicate-guarded access so nlohmann never hits its abort() path.
  • Serialize (libmav_receiver): reworked onto nlohmann::ordered_json + dump(), preserving field order (plain json would sort keys).
  • Migrated libmav_conversions, mission_import, mavlink_component_metadata, component_metadata_server_impl, and the system tests.
  • Updated build wiring (new third_party/nlohmann_json, removed jsoncpp + Findjsoncpp.cmake), packaging (.pc, CMake config, debian/control — header-only, so runtime dep dropped), CI, docs, and the example.

Testing

  • build-debug and build-server-debug both build cleanly.
  • Unit tests: 291 passed (includes mission import .plan fixtures + component metadata).
  • System tests: MavlinkDirect / ComponentMetadata / Intercept / ParamChange / RequestMessage all pass (round-trip serialize↔parse).
  • mavsdk_server unit tests: 338 passed.

@julianoes julianoes force-pushed the switch-to-nlohmann-json branch from 5d374eb to ae8885b Compare July 12, 2026 23:57
Replace the compiled jsoncpp dependency with the header-only nlohmann/json
(v3.11.3, already present transitively via libevents).

- Parse paths use the non-throwing json::parse(..., allow_exceptions=false)
  with predicate-guarded access to stay abort-free under -fno-exceptions.
- Rework the MavlinkDirect serializer (libmav_receiver) onto
  nlohmann::ordered_json + dump(), which fixes a latent string-escaping bug
  and renders NaN/Inf as null, while preserving field order.
- Migrate mission import and component metadata (parse and serialize) and the
  system tests accordingly.
- Update build wiring, packaging (.pc, CMake config, debian), CI, docs, and
  the mavlink_direct_sender example.
@julianoes julianoes force-pushed the switch-to-nlohmann-json branch from ae8885b to 4ad13c3 Compare July 13, 2026 00:32
PARAM_EXT_{VALUE,SET,ACK}.param_value is declared as char[128] in the
MAVLink definition but the extended-parameter protocol uses it to carry
the raw bytes of a typed value. libmav treats char[] as a NUL-terminated
string (getString/strnlen), so any value containing a zero byte is
truncated on receive, and the bytes are usually not valid UTF-8.

Represent these fields (via an explicit, documented allow-list) as JSON
byte arrays, read as raw bytes so nothing is truncated. The field stays
declared char, so the message CRC_EXTRA is unchanged and wire
compatibility is preserved. The parse direction already accepts a number
array for char[] fields, so this round-trips.

Add a system test covering the round-trip (including an interior NUL) and
adapt the param-change-notification collector, which previously read
param_value as a string.
JonasVautherin
JonasVautherin previously approved these changes Jul 13, 2026

@JonasVautherin JonasVautherin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a small nitpick, feel free to ignore 🙈

Comment thread cpp/third_party/nlohmann_json/CMakeLists.txt Outdated
…d lookup

The binary char[] allow-list used a function-local static
std::set<std::pair<std::string, std::string>>. Such a static has a
runtime destructor that runs at process exit on the main thread, while a
lingering io-context thread may still be serializing a message and
calling find() on it -- the MAVLink fuzzer caught this as a
heap-use-after-free.

Replace the set with a constexpr std::array of std::string_view literals:
no static object with a destructor, and no per-lookup heap allocation of
a temporary pair. Behaviour is unchanged.
@julianoes julianoes merged commit 4451997 into main Jul 14, 2026
54 checks passed
@julianoes julianoes deleted the switch-to-nlohmann-json branch July 14, 2026 01:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants