Fix update(merge_objects=true) throwing on primitive-to-object merge (#5414)

When merge_objects is true, recurse only if the existing value is an
object. Otherwise overwrite, matching the documented "all other values
are overwritten as usual" behavior.

Fixes #5402

Signed-off-by: elix3r <157088510+22elix3r@users.noreply.github.com>
diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index 235e6b7..4e05706 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -3516,7 +3516,10 @@
             if (merge_objects && it.value().is_object())
             {
                 auto it2 = m_data.m_value.object->find(it.key());
-                if (it2 != m_data.m_value.object->end())
+                // Only recurse when the existing value is itself an object.
+                // Otherwise overwrite, matching the documented "all other values
+                // are overwritten as usual" behavior (see #5402).
+                if (it2 != m_data.m_value.object->end() && it2->second.is_object())
                 {
                     it2->second.update(it.value(), true);
 #if JSON_DIAGNOSTICS
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 904d865..4c346b4 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -24870,7 +24870,10 @@
             if (merge_objects && it.value().is_object())
             {
                 auto it2 = m_data.m_value.object->find(it.key());
-                if (it2 != m_data.m_value.object->end())
+                // Only recurse when the existing value is itself an object.
+                // Otherwise overwrite, matching the documented "all other values
+                // are overwritten as usual" behavior (see #5402).
+                if (it2 != m_data.m_value.object->end() && it2->second.is_object())
                 {
                     it2->second.update(it.value(), true);
 #if JSON_DIAGNOSTICS
diff --git a/tests/src/unit-modifiers.cpp b/tests/src/unit-modifiers.cpp
index 18185ec..de14b3f 100644
--- a/tests/src/unit-modifiers.cpp
+++ b/tests/src/unit-modifiers.cpp
@@ -801,6 +801,30 @@
                     j1.update(j2, true);
                     CHECK(j1 == json({{"string", "t"}, {"numbers", 1}}));
                 }
+
+                SECTION("overwrite primitive with object")
+                {
+                    json j1 = {{"k", 1}};
+                    json const j2 = {{"k", {{"x", 2}}}};
+                    j1.update(j2, true);
+                    CHECK(j1 == json({{"k", {{"x", 2}}}}));
+                }
+
+                SECTION("overwrite array with object")
+                {
+                    json j1 = {{"k", {1, 2}}};
+                    json const j2 = {{"k", {{"x", 2}}}};
+                    j1.update(j2, true);
+                    CHECK(j1 == json({{"k", {{"x", 2}}}}));
+                }
+
+                SECTION("overwrite nested primitive with object")
+                {
+                    json j1 = {{"k", {{"inner", 1}}}};
+                    json const j2 = {{"k", {{"inner", {{"x", 2}}}}}};
+                    j1.update(j2, true);
+                    CHECK(j1 == json({{"k", {{"inner", {{"x", 2}}}}}}));
+                }
             }
         }
     }
diff --git a/tests/src/unit-regression2.cpp b/tests/src/unit-regression2.cpp
index 29b52d7..2e7450e 100644
--- a/tests/src/unit-regression2.cpp
+++ b/tests/src/unit-regression2.cpp
@@ -1555,4 +1555,15 @@
     }
 }
 
+TEST_CASE("issue #5402 - update(merge_objects=true) overwrites a primitive with an object")
+{
+    json t = {{"k", 1}};
+    t.update(json{{"k", {{"x", 2}}}}, true);
+    CHECK(t == json({{"k", {{"x", 2}}}}));
+
+    json mixed = {{"keep", {{"a", 1}}}, {"replace", 1}};
+    mixed.update(json{{"keep", {{"b", 2}}}, {"replace", {{"x", 2}}}}, true);
+    CHECK(mixed == json({{"keep", {{"a", 1}, {"b", 2}}}, {"replace", {{"x", 2}}}}));
+}
+
 DOCTEST_CLANG_SUPPRESS_WARNING_POP