Repository: xbmc/xbmc
Pull request #26612: [CVariant] Replace rapidjson with nlohmann-json 
Author: login: sundermann
Labels: name: Type: Improvement
description: non-breaking change which improves existing functionality
name: Component: Depends
name: CMake
name: v22 Piers
Created: 2025-04-05T21:58:56Z | Merged: 2025-04-25T17:41:10Z

## Description
<!--- Provide a general summary of your change in the Pull Request title above -->
<!--- Describe your change in detail here. -->
This pull request focuses on replacing the usage of RapidJSON with nlohmann-json across the codebase.

## Motivation and context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
RapidJSON has not had an update for almost 10 years now. It's still actively developed but it seems Tencent is just not interested in tagging a release. See https://github.com/Tencent/rapidjson/issues/2284#issuecomment-2156004291

As a sideeffect, this should also unbreak the webOS release builds on CI which currently is broken due to a mixup of different rapidjson versions from core and addons.

## How has this been tested?
<!--- Please describe in detail how you tested your change. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
Tests pass, macOS, webOS

## What is the effect on users?
<!--- Summarize the effect of this change on Kodi end-users. -->
<!--- If the PR does not have a noticeable impact (e.g., if it only changes documentation), -->
<!--- just leave it empty. Put in more detail the bigger the impact is. -->
<!--- This section may be used for automatic creation of release notes. -->

## Screenshots (if appropriate):

## Types of change
<!--- What type of change does your code introduce? Put an `x` with no space in all the boxes that apply like this: [X] -->
- [ ] **Bug fix** (non-breaking change which fixes an issue)
- [ ] **Clean up** (non-breaking change which removes non-working, unmaintained functionality)
- [x] **Improvement** (non-breaking change which improves existing functionality)
- [ ] **New feature** (non-breaking change which adds functionality)
- [ ] **Breaking change** (fix or feature that will cause existing functionality to change)
- [ ] **Cosmetic change** (non-breaking change that doesn't touch code)
- [ ] **Student submission** (PR was done for educational purposes and will be treated as such)
- [ ] **None of the above** (please explain below)

## Checklist:
<!--- Go over all the following points, and put an `X` with no space in all the boxes that apply like this: [X] -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [x] My code follows the **[Code Guidelines](https://github.com/xbmc/xbmc/blob/master/docs/CODE_GUIDELINES.md)** of this project 
- [ ] My change requires a change to the documentation, either Doxygen or wiki
- [ ] I have updated the documentation accordingly
- [x] I have read the **[Contributing](https://github.com/xbmc/xbmc/blob/master/docs/CONTRIBUTING.md)** document
- [ ] I have added tests to cover my change
- [x] All new and existing tests passed



Diff:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 33f49657c3c6b..82b66d555758e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -116,7 +116,7 @@ dependent_option(ENABLE_INTERNAL_FLATBUFFERS "Enable internal flatbuffers?")
 dependent_option(ENABLE_INTERNAL_FMT "Enable internal fmt?")
 dependent_option(ENABLE_INTERNAL_NFS "Enable internal libnfs?")
 dependent_option(ENABLE_INTERNAL_PCRE2 "Enable internal pcre2?")
-dependent_option(ENABLE_INTERNAL_RapidJSON "Enable internal rapidjson?")
+dependent_option(ENABLE_INTERNAL_NLOHMANNJSON "Enable internal nlohmannjson?")
 dependent_option(ENABLE_INTERNAL_XSLT "Enable internal libxslt?")
 
 # If ENABLE_INTERNAL_FMT is ON, we force ENABLE_INTERNAL_SPDLOG ON as it has a hard
@@ -241,7 +241,7 @@ set(required_deps ASS>=0.15.0
                   Lzo2
                   OpenSSL>=1.1.0
                   PCRE2
-                  RapidJSON>=1.0.2
+                  NlohmannJSON>=3.2.0
                   Spdlog
                   Sqlite3
                   TagLib>=1.9.0
diff --git a/cmake/modules/FindNlohmannJSON.cmake b/cmake/modules/FindNlohmannJSON.cmake
new file mode 100644
index 0000000000000..b6c4cdd8e5ac5
--- /dev/null
+++ b/cmake/modules/FindNlohmannJSON.cmake
@@ -0,0 +1,85 @@
+#.rst:
+# FindNlohmannJSON
+# -----------
+# Finds the NlohmannJSON library
+#
+# This will define the following target:
+#
+#   ${APP_NAME_LC}::NlohmannJSON - The NlohmannJSON library
+#
+
+if(NOT TARGET ${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME})
+  include(cmake/scripts/common/ModuleHelpers.cmake)
+
+  macro(buildnlohmannjson)
+    set(nlohmann_json_VERSION ${${${CMAKE_FIND_PACKAGE_NAME}_MODULE}_VER})
+    set(BUILD_NAME nlohmann_json_build)
+
+    set(CMAKE_ARGS -DJSON_BuildTests=OFF)
+    set(BUILD_BYPRODUCTS ${DEPENDS_PATH}/include/nlohmann/json.hpp)
+
+    BUILD_DEP_TARGET()
+  endmacro()
+
+  set(${CMAKE_FIND_PACKAGE_NAME}_MODULE_LC nlohmann_json)
+
+  SETUP_BUILD_VARS()
+
+  SETUP_FIND_SPECS()
+
+  find_package(nlohmann_json ${CONFIG_${CMAKE_FIND_PACKAGE_NAME}_FIND_SPEC} CONFIG ${SEARCH_QUIET}
+                             HINTS ${DEPENDS_PATH}/share/cmake
+                             ${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG})
+
+  # Check for existing NLOHMANNJSON. If version >= NLOHMANNJSON-VERSION file version, dont build
+  # A corner case, but if a linux/freebsd user WANTS to build internal nlohmann json, build anyway
+  if((nlohmann_json_VERSION VERSION_LESS ${${${CMAKE_FIND_PACKAGE_NAME}_MODULE}_VER} AND ENABLE_INTERNAL_NLOHMANNJSON) OR
+     ((CORE_SYSTEM_NAME STREQUAL linux OR CORE_SYSTEM_NAME STREQUAL freebsd) AND ENABLE_INTERNAL_NLOHMANNJSON))
+    # Build internal nlohmann_json
+    buildnlohmannjson()
+  else()
+    if(TARGET nlohmann_json::nlohmann_json)
+      get_target_property(NLOHMANN_JSON_INCLUDE_DIR nlohmann_json::nlohmann_json INTERFACE_INCLUDE_DIRECTORIES)
+      list(REMOVE_DUPLICATES NLOHMANN_JSON_INCLUDE_DIR)
+    endif()
+  endif()
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(NlohmannJSON
+                                    REQUIRED_VARS NLOHMANN_JSON_INCLUDE_DIR
+                                    VERSION_VAR nlohmann_json_VERSION)
+
+  if(NLOHMANNJSON_FOUND)
+    if(TARGET nlohmann_json::nlohmann_json AND NOT TARGET nlohmann_json_build)
+      add_library(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} ALIAS nlohmann_json::nlohmann_json)
+    else()
+      add_library(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} INTERFACE IMPORTED)
+      set_target_properties(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} PROPERTIES
+                                                                       INTERFACE_INCLUDE_DIRECTORIES "${NLOHMANN_JSON_INCLUDE_DIR}")
+    endif()
+
+    if(TARGET nlohmann_json_build)
+      add_dependencies(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} nlohmann_json_build)
+    endif()
+
+    # Add internal build target when a Multi Config Generator is used
+    # We cant add a dependency based off a generator expression for targeted build types,
+    # https://gitlab.kitware.com/cmake/cmake/-/issues/19467
+    # therefore if the find heuristics only find the library, we add the internal build
+    # target to the project to allow user to manually trigger for any build type they need
+    # in case only a specific build type is actually available (eg Release found, Debug Required)
+    # This is mainly targeted for windows who required different runtime libs for different
+    # types, and they arent compatible
+    if(_multiconfig_generator)
+      if(NOT TARGET nlohmann_json_build)
+        buildnlohmannjson()
+        set_target_properties(nlohmann_json_build PROPERTIES EXCLUDE_FROM_ALL TRUE)
+      endif()
+      add_dependencies(build_internal_depends nlohmann_json_build)
+    endif()
+  else()
+    if(NlohmannJSON_FIND_REQUIRED)
+      message(FATAL_ERROR "NlohmannJSON library not found. You may want to try -DENABLE_INTERNAL_NLOHMANNJSON=ON")
+    endif()
+  endif()
+endif()
diff --git a/cmake/modules/FindRapidJSON.cmake b/cmake/modules/FindRapidJSON.cmake
deleted file mode 100644
index 9c70eef381d38..0000000000000
--- a/cmake/modules/FindRapidJSON.cmake
+++ /dev/null
@@ -1,117 +0,0 @@
-#.rst:
-# FindRapidJSON
-# -----------
-# Finds the RapidJSON library
-#
-# This will define the following target:
-#
-#   ${APP_NAME_LC}::RapidJSON - The RapidJSON library
-#
-
-if(NOT TARGET ${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME})
-  include(cmake/scripts/common/ModuleHelpers.cmake)
-
-  macro(buildrapidjson)
-    set(BUILD_NAME rapidjson_build)
-
-    set(RapidJSON_VERSION ${${${CMAKE_FIND_PACKAGE_NAME}_MODULE}_VER})
-
-    set(patches "${CORE_SOURCE_DIR}/tools/depends/target/rapidjson/001-remove_custom_cxx_flags.patch"
-                "${CORE_SOURCE_DIR}/tools/depends/target/rapidjson/002-cmake-standardise_config_installpath.patch"
-                "${CORE_SOURCE_DIR}/tools/depends/target/rapidjson/003-cmake-removedocs-examples.patch"
-                "${CORE_SOURCE_DIR}/tools/depends/target/rapidjson/004-win-arm64.patch")
-
-    generate_patchcommand("${patches}")
-
-    set(CMAKE_ARGS -DRAPIDJSON_BUILD_DOC=OFF
-                   -DRAPIDJSON_BUILD_EXAMPLES=OFF
-                   -DRAPIDJSON_BUILD_TESTS=OFF
-                   -DRAPIDJSON_BUILD_THIRDPARTY_GTEST=OFF)
-
-    # rapidjson hasnt tagged a release since 2016. Force this for cmake 4.0
-    list(APPEND CMAKE_ARGS -DCMAKE_POLICY_VERSION_MINIMUM=3.10)
-
-    set(BUILD_BYPRODUCTS ${DEPENDS_PATH}/include/rapidjson/rapidjson.h)
-
-    BUILD_DEP_TARGET()
-  endmacro()
-
-  set(${CMAKE_FIND_PACKAGE_NAME}_MODULE_LC rapidjson)
-
-  SETUP_BUILD_VARS()
-
-  SETUP_FIND_SPECS()
-
-  find_package(RapidJSON ${CONFIG_${CMAKE_FIND_PACKAGE_NAME}_FIND_SPEC} CONFIG ${SEARCH_QUIET}
-                         HINTS ${DEPENDS_PATH}/lib/cmake
-                         ${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG})
-
-  # cmake config may not be available (eg Debian libpcre2-dev package)
-  # fallback to pkgconfig for non windows platforms
-  if(NOT RapidJSON_FOUND)
-    find_package(PkgConfig ${SEARCH_QUIET})
-
-    if(PKG_CONFIG_FOUND AND NOT (WIN32 OR WINDOWSSTORE))
-      pkg_check_modules(RapidJSON RapidJSON${PC_${CMAKE_FIND_PACKAGE_NAME}_FIND_SPEC} ${SEARCH_QUIET} IMPORTED_TARGET)
-    endif()
-  endif()
-
-  # Check for existing RAPIDJSON. If version >= RAPIDJSON-VERSION file version, dont build
-  # A corner case, but if a linux/freebsd user WANTS to build internal tinyxml2, build anyway
-  if((RapidJSON_VERSION VERSION_LESS ${${${CMAKE_FIND_PACKAGE_NAME}_MODULE}_VER} AND ENABLE_INTERNAL_RapidJSON) OR
-     ((CORE_SYSTEM_NAME STREQUAL linux OR CORE_SYSTEM_NAME STREQUAL freebsd) AND ENABLE_INTERNAL_RapidJSON))
-    # Build internal rapidjson
-    buildrapidjson()
-  else()
-    if(TARGET PkgConfig::RapidJSON)
-      get_target_property(RAPIDJSON_INCLUDE_DIR PkgConfig::RapidJSON INTERFACE_INCLUDE_DIRECTORIES)
-    elseif(TARGET rapidjson)
-      # RapidJSON cmake config newer than 1.1.0 tag creates a target
-      get_target_property(RAPIDJSON_INCLUDE_DIR rapidjson INTERFACE_INCLUDE_DIRECTORIES)
-    elseif(RAPIDJSON_INCLUDE_DIRS)
-      # RapidJSON cmake config <= 1.1.0 tag does not create a target, only the single variable
-      set(RAPIDJSON_INCLUDE_DIR ${RAPIDJSON_INCLUDE_DIRS})
-    endif()
-  endif()
-
-  include(FindPackageHandleStandardArgs)
-  find_package_handle_standard_args(RapidJSON
-                                    REQUIRED_VARS RAPIDJSON_INCLUDE_DIR
-                                    VERSION_VAR RapidJSON_VERSION)
-
-  if(RapidJSON_FOUND)
-    if(TARGET PkgConfig::RapidJSON AND NOT TARGET rapidjson_build)
-      add_library(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} ALIAS PkgConfig::RapidJSON)
-    elseif(TARGET rapidson AND NOT TARGET rapidjson_build)
-      add_library(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} ALIAS rapidson)
-    else()
-      add_library(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} INTERFACE IMPORTED)
-      set_target_properties(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} PROPERTIES
-                                                                       INTERFACE_INCLUDE_DIRECTORIES "${RAPIDJSON_INCLUDE_DIR}")
-    endif()
-
-    if(TARGET rapidjson_build)
-      add_dependencies(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} rapidjson_build)
-    endif()
-
-    # Add internal build target when a Multi Config Generator is used
-    # We cant add a dependency based off a generator expression for targeted build types,
-    # https://gitlab.kitware.com/cmake/cmake/-/issues/19467
-    # therefore if the find heuristics only find the library, we add the internal build
-    # target to the project to allow user to manually trigger for any build type they need
-    # in case only a specific build type is actually available (eg Release found, Debug Required)
-    # This is mainly targeted for windows who required different runtime libs for different
-    # types, and they arent compatible
-    if(_multiconfig_generator)
-      if(NOT TARGET rapidjson_build)
-        buildrapidjson()
-        set_target_properties(rapidjson_build PROPERTIES EXCLUDE_FROM_ALL TRUE)
-      endif()
-      add_dependencies(build_internal_depends rapidjson_build)
-    endif()
-  else()
-    if(RapidJSON_FIND_REQUIRED)
-      message(FATAL_ERROR "RapidJSON library not found. You may want to try -DENABLE_INTERNAL_RapidJSON=ON")
-    endif()
-  endif()
-endif()
diff --git a/docs/README.Fedora.md b/docs/README.Fedora.md
index 9dbfdfd146b78..25b3fbd31dbca 100644
--- a/docs/README.Fedora.md
+++ b/docs/README.Fedora.md
@@ -71,7 +71,7 @@ If you get a `package not found` type of message with the below command, remove
 
 Install build dependencies:
 ```
-sudo dnf install alsa-lib-devel autoconf automake avahi-compat-libdns_sd-devel avahi-devel bluez-libs-devel bzip2-devel cmake curl dbus-devel exiv2-devel flatbuffers flatbuffers-devel fmt-devel fontconfig-devel freetype-devel fribidi-devel fstrcmp-devel gawk gcc gcc-c++ gettext gettext-devel giflib-devel gperf gtest-devel java-11-openjdk-headless jre lcms2-devel libao-devel libass-devel libbluray-devel libcap-devel libcdio-devel libcec-devel libcurl-devel libidn2-devel libjpeg-turbo-devel libmicrohttpd-devel libmpc-devel libnfs-devel libplist-devel libpng12-devel libsmbclient-devel libtool libtool-ltdl-devel libudev-devel libunistring libunistring-devel libusb1-devel libuuid-devel libva-devel libvdpau-devel libxkbcommon-devel libxml2-devel libXmu-devel libXrandr-devel libxslt-devel libXt-devel lirc-devel lzo-devel make mariadb-devel mesa-libEGL-devel mesa-libGL-devel mesa-libGLU-devel mesa-libGLw-devel mesa-libOSMesa-devel nasm openssl-devel openssl-libs patch pcre2-devel pulseaudio-libs-devel python3-devel python3-pillow rapidjson-devel shairplay-devel spdlog-devel sqlite-devel swig taglib-devel tinyxml-devel tinyxml2-devel trousers-devel uuid-devel zlib-devel
+sudo dnf install alsa-lib-devel autoconf automake avahi-compat-libdns_sd-devel avahi-devel bluez-libs-devel bzip2-devel cmake curl dbus-devel exiv2-devel flatbuffers flatbuffers-devel fmt-devel fontconfig-devel freetype-devel fribidi-devel fstrcmp-devel gawk gcc gcc-c++ gettext gettext-devel giflib-devel gperf gtest-devel java-11-openjdk-headless jre json-devel lcms2-devel libao-devel libass-devel libbluray-devel libcap-devel libcdio-devel libcec-devel libcurl-devel libidn2-devel libjpeg-turbo-devel libmicrohttpd-devel libmpc-devel libnfs-devel libplist-devel libpng12-devel libsmbclient-devel libtool libtool-ltdl-devel libudev-devel libunistring libunistring-devel libusb1-devel libuuid-devel libva-devel libvdpau-devel libxkbcommon-devel libxml2-devel libXmu-devel libXrandr-devel libxslt-devel libXt-devel lirc-devel lzo-devel make mariadb-devel mesa-libEGL-devel mesa-libGL-devel mesa-libGLU-devel mesa-libGLw-devel mesa-libOSMesa-devel nasm openssl-devel openssl-libs patch pcre2-devel pulseaudio-libs-devel python3-devel python3-pillow shairplay-devel spdlog-devel sqlite-devel swig taglib-devel tinyxml-devel tinyxml2-devel trousers-devel uuid-devel zlib-devel
 ```
 
 > [!WARNING]  
diff --git a/docs/README.FreeBSD.md b/docs/README.FreeBSD.md
index 0c8e83b4f5ff3..960b19fee7ce7 100644
--- a/docs/README.FreeBSD.md
+++ b/docs/README.FreeBSD.md
@@ -82,7 +82,7 @@ If you get a `package not found` type of message with the below command, remove
 
 Install build dependencies:
 ```
-sudo pkg install autoconf automake avahi-app binutils cmake curl dbus doxygen e2fsprogs-libuuid enca encodings evdev-proto exiv2 ffmpeg flac flatbuffers font-util fontconfig freetype2 fribidi fstrcmp gawk gettext-tools giflib git glew gmake gmp gnutls googletest gperf gstreamer1-vaapi jpeg-turbo libaacs libass libbdplus libbluray libcapn libcdio libcec libdisplay-info libedit libfmt libgcrypt libgpg-error libidn libinotify libinput libmicrohttpd libnfs libogg libplist librtmp libtool libudev-devd libva libvdpau libvorbis libxkbcommon libxslt lirc lzo2 m4 mariadb-connector-c-3.3.8_1 mesa-libs nasm openjdk21 p8-platform pcre2 pkgconf python3 rapidjson shairplay sndio spdlog sqlite3 swig taglib tiff tinyxml tinyxml2 wayland-protocols waylandpp xf86-input-keyboard xf86-input-mouse xorg-server xrandr zip
+sudo pkg install autoconf automake avahi-app binutils cmake curl dbus doxygen e2fsprogs-libuuid enca encodings evdev-proto exiv2 ffmpeg flac flatbuffers font-util fontconfig freetype2 fribidi fstrcmp gawk gettext-tools giflib git glew gmake gmp gnutls googletest gperf gstreamer1-vaapi jpeg-turbo libaacs libass libbdplus libbluray libcapn libcdio libcec libdisplay-info libedit libfmt libgcrypt libgpg-error libidn libinotify libinput libmicrohttpd libnfs libogg libplist librtmp libtool libudev-devd libva libvdpau libvorbis libxkbcommon libxslt lirc lzo2 m4 mariadb-connector-c-3.3.8_1 mesa-libs nasm nlohmann-json openjdk21 p8-platform pcre2 pkgconf python3 shairplay sndio spdlog sqlite3 swig taglib tiff tinyxml tinyxml2 wayland-protocols waylandpp xf86-input-keyboard xf86-input-mouse xorg-server xrandr zip
 ```
 
 > [!WARNING]  
diff --git a/docs/README.Linux.md b/docs/README.Linux.md
index 3d556df73a3bd..e569f4a04659e 100644
--- a/docs/README.Linux.md
+++ b/docs/README.Linux.md
@@ -80,7 +80,7 @@ The following is the list of packages that are used to build Kodi on Debian/Ubun
 > [!NOTE]  
 > Kodi requires a compiler with C++17 support, i.e. gcc >= 7 or clang >= 5
 
-* autoconf, automake, autopoint, gettext, autotools-dev, cmake, curl, default-jre | openjdk-6-jre | openjdk-7-jre, gawk, gcc (>= 7) | gcc-7, g++ (>= 7) | g++-7, cpp (>= 7) | cpp-7, flatbuffers, gdc, gperf, libasound2-dev | libasound-dev, libass-dev (>= 0.9.8), libavahi-client-dev, libavahi-common-dev, libbluetooth-dev, libbluray-dev, libbz2-dev, libcdio-dev, libcec4-dev | libcec-dev, libp8-platform-dev, libcrossguid-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev | libcurl-dev, libcwiid-dev, libdbus-1-dev, libegl1-mesa-dev, libenca-dev, libexiv2-dev, libflac-dev, libfontconfig-dev, libfmt3-dev | libfmt-dev, libfreetype6-dev, libfribidi-dev, libfstrcmp-dev, libgcrypt-dev, libgif-dev (>= 5.0.5), libgles2-mesa-dev [armel] | libgl1-mesa-dev | libgl-dev, libglew-dev, libglu1-mesa-dev | libglu-dev, libgnutls-dev | libgnutls28-dev, libgpg-error-dev, libgtest-dev, libiso9660-dev, libjpeg-dev, liblcms2-dev, liblirc-dev, libltdl-dev, liblzo2-dev, libmicrohttpd-dev, libmysqlclient-dev, libnfs-dev, libogg-dev, libomxil-bellagio-dev [armel], libpcre2-dev, libplist-dev, libpng12-dev | libpng-dev, libpulse-dev, libshairplay-dev, libsmbclient-dev, libspdlog-dev, libsqlite3-dev, libssl-dev, libtag1-dev (>= 1.8) | libtag1x8, libtiff5-dev | libtiff-dev | libtiff4-dev, libtinyxml-dev, libtinyxml2-dev, libtool, libudev-dev, libunistring-dev, libva-dev, libvdpau-dev, libvorbis-dev, libxkbcommon-dev, libxmu-dev, libxrandr-dev, libxslt1-dev | libxslt-dev, libxt-dev, waylandpp-dev | netcat, wayland-protocols | wipe, lsb-release, meson (>= 0.47.0), nasm (>= 2.14), ninja-build, python3-dev, python3-pil | python-imaging, python-support | python3-minimal, rapidjson-dev, swig, unzip, uuid-dev, zip, zlib1g-dev
+* autoconf, automake, autopoint, gettext, autotools-dev, cmake, curl, default-jre | openjdk-6-jre | openjdk-7-jre, gawk, gcc (>= 7) | gcc-7, g++ (>= 7) | g++-7, cpp (>= 7) | cpp-7, flatbuffers, gdc, gperf, libasound2-dev | libasound-dev, libass-dev (>= 0.9.8), libavahi-client-dev, libavahi-common-dev, libbluetooth-dev, libbluray-dev, libbz2-dev, libcdio-dev, libcec4-dev | libcec-dev, libp8-platform-dev, libcrossguid-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev | libcurl-dev, libcwiid-dev, libdbus-1-dev, libegl1-mesa-dev, libenca-dev, libexiv2-dev, libflac-dev, libfontconfig-dev, libfmt3-dev | libfmt-dev, libfreetype6-dev, libfribidi-dev, libfstrcmp-dev, libgcrypt-dev, libgif-dev (>= 5.0.5), libgles2-mesa-dev [armel] | libgl1-mesa-dev | libgl-dev, libglew-dev, libglu1-mesa-dev | libglu-dev, libgnutls-dev | libgnutls28-dev, libgpg-error-dev, libgtest-dev, libiso9660-dev, libjpeg-dev, liblcms2-dev, liblirc-dev, libltdl-dev, liblzo2-dev, libmicrohttpd-dev, libmysqlclient-dev, libnfs-dev, libogg-dev, libomxil-bellagio-dev [armel], libpcre2-dev, libplist-dev, libpng12-dev | libpng-dev, libpulse-dev, libshairplay-dev, libsmbclient-dev, libspdlog-dev, libsqlite3-dev, libssl-dev, libtag1-dev (>= 1.8) | libtag1x8, libtiff5-dev | libtiff-dev | libtiff4-dev, libtinyxml-dev, libtinyxml2-dev, libtool, libudev-dev, libunistring-dev, libva-dev, libvdpau-dev, libvorbis-dev, libxkbcommon-dev, libxmu-dev, libxrandr-dev, libxslt1-dev | libxslt-dev, libxt-dev, waylandpp-dev | netcat, wayland-protocols | wipe, lsb-release, meson (>= 0.47.0), nasm (>= 2.14), ninja-build, python3-dev, python3-pil | python-imaging, python-support | nlohmann-json3-dev python3-minimal, swig, unzip, uuid-dev, zip, zlib1g-dev
 
 ### 3.1. Build missing dependencies
 Some packages may be missing or outdated in older distributions. Notably `crossguid`, `libfmt`, `libspdlog`, `waylandpp`, `wayland-protocols`, etc. are known to be outdated or missing. Fortunately there is an easy way to build individual dependencies with **[Kodi's unified depends build system](../tools/depends/README.md)**.
@@ -127,7 +127,7 @@ sudo make -C tools/depends/target/waylandpp PREFIX=/usr/local
 > Complete list of dependencies is available **[here](https://github.com/xbmc/xbmc/tree/master/tools/depends/target)**.
 
 ### 3.2. Enable internal dependencies
-Some dependencies can be configured to build before Kodi. That's the case with `flatbuffers`, `crossguid`, `fmt`, `spdlog`, `rapidjson`, `fstrcmp` and `dav1d`. To enable the internal build of a dependency, append `-DENABLE_INTERNAL_<DEPENDENCY_NAME>=ON` to the configure command below. For example, configuring an X11 build with internal `fmt` would become `cmake ../kodi -DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_INTERNAL_FMT=ON` instead of `cmake ../kodi -DCMAKE_INSTALL_PREFIX=/usr/local`.
+Some dependencies can be configured to build before Kodi. That's the case with `flatbuffers`, `crossguid`, `fmt`, `spdlog`, `fstrcmp`, `nlohmann-json` and `dav1d`. To enable the internal build of a dependency, append `-DENABLE_INTERNAL_<DEPENDENCY_NAME>=ON` to the configure command below. For example, configuring an X11 build with internal `fmt` would become `cmake ../kodi -DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_INTERNAL_FMT=ON` instead of `cmake ../kodi -DCMAKE_INSTALL_PREFIX=/usr/local`.
 Internal dependencies that are based on cmake upstream (currently crossguid, ffmpeg, fmt, spdlog) can have their build type overridden by defining `-D<DEPENDENCY_NAME>_BUILD_TYPE=<buildtype>`. Build Type can be one of `Release, RelWithDebInfo, Debug, MinSizeRel`. eg `-DFFMPEG_BUILD_TYPE=RelWithDebInfo`. If not provided, the build type will be the same as the core Kodi project.
 
 > [!NOTE]  
diff --git a/docs/README.Ubuntu.md b/docs/README.Ubuntu.md
index 38801cb07a7ac..c8118e900fa2b 100644
--- a/docs/README.Ubuntu.md
+++ b/docs/README.Ubuntu.md
@@ -126,7 +126,7 @@ If you get a `package not found` type of message with the below command, remove
 
 Install build dependencies manually:
 ```
-sudo apt install debhelper autoconf automake autopoint gettext autotools-dev cmake curl default-jre doxygen gawk gcc gdc gperf libasound2-dev libass-dev libavahi-client-dev libavahi-common-dev libbluetooth-dev libbluray-dev libbz2-dev libcdio-dev libp8-platform-dev libcrossguid-dev libcurl4-openssl-dev libcwiid-dev libdbus-1-dev libdrm-dev libegl1-mesa-dev libenca-dev libexiv2-dev libflac-dev libfmt-dev libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgcrypt-dev libgif-dev libgles2-mesa-dev libgl1-mesa-dev libglu1-mesa-dev libgnutls28-dev libgpg-error-dev libgtest-dev libiso9660-dev libjpeg-dev liblcms2-dev libltdl-dev liblzo2-dev libmicrohttpd-dev libmysqlclient-dev libnfs-dev libogg-dev libpcre2-dev libplist-dev libpng-dev libpulse-dev libshairplay-dev libsmbclient-dev libspdlog-dev libsqlite3-dev libssl-dev libtag1-dev libtiff5-dev libtinyxml-dev libtinyxml2-dev libtool libudev-dev libunistring-dev libva-dev libvdpau-dev libvorbis-dev libxmu-dev libxrandr-dev libxslt1-dev libxt-dev lsb-release meson nasm ninja-build python3-dev python3-pil python3-pip rapidjson-dev swig unzip uuid-dev zip zlib1g-dev
+sudo apt install debhelper autoconf automake autopoint gettext autotools-dev cmake curl default-jre doxygen gawk gcc gdc gperf libasound2-dev libass-dev libavahi-client-dev libavahi-common-dev libbluetooth-dev libbluray-dev libbz2-dev libcdio-dev libp8-platform-dev libcrossguid-dev libcurl4-openssl-dev libcwiid-dev libdbus-1-dev libdrm-dev libegl1-mesa-dev libenca-dev libexiv2-dev libflac-dev libfmt-dev libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgcrypt-dev libgif-dev libgles2-mesa-dev libgl1-mesa-dev libglu1-mesa-dev libgnutls28-dev libgpg-error-dev libgtest-dev libiso9660-dev libjpeg-dev liblcms2-dev libltdl-dev liblzo2-dev libmicrohttpd-dev libmysqlclient-dev libnfs-dev libogg-dev libpcre2-dev libplist-dev libpng-dev libpulse-dev libshairplay-dev libsmbclient-dev libspdlog-dev libsqlite3-dev libssl-dev libtag1-dev libtiff5-dev libtinyxml-dev libtinyxml2-dev libtool libudev-dev libunistring-dev libva-dev libvdpau-dev libvorbis-dev libxmu-dev libxrandr-dev libxslt1-dev libxt-dev lsb-release meson nasm ninja-build nlohmann-json3-dev python3-dev python3-pil python3-pip swig unzip uuid-dev zip zlib1g-dev
 ```
 
 > [!WARNING]  
diff --git a/docs/README.openSUSE.md b/docs/README.openSUSE.md
index 86d1ca62b6584..d3555d94ef0e1 100644
--- a/docs/README.openSUSE.md
+++ b/docs/README.openSUSE.md
@@ -83,7 +83,7 @@ If you get a `package not found` type of message with the below command, remove
 
 Install build dependencies:
 ```
-sudo zypper install alsa-devel autoconf automake bluez-devel boost-devel capi4linux-devel ccache cmake doxygen flac-devel fribidi-devel fstrcmp-devel gcc gcc-c++ gettext-devel giflib-devel glew-devel googletest gperf java-openjdk libass-devel libavahi-devel libbluray-devel libbz2-devel libcap-devel libcap-ng-devel libcdio-devel libcec-devel libcurl-devel libdvdread-devel libexiv2-devel libgudev-1_0-devel libidn2-devel libjasper-devel libjpeg-devel liblcms2-devel libmad-devel libmicrohttpd-devel libmodplug-devel libmpeg2-devel libmysqlclient-devel libnfs-devel libogg-devel libpcap-devel libplist-devel libpng12-devel libpulse-devel libsamplerate-devel libsmbclient-devel libtag-devel libtiff-devel libtool libudev-devel libuuid-devel libva-devel libvdpau-devel libvorbis-devel libXrandr-devel libXrender-devel libxslt-devel lirc-devel lzo-devel make Mesa-libEGL-devel Mesa-libGLESv2-devel Mesa-libGLESv3-devel nasm patch pcre2-devel python3-devel python3-Pillow randrproto-devel renderproto-devel shairplay-devel sqlite3-devel swig tinyxml-devel tinyxml2-devel
+sudo zypper install alsa-devel autoconf automake bluez-devel boost-devel capi4linux-devel ccache cmake doxygen flac-devel fribidi-devel fstrcmp-devel gcc gcc-c++ gettext-devel giflib-devel glew-devel googletest gperf java-openjdk libass-devel libavahi-devel libbluray-devel libbz2-devel libcap-devel libcap-ng-devel libcdio-devel libcec-devel libcurl-devel libdvdread-devel libexiv2-devel libgudev-1_0-devel libidn2-devel libjasper-devel libjpeg-devel liblcms2-devel libmad-devel libmicrohttpd-devel libmodplug-devel libmpeg2-devel libmysqlclient-devel libnfs-devel libogg-devel libpcap-devel libplist-devel libpng12-devel libpulse-devel libsamplerate-devel libsmbclient-devel libtag-devel libtiff-devel libtool libudev-devel libuuid-devel libva-devel libvdpau-devel libvorbis-devel libXrandr-devel libXrender-devel libxslt-devel lirc-devel lzo-devel make Mesa-libEGL-devel Mesa-libGLESv2-devel Mesa-libGLESv3-devel nasm nlohmann_json patch pcre2-devel python3-devel python3-Pillow randrproto-devel renderproto-devel shairplay-devel sqlite3-devel swig tinyxml-devel tinyxml2-devel
 ```
 
 > [!WARNING]  
@@ -100,7 +100,7 @@ sudo zypper install libgbm-devel libinput-devel libxkbcommon-devel
 ```
 
 > [!WARNING]  
-> Fedora repositories don't have install candidates for `libfmt`, `rapidjson` and `waylandpp`. See **[build missing dependencies manually](#31-build-missing-dependencies)** section before you proceed.
+> Fedora repositories don't have install candidates for `libfmt`, and `waylandpp`. See **[build missing dependencies manually](#31-build-missing-dependencies)** section before you proceed.
 
 Optional packages that you might want to install for extra functionality (generating doxygen documentation, for instance):
 ```
@@ -131,11 +131,10 @@ Change to Kodi's source code directory:
 cd $HOME/kodi
 ```
 
-Build and install missing dependencies from repositories (*flatbuffers*, *libfmt*, *rapidjson* and *waylandpp*):
+Build and install missing dependencies from repositories (*flatbuffers*, *libfmt*, and *waylandpp*):
 ```
 sudo make -C tools/depends/target/flatbuffers PREFIX=/usr/local
 sudo make -C tools/depends/target/libfmt PREFIX=/usr/local
-sudo make -C tools/depends/target/rapidjson PREFIX=/usr/local
 sudo make -C tools/depends/target/waylandpp PREFIX=/usr/local
 ```
 
diff --git a/tools/depends/target/nlohmann_json/Makefile b/tools/depends/target/nlohmann_json/Makefile
new file mode 100644
index 0000000000000..6d900810933ca
--- /dev/null
+++ b/tools/depends/target/nlohmann_json/Makefile
@@ -0,0 +1,22 @@
+include ../../Makefile.include NLOHMANN_JSON-VERSION ../../download-files.include
+DEPS = Makefile NLOHMANN_JSON-VERSION ../../download-files.include
+
+CMAKE_OPTIONS=-DJSON_BuildTests=OFF
+
+all: .installed-$(PLATFORM)
+
+$(PLATFORM): $(DEPS) | $(TARBALLS_LOCATION)/$(ARCHIVE).$(HASH_TYPE)
+	rm -rf $(PLATFORM)/*; mkdir -p $(PLATFORM)/build
+	cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
+	cd $(PLATFORM)/build; $(CMAKE) $(CMAKE_OPTIONS) ..
+
+.installed-$(PLATFORM): $(PLATFORM)
+	$(MAKE) -C $(PLATFORM)/build install
+	touch $@
+
+clean:
+	$(MAKE) -C $(PLATFORM)/build clean
+	rm -f .installed-$(PLATFORM)
+
+distclean::
+	rm -rf $(PLATFORM) .installed-$(PLATFORM)
diff --git a/tools/depends/target/nlohmann_json/NLOHMANN_JSON-VERSION b/tools/depends/target/nlohmann_json/NLOHMANN_JSON-VERSION
new file mode 100644
index 0000000000000..ab9a2cdfa7ba7
--- /dev/null
+++ b/tools/depends/target/nlohmann_json/NLOHMANN_JSON-VERSION
@@ -0,0 +1,5 @@
+LIBNAME=json
+VERSION=3.12.0
+ARCHIVE=$(LIBNAME)-$(VERSION).tar.xz
+SHA512=07a47d478bb054367cede3b7864a19a673f486aa3286cc2fd176e5947905354ce61f81e75cad9f2a17d09c1856b973772e24c350bdd7a18b7d77a6307523573c
+BYPRODUCT=json.hpp
diff --git a/tools/depends/target/rapidjson/001-remove_custom_cxx_flags.patch b/tools/depends/target/rapidjson/001-remove_custom_cxx_flags.patch
deleted file mode 100644
index c923c763801d6..0000000000000
--- a/tools/depends/target/rapidjson/001-remove_custom_cxx_flags.patch
+++ /dev/null
@@ -1,26 +0,0 @@
---- a/CMakeLists.txt
-+++ b/CMakeLists.txt
-@@ -50,7 +50,6 @@
- endif(CCACHE_FOUND)
- 
- if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
--    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror")
-     if (RAPIDJSON_BUILD_CXX11)
-         if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0")
-             set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
-@@ -73,7 +72,6 @@
-         endif()
-     endif()
- elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
--    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror -Wno-missing-field-initializers")
-     if (RAPIDJSON_BUILD_CXX11)
-         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
-     endif()
-@@ -88,7 +86,6 @@
-         endif()
-     endif()
- elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
--    add_definitions(-D_CRT_SECURE_NO_WARNINGS=1)
-     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
- endif()
- 
diff --git a/tools/depends/target/rapidjson/002-cmake-standardise_config_installpath.patch b/tools/depends/target/rapidjson/002-cmake-standardise_config_installpath.patch
deleted file mode 100644
index 937d19e8e367a..0000000000000
--- a/tools/depends/target/rapidjson/002-cmake-standardise_config_installpath.patch
+++ /dev/null
@@ -1,15 +0,0 @@
---- a/CMakeLists.txt
-+++ b/CMakeLists.txt
-@@ -94,11 +94,7 @@
- SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE STRING "Directory where lib will install")
- SET(DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}" CACHE PATH "Path to the documentation")
- 
--IF(UNIX OR CYGWIN)
--    SET(_CMAKE_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake/${PROJECT_NAME}")
--ELSEIF(WIN32)
--    SET(_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/cmake")
--ENDIF()
-+SET(_CMAKE_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake/${PROJECT_NAME}")
- SET(CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" CACHE PATH "The directory cmake fiels are installed in")
- 
- include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
diff --git a/tools/depends/target/rapidjson/003-cmake-removedocs-examples.patch b/tools/depends/target/rapidjson/003-cmake-removedocs-examples.patch
deleted file mode 100644
index 4bf06da59c4c5..0000000000000
--- a/tools/depends/target/rapidjson/003-cmake-removedocs-examples.patch
+++ /dev/null
@@ -1,28 +0,0 @@
---- a/CMakeLists.txt
-+++ b/CMakeLists.txt
-@@ -134,14 +134,17 @@
-       COMPONENT pkgconfig)
- ENDIF()
- 
-+if(RAPIDJSON_BUILD_DOC)
- install(FILES readme.md
-         DESTINATION "${DOC_INSTALL_DIR}"
-         COMPONENT doc)
-+endif()
- 
- install(DIRECTORY include/rapidjson
-     DESTINATION "${INCLUDE_INSTALL_DIR}"
-     COMPONENT dev)
- 
-+if(RAPIDJSON_BUILD_EXAMPLES)
- install(DIRECTORY example/
-     DESTINATION "${DOC_INSTALL_DIR}/examples"
-     COMPONENT examples
-@@ -150,6 +153,7 @@
-     PATTERN "CMakeFiles" EXCLUDE
-     PATTERN "Makefile" EXCLUDE
-     PATTERN "cmake_install.cmake" EXCLUDE)
-+endif()
- 
- # Provide config and version files to be used by other applications
- # ===============================
diff --git a/tools/depends/target/rapidjson/004-win-arm64.patch b/tools/depends/target/rapidjson/004-win-arm64.patch
deleted file mode 100644
index 2d045dcaf6238..0000000000000
--- a/tools/depends/target/rapidjson/004-win-arm64.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/include/rapidjson/rapidjson.h
-+++ b/include/rapidjson/rapidjson.h
-@@ -236,7 +236,7 @@
- #    define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
- #  elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
- #    define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
--#  elif defined(_MSC_VER) && defined(_M_ARM)
-+#  elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
- #    define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
- #  elif defined(RAPIDJSON_DOXYGEN_RUNNING)
- #    define RAPIDJSON_ENDIAN
diff --git a/tools/depends/target/rapidjson/Makefile b/tools/depends/target/rapidjson/Makefile
deleted file mode 100644
index 1e0a2d2e752b8..0000000000000
--- a/tools/depends/target/rapidjson/Makefile
+++ /dev/null
@@ -1,62 +0,0 @@
--include ../../Makefile.include
-include RAPIDJSON-VERSION
-DEPS = Makefile RAPIDJSON-VERSION 001-remove_custom_cxx_flags.patch ../../download-files.include \
-                                  002-cmake-standardise_config_installpath.patch \
-                                  003-cmake-removedocs-examples.patch \
-                                  004-win-arm64.patch
-
-CMAKE_OPTIONS=-DRAPIDJSON_HAS_STDSTRING=ON -DRAPIDJSON_BUILD_DOC=OFF -DRAPIDJSON_BUILD_EXAMPLES=OFF -DRAPIDJSON_BUILD_TESTS=OFF -DRAPIDJSON_BUILD_THIRDPARTY_GTEST=OFF
-
-ifeq ($(CROSS_COMPILING), yes)
-  DEPS += ../../Makefile.include
-else
-  CXXFLAGS += -std=c++11
-  ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
-
-  ifeq ($(PLATFORM),)
-    PLATFORM = native
-    TARBALLS_LOCATION = $(ROOT_DIR)
-    BASE_URL := http://mirrors.kodi.tv/build-deps/sources
-    RETRIEVE_TOOL := curl
-    RETRIEVE_TOOL_FLAGS := -LsS --create-dirs --retry 10 --retry-connrefused -o
-    ARCHIVE_TOOL := tar
-    ARCHIVE_TOOL_FLAGS := --strip-components=1 -xf
-    HASH_TOOL := sha512sum
-    HASH_TOOL_FLAGS = -c --status
-    CMAKE := cmake
-    CMAKE_OPTIONS := -DCMAKE_INSTALL_PREFIX=$(PREFIX) $(CMAKE_OPTIONS)
-  endif
-endif
-
-include ../../download-files.include
-
-all: .installed-$(PLATFORM)
-
-
-$(PLATFORM): $(DEPS) | $(TARBALLS_LOCATION)/$(ARCHIVE).$(HASH_TYPE)
-ifeq ($(PREFIX),)
-	@echo
-	@echo "ERROR: please set PREFIX to the kodi install path e.g. $(MAKE) PREFIX=/usr/local"
-	@exit 1
-endif
-	rm -rf $(PLATFORM); mkdir -p $(PLATFORM)
-	cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
-	cd $(PLATFORM); rm -rf build; mkdir -p build
-	cd $(PLATFORM); patch -p1 -i ../001-remove_custom_cxx_flags.patch
-	cd $(PLATFORM); patch -p1 -i ../002-cmake-standardise_config_installpath.patch
-	cd $(PLATFORM); patch -p1 -i ../003-cmake-removedocs-examples.patch
-	cd $(PLATFORM); patch -p1 -i ../004-win-arm64.patch
-	cd $(PLATFORM)/build; $(CMAKE) $(CMAKE_OPTIONS) ..
-
-.installed-$(PLATFORM): $(PLATFORM)
-	$(MAKE) -C $(PLATFORM)/build
-	$(MAKE) -C $(PLATFORM)/build install
-	rm -rf $(PREFIX)/share/doc/RapidJSON
-	touch $@
-
-clean:
-	$(MAKE) -C $(PLATFORM)/build clean
-	rm -f .installed-$(PLATFORM)
-
-distclean:
-	rm -rf $(PLATFORM) .installed-$(PLATFORM)
diff --git a/tools/depends/target/rapidjson/RAPIDJSON-VERSION b/tools/depends/target/rapidjson/RAPIDJSON-VERSION
deleted file mode 100644
index 120e868fca6c3..0000000000000
--- a/tools/depends/target/rapidjson/RAPIDJSON-VERSION
+++ /dev/null
@@ -1,4 +0,0 @@
-LIBNAME=rapidjson
-VERSION=1.1.0
-ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
-SHA512=2e82a4bddcd6c4669541f5945c2d240fb1b4fdd6e239200246d3dd50ce98733f0a4f6d3daa56f865d8c88779c036099c52a9ae85d47ad263686b68a88d832dff
diff --git a/xbmc/platform/android/activity/XBMCApp.cpp b/xbmc/platform/android/activity/XBMCApp.cpp
index 615647604c911..debda400d175e 100644
--- a/xbmc/platform/android/activity/XBMCApp.cpp
+++ b/xbmc/platform/android/activity/XBMCApp.cpp
@@ -42,6 +42,7 @@
 #include "settings/Settings.h"
 #include "settings/SettingsComponent.h"
 #include "threads/Event.h"
+#include "utils/JSONVariantParser.h"
 #include "utils/StringUtils.h"
 #include "utils/TimeUtils.h"
 #include "utils/URIUtils.h"
@@ -102,7 +103,6 @@
 #include <crossguid/guid.hpp>
 #include <dlfcn.h>
 #include <jni.h>
-#include <rapidjson/document.h>
 #include <unistd.h>
 
 #define ACTION_XBMC_RESUME "android.intent.XBMC_RESUME"
@@ -1071,17 +1071,18 @@ bool CXBMCApp::StartActivity(const std::string& package,
 
   if (!extras.empty())
   {
-    rapidjson::Document doc;
-    doc.Parse(extras.c_str());
-    if (!doc.IsArray())
+    CVariant doc;
+    CJSONVariantParser::Parse(extras, doc);
+    if (!doc.isArray())
     {
       CLog::LogF(LOGDEBUG, "Invalid intent extras format: Needs to be an array");
       return false;
     }
 
-    for (auto& e : doc.GetArray())
+    for (auto it = doc.begin_array(); it != doc.end_array(); ++it)
     {
-      if (!e.IsObject() || !e.HasMember("type") || !e.HasMember("key") || !e.HasMember("value"))
+      CVariant& e = *it;
+      if (!e.isObject() || !e.isMember("type") || !e.isMember("key") || !e.isMember("value"))
       {
         CLog::LogF(LOGDEBUG, "Invalid intent extras value format");
         continue;
@@ -1089,12 +1090,12 @@ bool CXBMCApp::StartActivity(const std::string& package,
 
       if (e["type"] == "string")
       {
-        newIntent.putExtra(e["key"].GetString(), e["value"].GetString());
-        CLog::LogF(LOGDEBUG, "Putting extra key: {}, value: {}", e["key"].GetString(),
-                   e["value"].GetString());
+        newIntent.putExtra(e["key"].asString(), e["value"].asString());
+        CLog::LogF(LOGDEBUG, "Putting extra key: {}, value: {}", e["key"].asString(),
+                   e["value"].asString());
       }
       else
-        CLog::LogF(LOGDEBUG, "Intent extras data type ({}) not implemented", e["type"].GetString());
+        CLog::LogF(LOGDEBUG, "Intent extras data type ({}) not implemented", e["type"].asString());
     }
   }
 
diff --git a/xbmc/utils/JSONVariantParser.cpp b/xbmc/utils/JSONVariantParser.cpp
index 0790d53680cf4..b4681f8a27ac7 100644
--- a/xbmc/utils/JSONVariantParser.cpp
+++ b/xbmc/utils/JSONVariantParser.cpp
@@ -8,27 +8,28 @@
 
 #include "JSONVariantParser.h"
 
-#include <rapidjson/reader.h>
+#include <nlohmann/json.hpp>
 
-class CJSONVariantParserHandler
+class CJSONVariantParserHandler : public nlohmann::json::json_sax_t
 {
 public:
   explicit CJSONVariantParserHandler(CVariant& parsedObject);
 
-  bool Null();
-  bool Bool(bool b);
-  bool Int(int i);
-  bool Uint(unsigned u);
-  bool Int64(int64_t i);
-  bool Uint64(uint64_t u);
-  bool Double(double d);
-  bool RawNumber(const char* str, rapidjson::SizeType length, bool copy);
-  bool String(const char* str, rapidjson::SizeType length, bool copy);
-  bool StartObject();
-  bool Key(const char* str, rapidjson::SizeType length, bool copy);
-  bool EndObject(rapidjson::SizeType memberCount);
-  bool StartArray();
-  bool EndArray(rapidjson::SizeType elementCount);
+  bool null() override;
+  bool binary(binary_t& b) override;
+  bool boolean(bool b) override;
+  bool number_integer(number_integer_t i) override;
+  bool number_unsigned(number_unsigned_t u) override;
+  bool number_float(number_float_t d, const string_t& s) override;
+  bool string(std::string& str) override;
+  bool start_object(std::size_t elements) override;
+  bool key(string_t& str) override;
+  bool end_object() override;
+  bool start_array(std::size_t elements) override;
+  bool end_array() override;
+  bool parse_error(std::size_t position,
+                   const std::string& last_token,
+                   const nlohmann::json::exception& ex) override;
 
 private:
   template <typename... TArgs>
@@ -61,7 +62,7 @@ CJSONVariantParserHandler::CJSONVariantParserHandler(CVariant& parsedObject)
   : m_parsedObject(parsedObject), m_parse(), m_key()
 { }
 
-bool CJSONVariantParserHandler::Null()
+bool CJSONVariantParserHandler::null()
 {
   PushObject(CVariant::ConstNullVariant);
   PopObject();
@@ -69,81 +70,78 @@ bool CJSONVariantParserHandler::Null()
   return true;
 }
 
-bool CJSONVariantParserHandler::Bool(bool b)
+bool CJSONVariantParserHandler::boolean(bool b)
 {
   return Primitive(b);
 }
 
-bool CJSONVariantParserHandler::Int(int i)
+bool CJSONVariantParserHandler::number_integer(number_integer_t i)
 {
   return Primitive(i);
 }
 
-bool CJSONVariantParserHandler::Uint(unsigned u)
+bool CJSONVariantParserHandler::number_unsigned(number_unsigned_t u)
 {
   return Primitive(u);
 }
 
-bool CJSONVariantParserHandler::Int64(int64_t i)
-{
-  return Primitive(i);
-}
-
-bool CJSONVariantParserHandler::Uint64(uint64_t u)
-{
-  return Primitive(u);
-}
-
-bool CJSONVariantParserHandler::Double(double d)
+bool CJSONVariantParserHandler::number_float(number_float_t d, const string_t& s)
 {
   return Primitive(d);
 }
 
-bool CJSONVariantParserHandler::RawNumber(const char* str, rapidjson::SizeType length, bool copy)
+bool CJSONVariantParserHandler::string(std::string& str)
 {
-  return Primitive(str, length);
+  return Primitive(str);
 }
 
-bool CJSONVariantParserHandler::String(const char* str, rapidjson::SizeType length, bool copy)
+bool CJSONVariantParserHandler::binary(binary_t& b)
 {
-  return Primitive(str, length);
+  return true;
 }
 
-bool CJSONVariantParserHandler::StartObject()
+bool CJSONVariantParserHandler::start_object(std::size_t elements)
 {
   PushObject(CVariant::VariantTypeObject);
 
   return true;
 }
 
-bool CJSONVariantParserHandler::Key(const char* str, rapidjson::SizeType length, bool copy)
+bool CJSONVariantParserHandler::key(std::string& str)
 {
-  m_key = std::string(str, 0, length);
+  m_key = str;
 
   return true;
 }
 
-bool CJSONVariantParserHandler::EndObject(rapidjson::SizeType memberCount)
+bool CJSONVariantParserHandler::end_object()
 {
   PopObject();
 
   return true;
 }
 
-bool CJSONVariantParserHandler::StartArray()
+bool CJSONVariantParserHandler::start_array(std::size_t elements)
 {
   PushObject(CVariant::VariantTypeArray);
 
   return true;
 }
 
-bool CJSONVariantParserHandler::EndArray(rapidjson::SizeType elementCount)
+bool CJSONVariantParserHandler::end_array()
 {
   PopObject();
 
   return true;
 }
 
+bool CJSONVariantParserHandler::parse_error(std::size_t position,
+                                            const std::string& last_token,
+                                            const nlohmann::json::exception& ex)
+{
+  return false;
+}
+
 void CJSONVariantParserHandler::PushObject(CVariant variant)
 {
   const auto variant_type = variant.type();
@@ -201,16 +199,8 @@ bool CJSONVariantParser::Parse(const char* json, CVariant& data)
   if (json == nullptr)
     return false;
 
-  rapidjson::Reader reader;
-  rapidjson::StringStream stringStream(json);
-
   CJSONVariantParserHandler handler(data);
-  // use kParseIterativeFlag to eliminate possible stack overflow
-  // from json parsing via reentrant calls
-  if (reader.Parse<rapidjson::kParseIterativeFlag>(stringStream, handler))
-    return true;
-
-  return false;
+  return nlohmann::json::sax_parse(json, &handler);
 }
 
 bool CJSONVariantParser::Parse(const std::string& json, CVariant& data)
diff --git a/xbmc/utils/JSONVariantWriter.cpp b/xbmc/utils/JSONVariantWriter.cpp
index b48a8ae151f2b..feb0a424045bf 100644
--- a/xbmc/utils/JSONVariantWriter.cpp
+++ b/xbmc/utils/JSONVariantWriter.cpp
@@ -10,83 +10,76 @@
 
 #include "utils/Variant.h"
 
-#include <rapidjson/prettywriter.h>
-#include <rapidjson/stringbuffer.h>
-#include <rapidjson/writer.h>
+#include <nlohmann/json.hpp>
 
-template<class TWriter>
-bool InternalWrite(TWriter& writer, const CVariant &value)
+void InternalWrite(nlohmann::json& writer, const CVariant& value)
 {
   switch (value.type())
   {
   case CVariant::VariantTypeInteger:
-    return writer.Int64(value.asInteger());
-
+    writer = value.asInteger();
+    break;
   case CVariant::VariantTypeUnsignedInteger:
-    return writer.Uint64(value.asUnsignedInteger());
-
+    writer = value.asUnsignedInteger();
+    break;
   case CVariant::VariantTypeDouble:
-    return writer.Double(value.asDouble());
-
+    writer = value.asDouble();
+    break;
   case CVariant::VariantTypeBoolean:
-    return writer.Bool(value.asBoolean());
-
+    writer = value.asBoolean();
+    break;
   case CVariant::VariantTypeString:
-    return writer.String(value.c_str(), value.size());
-
+    writer = std::string(value.c_str(), value.size());
+    break;
   case CVariant::VariantTypeArray:
-    if (!writer.StartArray())
-      return false;
+    writer = nlohmann::json::array();
 
     for (CVariant::const_iterator_array itr = value.begin_array(); itr != value.end_array(); ++itr)
     {
-      if (!InternalWrite(writer, *itr))
-        return false;
+      nlohmann::json& o = writer.emplace_back();
+      InternalWrite(o, *itr);
     }
 
-    return writer.EndArray(value.size());
-
+    break;
   case CVariant::VariantTypeObject:
-    if (!writer.StartObject())
-      return false;
+    writer = nlohmann::json::object();
 
     for (CVariant::const_iterator_map itr = value.begin_map(); itr != value.end_map(); ++itr)
     {
-      if (!writer.Key(itr->first.c_str()) ||
-        !InternalWrite(writer, itr->second))
-        return false;
+      nlohmann::json& o = writer[itr->first];
+      InternalWrite(o, itr->second);
     }
 
-    return writer.EndObject(value.size());
+    break;
 
   case CVariant::VariantTypeConstNull:
   case CVariant::VariantTypeNull:
   default:
-    return writer.Null();
+    writer = nullptr;
+    break;
   }
-
-  return false;
 }
 
 bool CJSONVariantWriter::Write(const CVariant &value, std::string& output, bool compact)
 {
-  rapidjson::StringBuffer stringBuffer;
-  if (compact)
+  try
   {
-    rapidjson::Writer<rapidjson::StringBuffer> writer(stringBuffer);
+    nlohmann::json json;
+    InternalWrite(json, value);
 
-    if (!InternalWrite(writer, value) || !writer.IsComplete())
-      return false;
+    if (compact)
+    {
+      output = json.dump();
+    }
+    else
+    {
+      output = json.dump(1, '\t');
+    }
   }
-  else
+  catch (nlohmann::json::exception& e)
   {
-    rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(stringBuffer);
-    writer.SetIndent('\t', 1);
-
-    if (!InternalWrite(writer, value) || !writer.IsComplete())
-      return false;
+    return false;
   }
 
-  output = stringBuffer.GetString();
   return true;
 }


Reviews:
user: login: fuzzard
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T02:44:22Z
user: login: fuzzard
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T02:44:39Z
user: login: fuzzard
body: Other than a couple minors. things look ok for me on the build side.

I'll leave others to comment on the actual c++ side, but a minor scroll through it looked reasonable to me. Theres a fair bit of use of CJSONVariantPaser in the codebase, but the tests seem to be passing, so in theory it should be ok i guess.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T02:51:37Z
user: login: sundermann
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T18:29:52Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR replaces RapidJSON with nlohmann-json to modernize JSON handling and fix build issues on various platforms. Key changes include:
- Replacing RapidJSON API calls with nlohmann-json calls in JSONVariantWriter.cpp and JSONVariantParser.cpp.
- Updating the Android activity extras parsing to use the new parser.
- Revising dependency installation documentation to reference nlohmann-json instead of RapidJSON.

### Reviewed Changes

Copilot reviewed 8 out of 19 changed files in this pull request and generated 1 comment.

| File | Description |
| ---- | ----------- |
| xbmc/utils/JSONVariantWriter.cpp | Updated JSON writer functions to convert CVariant values into nlohmann::json objects/values. |
| xbmc/utils/JSONVariantParser.cpp | Adapted the SAX parser handler to match the nlohmann::json SAX interface. |
| xbmc/platform/android/activity/XBMCApp.cpp | Modified intent extras parsing to use the new nlohmann::json-based variant parser. |
| docs/README.* | Updated build dependency instructions to include nlohmann-json and remove RapidJSON. |

<details>
<summary>Files not reviewed (11)</summary>

* **CMakeLists.txt**: Language not supported
* **cmake/modules/FindNlohmannJSON.cmake**: Language not supported
* **cmake/modules/FindRapidJSON.cmake**: Language not supported
* **tools/depends/target/nlohmann_json/Makefile**: Language not supported
* **tools/depends/target/nlohmann_json/NLOHMANN_JSON-VERSION**: Language not supported
* **tools/depends/target/rapidjson/001-remove_custom_cxx_flags.patch**: Language not supported
* **tools/depends/target/rapidjson/002-cmake-standardise_config_installpath.patch**: Language not supported
* **tools/depends/target/rapidjson/003-cmake-removedocs-examples.patch**: Language not supported
* **tools/depends/target/rapidjson/004-win-arm64.patch**: Language not supported
* **tools/depends/target/rapidjson/Makefile**: Language not supported
* **tools/depends/target/rapidjson/RAPIDJSON-VERSION**: Language not supported
</details>

<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**xbmc/utils/JSONVariantParser.cpp:97**
* The binary handler currently returns true without processing the binary data. If the application is expected to handle binary JSON data, consider implementing appropriate conversion logic or document why binary data is safely ignored.
```
bool CJSONVariantParserHandler::binary(binary_t& b) override { return true; }
```
</details>


state: COMMENTED
author_association: NONE
submitted_at: 2025-04-19T16:01:20Z
user: login: neo1973
body: Those two changes reduce the time to ~180ms for my data set.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-20T14:22:02Z
user: login: neo1973
body: Jenkins is now green as well :+1: 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-22T22:30:36Z
user: login: phunkyfish
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T08:02:15Z
user: login: sundermann
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T21:38:12Z

Review comments:
diff_hunk: @@ -115,7 +115,7 @@ dependent_option(ENABLE_INTERNAL_FLATBUFFERS "Enable internal flatbuffers?")
 dependent_option(ENABLE_INTERNAL_FMT "Enable internal fmt?")
 dependent_option(ENABLE_INTERNAL_NFS "Enable internal libnfs?")
 dependent_option(ENABLE_INTERNAL_PCRE2 "Enable internal pcre2?")
-dependent_option(ENABLE_INTERNAL_RapidJSON "Enable internal rapidjson?")
+dependent_option(ENABLE_INTERNAL_NlohmannJSON "Enable internal nlohmannjson?")
path: CMakeLists.txt
user: login: fuzzard
body: I would make this all uppercase. It makes things easier internally to rely on that standard, and was changed at some point in time prior to my involvement to be the defacto
created_at: 2025-04-07T02:44:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -240,7 +240,7 @@ set(required_deps ASS>=0.15.0
                   Lzo2
                   OpenSSL>=1.1.0
                   PCRE2
-                  RapidJSON>=1.0.2
+                  NlohmannJSON>=3.2.0
path: CMakeLists.txt
user: login: fuzzard
body: Query, is the 3.2 a hard requirement? Just confirming.
created_at: 2025-04-07T02:44:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -240,7 +240,7 @@ set(required_deps ASS>=0.15.0
                   Lzo2
                   OpenSSL>=1.1.0
                   PCRE2
-                  RapidJSON>=1.0.2
+                  NlohmannJSON>=3.2.0
path: CMakeLists.txt
user: login: sundermann
body: Yes, `sax_parse` is available from 3.2.0 which was released in 2018
created_at: 2025-04-07T18:29:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -10,83 +10,82 @@
 
 #include "utils/Variant.h"
 
-#include <rapidjson/prettywriter.h>
-#include <rapidjson/stringbuffer.h>
-#include <rapidjson/writer.h>
+#include <nlohmann/json.hpp>
 
-template<class TWriter>
-bool InternalWrite(TWriter& writer, const CVariant &value)
+bool InternalWrite(nlohmann::json& writer, const CVariant& value)
 {
   switch (value.type())
   {
   case CVariant::VariantTypeInteger:
-    return writer.Int64(value.asInteger());
-
+    writer = value.asInteger();
+    return true;
   case CVariant::VariantTypeUnsignedInteger:
-    return writer.Uint64(value.asUnsignedInteger());
-
+    writer = value.asUnsignedInteger();
+    return true;
   case CVariant::VariantTypeDouble:
-    return writer.Double(value.asDouble());
-
+    writer = value.asDouble();
+    return true;
   case CVariant::VariantTypeBoolean:
-    return writer.Bool(value.asBoolean());
-
+    writer = value.asBoolean();
+    return true;
   case CVariant::VariantTypeString:
-    return writer.String(value.c_str(), value.size());
-
+    writer = std::string(value.c_str(), value.size());
+    return true;
   case CVariant::VariantTypeArray:
-    if (!writer.StartArray())
-      return false;
+    writer = nlohmann::json::array();
 
     for (CVariant::const_iterator_array itr = value.begin_array(); itr != value.end_array(); ++itr)
     {
-      if (!InternalWrite(writer, *itr))
+      auto o = nlohmann::json::object();
path: xbmc/utils/JSONVariantWriter.cpp
user: login: Copilot
body: When processing array elements, initializing with nlohmann::json::object() may be too restrictive if the element is not an object. Consider using a default-constructed nlohmann::json (i.e. 'nlohmann::json o;') so that any type can be assigned.
```suggestion
      nlohmann::json o;
```
created_at: 2025-04-19T16:01:20Z
author_association: NONE
diff_hunk: @@ -10,83 +10,84 @@
 
 #include "utils/Variant.h"
 
-#include <rapidjson/prettywriter.h>
-#include <rapidjson/stringbuffer.h>
-#include <rapidjson/writer.h>
+#include <nlohmann/json.hpp>
 
-template<class TWriter>
-bool InternalWrite(TWriter& writer, const CVariant &value)
+bool InternalWrite(nlohmann::json& writer, const CVariant& value)
 {
   switch (value.type())
   {
   case CVariant::VariantTypeInteger:
-    return writer.Int64(value.asInteger());
-
+    writer = value.asInteger();
+    return true;
   case CVariant::VariantTypeUnsignedInteger:
-    return writer.Uint64(value.asUnsignedInteger());
-
+    writer = value.asUnsignedInteger();
+    return true;
   case CVariant::VariantTypeDouble:
-    return writer.Double(value.asDouble());
-
+    writer = value.asDouble();
+    return true;
   case CVariant::VariantTypeBoolean:
-    return writer.Bool(value.asBoolean());
-
+    writer = value.asBoolean();
+    return true;
   case CVariant::VariantTypeString:
-    return writer.String(value.c_str(), value.size());
-
+    writer = std::string(value.c_str(), value.size());
+    return true;
   case CVariant::VariantTypeArray:
-    if (!writer.StartArray())
-      return false;
+    writer = nlohmann::json::array();
 
     for (CVariant::const_iterator_array itr = value.begin_array(); itr != value.end_array(); ++itr)
     {
-      if (!InternalWrite(writer, *itr))
+      nlohmann::json o;
+      if (!InternalWrite(o, *itr))
         return false;
+      writer.emplace_back(o);
path: xbmc/utils/JSONVariantWriter.cpp
user: login: neo1973
body: ```suggestion
      nlohmann::json& o = writer.emplace_back();
      if (!InternalWrite(o, *itr))
        return false;
```
created_at: 2025-04-20T14:12:16Z
author_association: MEMBER
diff_hunk: @@ -10,83 +10,84 @@
 
 #include "utils/Variant.h"
 
-#include <rapidjson/prettywriter.h>
-#include <rapidjson/stringbuffer.h>
-#include <rapidjson/writer.h>
+#include <nlohmann/json.hpp>
 
-template<class TWriter>
-bool InternalWrite(TWriter& writer, const CVariant &value)
+bool InternalWrite(nlohmann::json& writer, const CVariant& value)
 {
   switch (value.type())
   {
   case CVariant::VariantTypeInteger:
-    return writer.Int64(value.asInteger());
-
+    writer = value.asInteger();
+    return true;
   case CVariant::VariantTypeUnsignedInteger:
-    return writer.Uint64(value.asUnsignedInteger());
-
+    writer = value.asUnsignedInteger();
+    return true;
   case CVariant::VariantTypeDouble:
-    return writer.Double(value.asDouble());
-
+    writer = value.asDouble();
+    return true;
   case CVariant::VariantTypeBoolean:
-    return writer.Bool(value.asBoolean());
-
+    writer = value.asBoolean();
+    return true;
   case CVariant::VariantTypeString:
-    return writer.String(value.c_str(), value.size());
-
+    writer = std::string(value.c_str(), value.size());
+    return true;
   case CVariant::VariantTypeArray:
-    if (!writer.StartArray())
-      return false;
+    writer = nlohmann::json::array();
 
     for (CVariant::const_iterator_array itr = value.begin_array(); itr != value.end_array(); ++itr)
     {
-      if (!InternalWrite(writer, *itr))
+      nlohmann::json o;
+      if (!InternalWrite(o, *itr))
         return false;
+      writer.emplace_back(o);
     }
 
-    return writer.EndArray(value.size());
-
+    return true;
   case CVariant::VariantTypeObject:
-    if (!writer.StartObject())
-      return false;
+    writer = nlohmann::json::object();
 
     for (CVariant::const_iterator_map itr = value.begin_map(); itr != value.end_map(); ++itr)
     {
-      if (!writer.Key(itr->first.c_str()) ||
-        !InternalWrite(writer, itr->second))
+      nlohmann::json o;
+      if (InternalWrite(o, itr->second))
+        writer[itr->first] = std::move(o);
+      else
         return false;
path: xbmc/utils/JSONVariantWriter.cpp
user: login: neo1973
body: ```suggestion
      nlohmann::json& o = writer[itr->first];
      if (!InternalWrite(o, itr->second))
        return false;
```
created_at: 2025-04-20T14:13:39Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,85 @@
+#.rst:
+# FindNlohmannJSON
+# -----------
+# Finds the NlohmannJSON library
+#
+# This will define the following target:
+#
+#   ${APP_NAME_LC}::NlohmannJSON - The NlohmannJSON library
+#
+
+if(NOT TARGET ${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME})
+  include(cmake/scripts/common/ModuleHelpers.cmake)
+
+  macro(buildnlohmannjson)
+    set(nlohmann_json_VERSION ${${${CMAKE_FIND_PACKAGE_NAME}_MODULE}_VER})
+    set(BUILD_NAME nlohmann_json_build)
+
+    set(CMAKE_ARGS -DJSON_BuildTests=OFF)
+    set(BUILD_BYPRODUCTS ${DEPENDS_PATH}/include/nlohmann/json.hpp)
+
+    BUILD_DEP_TARGET()
+  endmacro()
+
+  set(${CMAKE_FIND_PACKAGE_NAME}_MODULE_LC nlohmann_json)
+
+  SETUP_BUILD_VARS()
+
+  SETUP_FIND_SPECS()
+
+  find_package(nlohmann_json ${CONFIG_${CMAKE_FIND_PACKAGE_NAME}_FIND_SPEC} CONFIG ${SEARCH_QUIET}
+                             HINTS ${DEPENDS_PATH}/share/cmake
+                             ${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG})
+
+  # Check for existing NLOHMANNJSON. If version >= NLOHMANNJSON-VERSION file version, dont build
+  # A corner case, but if a linux/freebsd user WANTS to build internal tinyxml2, build anyway
path: cmake/modules/FindNlohmannJSON.cmake
user: login: phunkyfish
body: Why tinyxml?
created_at: 2025-04-24T08:01:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,85 @@
+#.rst:
+# FindNlohmannJSON
+# -----------
+# Finds the NlohmannJSON library
+#
+# This will define the following target:
+#
+#   ${APP_NAME_LC}::NlohmannJSON - The NlohmannJSON library
+#
+
+if(NOT TARGET ${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME})
+  include(cmake/scripts/common/ModuleHelpers.cmake)
+
+  macro(buildnlohmannjson)
+    set(nlohmann_json_VERSION ${${${CMAKE_FIND_PACKAGE_NAME}_MODULE}_VER})
+    set(BUILD_NAME nlohmann_json_build)
+
+    set(CMAKE_ARGS -DJSON_BuildTests=OFF)
+    set(BUILD_BYPRODUCTS ${DEPENDS_PATH}/include/nlohmann/json.hpp)
+
+    BUILD_DEP_TARGET()
+  endmacro()
+
+  set(${CMAKE_FIND_PACKAGE_NAME}_MODULE_LC nlohmann_json)
+
+  SETUP_BUILD_VARS()
+
+  SETUP_FIND_SPECS()
+
+  find_package(nlohmann_json ${CONFIG_${CMAKE_FIND_PACKAGE_NAME}_FIND_SPEC} CONFIG ${SEARCH_QUIET}
+                             HINTS ${DEPENDS_PATH}/share/cmake
+                             ${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG})
+
+  # Check for existing NLOHMANNJSON. If version >= NLOHMANNJSON-VERSION file version, dont build
+  # A corner case, but if a linux/freebsd user WANTS to build internal tinyxml2, build anyway
path: cmake/modules/FindNlohmannJSON.cmake
user: login: sundermann
body: Good catch, that was already a typo in `FindRapidjson.cmake` and carried over.
created_at: 2025-04-24T21:38:12Z
author_association: CONTRIBUTOR

PR comments:
user: login: fuzzard
created_at: 2025-04-05T22:41:55Z
body: I'm all for a replacement. I had started looking at https://github.com/ibireme/yyjson , but anything is fine by me.
author_association: CONTRIBUTOR
user: login: fuzzard
created_at: 2025-04-06T03:45:37Z
body: Fixup for the build side of things
https://github.com/fuzzard/xbmc/commit/e7b39c0d13523841e49cfb564a980c6af882d8c4

Would always use upstream sources as well. And use the tar.xz, smaller size
author_association: CONTRIBUTOR
user: login: fuzzard
created_at: 2025-04-06T04:24:02Z
body: https://github.com/fuzzard/xbmc/commit/6b48b83f75076b93818f156a1d49e5148c54efe5

seems to fix android. Not sure if theres a better way to handle it, i'll let the c++ experts chime in
author_association: CONTRIBUTOR
user: login: sundermann
created_at: 2025-04-06T09:06:34Z
body: @fuzzard Thanks for your fixes. I've applied them. Also moved parsing to the `sax_parser` interface which closer mimics what we had before.

I had removed all the rapidjson stuff from the linux install pages but haven't added nlohmann-json back in. Should we do that? It should fall back to an internal build if the library is not found.
author_association: CONTRIBUTOR
user: login: fuzzard
created_at: 2025-04-06T09:13:23Z
body: Definitely should. Linux should always prefer system libs as a default over any internal building we provide
author_association: CONTRIBUTOR
user: login: sundermann
created_at: 2025-04-06T10:15:42Z
body: I have updated the installation docs for linux
author_association: CONTRIBUTOR
user: login: sundermann
created_at: 2025-04-06T19:53:27Z
body: `CVariant` always uses 64 bit integers internally, so there's no need to cast it explicitly. I think this is ready for review
author_association: CONTRIBUTOR
user: login: Tolriq
created_at: 2025-04-09T07:19:32Z
body: Can some quick performance tests be done to confirm the impact is not too high? Specially large JSON output of a couple of MB on low end devices like rpi.
author_association: CONTRIBUTOR
user: login: sundermann
created_at: 2025-04-09T10:07:38Z
body: While nlohmann-json is slower than rapidJSON, we're not handling large JSON datasets here. The performance difference will be negligible, so I recommend prioritizing maintainability over marginal performance gains in this case.
author_association: CONTRIBUTOR
user: login: Tolriq
created_at: 2025-04-09T10:11:22Z
body: There was tons of work to speed up json, there is a lot of large data set as soon as we talk API. The webinterface, any remote app like Kore, or Yatse will request large JSON to have the library data to show. Even chunked we are still talking about MB of data.
There's speed and memory usage to take in account to choose the successor, check the history of the migration to rapidJSON and the work of montellese.
author_association: CONTRIBUTOR
user: login: acidzab
created_at: 2025-04-09T19:50:14Z
body: Could you consider simdjson as a replace for RapidJSON ?
author_association: NONE
user: login: fuzzard
created_at: 2025-04-09T20:13:37Z
body: PRs for alternatives are welcome
author_association: CONTRIBUTOR
user: login: sundermann
created_at: 2025-04-09T21:15:14Z
body: The concern seems mostly focused around serialization. The nice thing about rapidjson is that it has an explicit [writer interface](https://rapidjson.org/classrapidjson_1_1_writer.html) meaning that we can directly convert a `CVariant` into a string without requiring an intermediate json object representation. I haven't seen something similar in any other popular json library and it's also not something that `simdjson` could solve. 

Talking about deserialization `simdjson` does not have an SAX interface for parsing either so it would require us to first parse the whole json into a document and then constructing a `CVariant` from it which is somewhat inefficient as it requires twice the memory and a iterating a second time over the intermediate json representation.

Speaking of performance generally, the current approch for serialization is recursive which is somewhat inefficient as well. It could be rewritten using a stack.
author_association: CONTRIBUTOR
user: login: Tolriq
created_at: 2025-04-10T05:28:20Z
body: Honestly i don't recall all the details everything was made a very very long time ago :) 
And the new libs and compiler have maybe / probably improved enough so that the performance impact won't be that high.
All i ask is that there's some quick benchmark done to ensure there's no insane performance regression for the API users like the web interface, Kore and Yatse. It was a contention point with a lot of improvements, users will certain complain if everything take 50% of more time now, or just no more works because large chunks use too much memory to serialize on low end devices.
author_association: CONTRIBUTOR
user: login: neo1973
created_at: 2025-04-19T15:49:55Z
body: I measured the `POST http://localhost:8080/jsonrpc?TVShowCollection` call on my PC in release build. Content of the call:

```json
{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShows","id":"1745076795448","params":{"properties":["title","art","playcount","lastplayed","dateadded","episode","rating","year","file","genre","watchedepisodes","cast","studio","mpaa","tag"],"limits":{"start":0},"sort":{"method":"title","order":"ascending","ignorearticle":true}}}
```

This produces a 1.52 MB JSON response with my DB. The numbers:

- nlohmann: ~190ms
- rapidjson: ~170ms

So yes, slower, but not by a lot.
author_association: MEMBER
user: login: sundermann
created_at: 2025-04-20T12:16:13Z
body: I measured 420ms using nlohmann-json and 370ms using Rapidjson on macOS (Debug + ASan) using the call above. In Release configuration the performance difference was minimal 60ms vs 58ms.

For fun, I tried converting the serialization into an iterative solution but the performance was much worse (2000ms). Probably because the stack needs to track a lot more with an iterative approach.
author_association: CONTRIBUTOR
user: login: sundermann
created_at: 2025-04-20T17:08:12Z
body: I've applied those two changes and also changed the return type to `void` as the API uses exceptions to indicate failures and we catch these in the `Write` function.

I couldn't see a huge improvement on my library (maybe 3ms but it might just be within measurement tolerance) 
author_association: CONTRIBUTOR
user: login: neo1973
created_at: 2025-04-21T12:13:11Z
body: Any idea why the FreeBSD builder is unhappy?
author_association: MEMBER
user: login: sundermann
created_at: 2025-04-21T12:14:38Z
body: It needs the dependency installed because FreeBSD doesn't do an internal build
author_association: CONTRIBUTOR
user: login: sundermann
created_at: 2025-04-21T17:00:27Z
body: Updated depends nlohmann-json to 3.12.0 which includes cmake 4.0 support
author_association: CONTRIBUTOR
user: login: fuzzard
created_at: 2025-04-22T09:51:18Z
body: Can you apply the following fixup https://github.com/fuzzard/xbmc/commit/60b5a39e9f121372f1679e924e41b0a8bf27adb5

older nlohmann_json configs have a target called nlohmann_json, so the build name has to be overridden to avoid potential clashes. Theres also been some recent changes regarding the spec search stuff

Can you also alter the ARCHIVE name in the VERSION file

```
ARCHIVE=$(LIBNAME)-$(VERSION).tar.xz
``` 

We need to be able to distinguish version on the mirror, and we cant just override an older archive with the tarball name they use for their releases (json.tar.xz)

The file ``json-3.12.0.tar.xz`` has been put on the mirrors now
author_association: CONTRIBUTOR
user: login: sundermann
created_at: 2025-04-22T18:11:53Z
body: @fuzzard Thanks for your build system changes and uploading the tarball. I've applied the fixup and removed the `BASE_URL` change
author_association: CONTRIBUTOR