Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions release-notes/CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ Christian Beikov (@beikov)
with `@JsonIgnore`-annotated getter
(3.2.2)

Lavender Shannon (@retrodaredevil)
* Reported #892: Whitespace-only element body treated as unknown property `""`
(3.3.0)

@arimu1
* Fixed #892: Ignore whitespace-only unnamed XML text as unknown property
(3.3.0)

@Sahana2524
* Contributed #891: Enforce `StreamReadConstraints.maxNestingDepth` in
`FromXmlParser`
Expand Down
3 changes: 3 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Version: 3.x (for earlier see VERSION-2.x)

3.3.0 (not yet released)

#892: Whitespace-only element body treated as unknown property `""`
(reported by @retrodaredevil)
(fix by @arimu1)
#873: Fix handling of `@JsonApplyView`
(fix by @cowtowncoder, w/ Claude code)
#878: Re-apply entity/DTD hardening after JDK deserialization of `XmlFactory`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,25 @@ public XmlDeserializationContext(TokenStreamFactory tsf,
/**********************************************************************
*/

/**
* [dataformat-xml#892]: Pretty-printed XML with attribute-only beans/records
* exposes whitespace-only character data as the unnamed text property
* ({@code ""} by default). Skip that synthetic property when it is not
* mapped ({@code @JacksonXmlText}); mapped text still binds as usual.
*/
@Override
public boolean handleUnknownProperty(JsonParser p, ValueDeserializer<?> deser,
Object instanceOrClass, String propName)
throws JacksonException
{
if (_xmlTextElementName.equals(propName)
&& _isIgnorableWhitespaceXmlText(p)) {
p.skipChildren();
return true;
}
return super.handleUnknownProperty(p, deser, instanceOrClass, propName);
}

@Override
public Object readRootValue(JsonParser p, JavaType valueType,
ValueDeserializer<Object> deser, Object valueToUpdate)
Expand Down Expand Up @@ -120,6 +139,13 @@ public TokenBuffer bufferForInputBuffering(JsonParser p) {
/**********************************************************************
*/

private static boolean _isIgnorableWhitespaceXmlText(JsonParser p)
throws JacksonException
{
return (p.currentToken() == JsonToken.VALUE_STRING)
&& XmlTokenStream._allWs(p.getString());
}

/**
* Helper method for [dataformat-xml#247]: verify that the root element name
* matches the expected name when {@link XmlReadFeature#ENFORCE_ROOT_ELEMENT_NAME}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package tools.jackson.dataformat.xml.deser;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.JsonProperty;

import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.exc.UnrecognizedPropertyException;
import tools.jackson.dataformat.xml.XmlMapper;
import tools.jackson.dataformat.xml.XmlTestUtil;

import static org.junit.jupiter.api.Assertions.*;

// [dataformat-xml#892]: whitespace-only element body on an attribute-only type
// must not be treated as unknown property ""
public class WhitespaceOnlyUnknownProp892Test extends XmlTestUtil
{
record Response(
@JsonProperty("Item") Item item
) {
}

record Item(String name) {
}

static class ItemPojo {
public String name;
}

private final XmlMapper MAPPER = mapperBuilder()
.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.build();

private static final String ISSUE_XML = """
<Response>
\t<Item name="example">
\t</Item>
</Response>
""";

@Test
public void testRecordWhitespaceOnlyBody892() throws Exception
{
Response response = MAPPER.readValue(ISSUE_XML, Response.class);
assertEquals(new Item("example"), response.item());
}

@Test
public void testPojoWhitespaceOnlyBody892() throws Exception
{
ItemPojo item = MAPPER.readValue("<Item name=\"example\">\n\t</Item>", ItemPojo.class);
assertEquals("example", item.name);
}

@Test
public void testNonWhitespaceTextStillUnknown892() throws Exception
{
try {
MAPPER.readValue("<Item name=\"example\">secret</Item>", ItemPojo.class);
fail("Should not pass");
} catch (UnrecognizedPropertyException e) {
verifyException(e, "Unrecognized property \"\"");
}
}
}