diff --git a/flow-server/src/main/java/com/vaadin/flow/internal/JacksonUtils.java b/flow-server/src/main/java/com/vaadin/flow/internal/JacksonUtils.java index c633e783f0f..14b12fe6724 100644 --- a/flow-server/src/main/java/com/vaadin/flow/internal/JacksonUtils.java +++ b/flow-server/src/main/java/com/vaadin/flow/internal/JacksonUtils.java @@ -519,6 +519,9 @@ public static T readValue(JsonNode jsonValue, * @return converted JSON value */ public static BaseJsonNode writeValue(Object object) { + if (object == null) { + return (BaseJsonNode) objectMapper.nullNode(); + } return objectMapper.valueToTree(object); } diff --git a/flow-server/src/test/java/com/vaadin/flow/internal/JacksonUtilsTest.java b/flow-server/src/test/java/com/vaadin/flow/internal/JacksonUtilsTest.java index a145e4a779c..2fe407a220d 100644 --- a/flow-server/src/test/java/com/vaadin/flow/internal/JacksonUtilsTest.java +++ b/flow-server/src/test/java/com/vaadin/flow/internal/JacksonUtilsTest.java @@ -36,6 +36,7 @@ import tools.jackson.databind.JsonNode; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.BaseJsonNode; import tools.jackson.databind.node.DoubleNode; import tools.jackson.databind.node.ObjectNode; @@ -490,4 +491,11 @@ public void toFileJson() throws JacksonException { } + @Test + public void writeValue_nullReturnsNullNode() { + BaseJsonNode result = JacksonUtils.writeValue(null); + Assert.assertTrue("Expected NullNode", result.isNull()); + Assert.assertEquals(mapper.nullNode(), result); + } + } diff --git a/flow-tests/test-react-adapter/src/main/java/com/vaadin/flow/ReactAdapterView.java b/flow-tests/test-react-adapter/src/main/java/com/vaadin/flow/ReactAdapterView.java index 2a2de3f3676..0bc6673782b 100644 --- a/flow-tests/test-react-adapter/src/main/java/com/vaadin/flow/ReactAdapterView.java +++ b/flow-tests/test-react-adapter/src/main/java/com/vaadin/flow/ReactAdapterView.java @@ -35,6 +35,10 @@ public ReactAdapterView() { (event) -> input.setValue("set value")); setValueButton.setId("setValueButton"); + var setNullButton = new NativeButton("Set null", + (event) -> input.setValue(null)); + setNullButton.setId("setNullValueButton"); + var getOutput = new Span(); getOutput.setId("getOutput"); @@ -43,7 +47,7 @@ public ReactAdapterView() { getValueButton.setId("getValueButton"); add(new Div(input, listenerOutput), new Div(setValueButton), - new Div(getValueButton, getOutput)); + new Div(setNullButton), new Div(getValueButton, getOutput)); } } diff --git a/flow-tests/test-react-adapter/src/test/java/com/vaadin/flow/ReactAdapterIT.java b/flow-tests/test-react-adapter/src/test/java/com/vaadin/flow/ReactAdapterIT.java index 0d3c791a0b8..2df64cda400 100644 --- a/flow-tests/test-react-adapter/src/test/java/com/vaadin/flow/ReactAdapterIT.java +++ b/flow-tests/test-react-adapter/src/test/java/com/vaadin/flow/ReactAdapterIT.java @@ -81,6 +81,19 @@ public void validateListener() { $(SpanElement.class).id("listenerOutput").getText()); } + @Test + public void validateSetNullState() { + open(); + + waitForDevServer(); + + $(NativeButtonElement.class).id("setNullValueButton").click(); + + // getPropertyString returns null for null/undefined values + String value = getAdapterElement().getPropertyString("value"); + Assert.assertNull("Expected null value, not string 'null'", value); + } + private TestBenchElement getAdapterElement() { return $("react-input").first(); }