diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index a45c583746..914a5f235e 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -36,6 +36,8 @@ Project: jackson-databind (reported by Kornel Zemla) #4863: Add basic Stream support in `JsonNode`: `valueStream()`, `propertyStream()`, `forEachEntry()` +#4867: Add `Optional JsonNode.asOptional()` convenience method + (fix by Joo-Hyuk K) 2.18.3 (not yet released) diff --git a/src/main/java/tools/jackson/databind/JsonNode.java b/src/main/java/tools/jackson/databind/JsonNode.java index e5da02cf86..97821e73fd 100644 --- a/src/main/java/tools/jackson/databind/JsonNode.java +++ b/src/main/java/tools/jackson/databind/JsonNode.java @@ -736,6 +736,17 @@ public boolean asBoolean(boolean defaultValue) { return defaultValue; } + /** + * Method that will return a {@link JsonNode} wrapped in Java's {@link Optional}. + * + * @return `Optional` containing this node, or {@link Optional#empty()} + * if this is a {@link MissingNode}. + * @since 2.19 + */ + public Optional asOptional() { + return Optional.of(this); + } + /* /********************************************************************** /* Public API, extended traversal (2.10) with "required()" diff --git a/src/main/java/tools/jackson/databind/node/MissingNode.java b/src/main/java/tools/jackson/databind/node/MissingNode.java index 259936eece..ef5ea45e69 100644 --- a/src/main/java/tools/jackson/databind/node/MissingNode.java +++ b/src/main/java/tools/jackson/databind/node/MissingNode.java @@ -1,6 +1,7 @@ package tools.jackson.databind.node; import java.util.List; +import java.util.Optional; import tools.jackson.core.*; import tools.jackson.databind.JsonNode; @@ -65,24 +66,8 @@ public final boolean isMissingNode() { */ @Override - public final void serialize(JsonGenerator g, SerializationContext provider) - throws JacksonException - { - /* Nothing to output... should we signal an error tho? - * Chances are, this is an erroneous call. For now, let's - * not do that; serialize as explicit null. Why? Because we - * cannot just omit a value as JSON Object field name may have - * been written out. - */ - g.writeNull(); - } - - @Override - public void serializeWithType(JsonGenerator g, SerializationContext provider, - TypeSerializer typeSer) - throws JacksonException - { - g.writeNull(); + public Optional asOptional() { + return Optional.empty(); } @SuppressWarnings("unchecked") @@ -139,9 +124,36 @@ public List findParents(String fieldName, List foundSoFar) { } /* - /********************************************************** + /********************************************************************** + /* Serialization: bit tricky as we don't really have a value + /********************************************************************** + */ + + @Override + public final void serialize(JsonGenerator g, SerializationContext provider) + throws JacksonException + { + /* Nothing to output... should we signal an error tho? + * Chances are, this is an erroneous call. For now, let's + * not do that; serialize as explicit null. Why? Because we + * cannot just omit a value as JSON Object field name may have + * been written out. + */ + g.writeNull(); + } + + @Override + public void serializeWithType(JsonGenerator g, SerializationContext provider, + TypeSerializer typeSer) + throws JacksonException + { + g.writeNull(); + } + + /* + /********************************************************************** /* Standard method overrides - /********************************************************** + /********************************************************************** */ @Override diff --git a/src/test/java/tools/jackson/databind/node/JsonNodeBasicTest.java b/src/test/java/tools/jackson/databind/node/JsonNodeBasicTest.java index b3c715d015..82dbf7ca1d 100644 --- a/src/test/java/tools/jackson/databind/node/JsonNodeBasicTest.java +++ b/src/test/java/tools/jackson/databind/node/JsonNodeBasicTest.java @@ -1,6 +1,7 @@ package tools.jackson.databind.node; import java.util.Comparator; +import java.util.Optional; import org.junit.jupiter.api.Test; @@ -214,4 +215,37 @@ public void testArrayWithDefaultTyping() throws Exception assertEquals(1, obj.size()); assertEquals(2, obj.path("a").asInt()); } + + // [databind#4867] + @Test + public void testAsOptional() { + // Test with MissingNode + JsonNode missingNode = MissingNode.getInstance(); + Optional missingOptional = missingNode.asOptional(); + assertFalse(missingOptional.isPresent()); + + // Test with ObjectNode + ObjectNode objectNode = MAPPER.createObjectNode(); + Optional objectOptional = objectNode.asOptional(); + assertTrue(objectOptional.isPresent()); + assertEquals(objectNode, objectOptional.get()); + + // Test with ArrayNode + ArrayNode arrayNode = MAPPER.createArrayNode(); + Optional arrayOptional = arrayNode.asOptional(); + assertTrue(arrayOptional.isPresent()); + assertEquals(arrayNode, arrayOptional.get()); + + // Test with TextNode + TextNode textNode = TextNode.valueOf("text"); + Optional textOptional = textNode.asOptional(); + assertTrue(textOptional.isPresent()); + assertEquals(textNode, textOptional.get()); + + // Test with NullNode + NullNode nullNode = NullNode.getInstance(); + Optional nullOptional = nullNode.asOptional(); + assertTrue(nullOptional.isPresent()); + assertEquals(nullNode, nullOptional.get()); + } } diff --git a/src/test/java/tools/jackson/databind/node/MissingNodeTest.java b/src/test/java/tools/jackson/databind/node/MissingNodeTest.java index be0e1855d7..8516af27b0 100644 --- a/src/test/java/tools/jackson/databind/node/MissingNodeTest.java +++ b/src/test/java/tools/jackson/databind/node/MissingNodeTest.java @@ -62,6 +62,7 @@ public void testMissingViaMapper() throws Exception assertTrue(onode.isObject()); assertEquals(0, onode.size()); assertFalse(onode.isMissingNode()); // real node + assertTrue(onode.asOptional().isPresent()); assertNull(onode.textValue()); // how about dereferencing? @@ -74,6 +75,7 @@ public void testMissingViaMapper() throws Exception JsonNode dummyNode2 = dummyNode.path(98); assertNotNull(dummyNode2); assertTrue(dummyNode2.isMissingNode()); + assertFalse(dummyNode2.asOptional().isPresent()); JsonNode dummyNode3 = dummyNode.path("field"); assertNotNull(dummyNode3); assertTrue(dummyNode3.isMissingNode()); @@ -92,6 +94,7 @@ public void testMissingViaMapper() throws Exception assertTrue(dummyNode.isMissingNode()); assertNull(dummyNode.get(0)); assertNull(dummyNode.get("myfield")); + assertFalse(dummyNode.asOptional().isPresent()); dummyNode2 = dummyNode.path(98); assertNotNull(dummyNode2); assertTrue(dummyNode2.isMissingNode());