From 21b69df66c1a5f1017051971246d8eee83f00fe1 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 16 Apr 2021 20:23:26 -0700 Subject: [PATCH] Fix #3122 --- release-notes/CREDITS-2.x | 4 +++ release-notes/VERSION-2.x | 2 ++ .../jackson/databind/ObjectReader.java | 13 +++++++ .../databind/deser/merge/NodeMergeTest.java | 36 ++++++++++++++++++- 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index e763c87d24..47789bfada 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -1327,3 +1327,7 @@ Asaf Romano (asaf-romano@github) David Hoffman (dhofftgt@github) * Contributed #3082: Dont track unknown props in buffer if `ignoreAllUnknown` is true (2.13.0) + +Eric Sirianni (sirianni@github) + * Reported #3122: Deep merge for `JsonNode` using `ObjectReader.readTree()` + (2.13.0) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 1b8f61d1c5..717f334828 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -28,6 +28,8 @@ Project: jackson-databind #3099: Optimize "AnnotatedConstructor.call()" case by passing explicit null #3101: Add AnnotationIntrospector.XmlExtensions interface for decoupling javax dependencies #3117: Use more limiting default visibility settings for JDK types (java.*, javax.*) +#3122: Deep merge for `JsonNode` using `ObjectReader.readTree()` + (reported by Eric S) - Fix to avoid problem with `BigDecimalNode`, scale of `Integer.MIN_VALUE` (see [dataformats-binary#264] for details) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java index 99d7818436..40b312a9d3 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java @@ -2062,6 +2062,12 @@ protected final JsonNode _bindAndCloseAsTree(JsonParser p0) throws IOException { protected final JsonNode _bindAsTree(JsonParser p) throws IOException { + // 16-Apr-2021, tatu: Should usually NOT be called this way but + // as per [databind#3122] should still work + if (_valueToUpdate != null) { + return (JsonNode) _bind(p, _valueToUpdate); + } + // Need to inline `_initForReading()` due to tree reading handling end-of-input specially _config.initialize(p); if (_schema != null) { @@ -2098,6 +2104,13 @@ protected final JsonNode _bindAsTree(JsonParser p) throws IOException */ protected final JsonNode _bindAsTreeOrNull(JsonParser p) throws IOException { + // 16-Apr-2021, tatu: Should usually NOT be called this way but + // as per [databind#3122] should still work + if (_valueToUpdate != null) { + return (JsonNode) _bind(p, _valueToUpdate); + } + + // Need to inline `_initForReading()` (as per above) _config.initialize(p); if (_schema != null) { p.setSchema(_schema); diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/merge/NodeMergeTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/merge/NodeMergeTest.java index b76d854f11..5e5756d8a6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/merge/NodeMergeTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/merge/NodeMergeTest.java @@ -1,7 +1,7 @@ package com.fasterxml.jackson.databind.deser.merge; import com.fasterxml.jackson.annotation.JsonMerge; - +import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -162,4 +162,38 @@ public void testUpdateArrayWithString() throws Exception assertEquals(a2q("{'test':'n/a'}"), result.toString()); } + + // [databind#3122]: "readTree()" fails where "readValue()" doesn't: + public void testObjectDeepMerge3122() throws Exception + { + final String jsonToMerge = a2q("{'root':{'b':'bbb','foo':'goodbye'}}"); + + JsonNode node1 = MAPPER.readTree(a2q("{'root':{'a':'aaa','foo':'hello'}}")); + assertEquals(2, node1.path("root").size()); + + JsonNode node2 = MAPPER.readerForUpdating(node1) + .readValue(jsonToMerge); + assertSame(node1, node2); + assertEquals(3, node1.path("root").size()); + + node1 = MAPPER.readTree(a2q("{'root':{'a':'aaa','foo':'hello'}}")); + JsonNode node3 = MAPPER.readerForUpdating(node1) + .readTree(jsonToMerge); + assertSame(node1, node3); + assertEquals(3, node1.path("root").size()); + + node1 = MAPPER.readTree(a2q("{'root':{'a':'aaa','foo':'hello'}}")); + JsonNode node4 = MAPPER.readerForUpdating(node1) + .readTree(utf8Bytes(jsonToMerge)); + assertSame(node1, node4); + assertEquals(3, node1.path("root").size()); + + // And finally variant passing `JsonParser` + try (JsonParser p = MAPPER.createParser(jsonToMerge)) { + JsonNode node5 = MAPPER.readerForUpdating(node1) + .readTree(p); + assertSame(node1, node5); + assertEquals(3, node1.path("root").size()); + } + } }