Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
strategy:
fail-fast: false
matrix:
java_version: ['17', '21', '24' ]
java_version: ['17', '21', '25' ]
include:
- java_version: '17'
release_build: 'R'
Expand Down
78 changes: 39 additions & 39 deletions release-notes/CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -6,61 +6,61 @@ XML format backend (`jackson-dataformat-xml`), version 3.x
Tatu Saloranta, tatu.saloranta@iki.fi: author

Morten Olav Hansen (mortenoh@github)

* Reported #25: `ACCEPT_EMPTY_STRING_AS_NULL_OBJECT` not honored in xml module
for attributes
(3.0.0)
* Reported #25: `ACCEPT_EMPTY_STRING_AS_NULL_OBJECT` not honored in xml module
for attributes
(3.0.0)

Ghenadii Batalski (@ghenadiibatalski)

* Reported #793: `XmlSerializationContext`: NPE _writeCapabilities not set for
`SerializationContext`
(3.0.4)
* Reported #793: `XmlSerializationContext`: NPE _writeCapabilities not set for
`SerializationContext`
(3.0.4)

PJ Fanning (@pjfanning)

* Fixed #793: `XmlSerializationContext`: NPE _writeCapabilities not set for
`SerializationContext`
(3.0.4)
* Fixed #793: `XmlSerializationContext`: NPE _writeCapabilities not set for
`SerializationContext`
(3.0.4)

Dai MIKURUBE (@dmikurube)

* Reported #149: `@JacksonXmlElementWrapper` as a `@JsonCreator parameter` not working
(3.2.0)
* Reported #149: `@JacksonXmlElementWrapper` as a `@JsonCreator parameter` not working
(3.2.0)

Christopher McVay (@mcvayc)

* Fixed #149: `@JacksonXmlElementWrapper` as a `@JsonCreator parameter` not working
(3.2.0)
* Fixed #517: XML wrapper doesn't work with java records
(3.2.0)
* Fixed #615: Deserialization of Xml with `@JacksonXmlText` using `@JsonCreator` (into
* Fixed #149: `@JacksonXmlElementWrapper` as a `@JsonCreator parameter` not working
(3.2.0)
* Fixed #517: XML wrapper doesn't work with java records
(3.2.0)
* Fixed #615: Deserialization of Xml with `@JacksonXmlText` using `@JsonCreator` (into
`java.util.Map`) fails
(3.2.0)
* Fixed #735: Java Record with `@JacksonXmlText` stopped working with 2.18
(3.2.0)
* Fixed #795: `HttpHeader` object (= <httpHeaders>) is not wrapped by the XML `<property>` tag
(3.2.0)
* Fixed #306: Can not use `@JacksonXmlText` for Creator property (creator parameter)
(3.2.0)
(3.2.0)
* Fixed #735: Java Record with `@JacksonXmlText` stopped working with 2.18
(3.2.0)
* Fixed #795: `HttpHeader` object (= <httpHeaders>) is not wrapped by the XML `<property>` tag
(3.2.0)
* Fixed #306: Can not use `@JacksonXmlText` for Creator property (creator parameter)
(3.2.0)

Eduard Wirch (@ewirch)
* Reported #306: Can not use `@JacksonXmlText` for Creator property (creator parameter)
(3.2.0)

* Reported #306: Can not use `@JacksonXmlText` for Creator property (creator parameter)
(3.2.0)
Jiri Mikulasek (@jimirocks)
* Reported #455: Can't deserialize list in JsonSubtype when type property is visible
(3.2.0)

Josip Antoliš (@Antolius)
* Reported #517: XML wrapper doesn't work with java records
(3.2.0)

* Reported #517: XML wrapper doesn't work with java records
(3.2.0)
Vitor Pamplona (@vitorpamplona)
* Reported #525: Order of XML Properties trigger different behaviors with
polymorphic nested objects
(3.2.0)

Severin Kistler (@kistlers)

* Reported #615: Deserialization of Xml with `@JacksonXmlText` using `@JsonCreator` (into
`java.util.Map`) fails
(3.2.0)
* Reported #615: Deserialization of Xml with `@JacksonXmlText` using `@JsonCreator` (into
`java.util.Map`) fails
(3.2.0)

Charles Moulliard (@cmoulliard)

* Reported #795: `HttpHeader` object (= <httpHeaders>) is not wrapped by the XML `<property>` tag
(3.2.0)
* Reported #795: `HttpHeader` object (= <httpHeaders>) is not wrapped by the XML `<property>` tag
(3.2.0)
15 changes: 15 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,24 @@ Version: 3.x (for earlier see VERSION-2.x)
#306: Can not use `@JacksonXmlText` for Creator property (creator parameter)
(reported by Eduard W)
(fix by Christopher M)
#426: `InvalidTypeIdException` when parsing XML to POJO containing nested `List<>`,
custom `TypeIdResolver`
(reported by @ankagar)
(fix by @cowtowncoder, w/ Claude code)
#455: Can't deserialize list in JsonSubtype when type property is visible
(reported by Jiri M)
(fix by @cowtowncoder, w/ Claude code)
#517: XML wrapper doesn't work with java records
(reported by Josip A)
(fix by Christopher M)
#525: Order of XML Properties trigger different behaviors with
polymorphic nested objects
(reported by Vitor P)
(fix by @cowtowncoder, w/ Claude code)
#567: First element in unwrapped XML array is ignored during
deserialization to base class
(reported by @tetradon)
(fix by @cowtowncoder, w/ Claude code)
#615: Deserialization of Xml with `@JacksonXmlText` using `@JsonCreator` (into
`java.util.Map`) fails
(reported by Severin K)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,11 +290,16 @@ public void addVirtualWrapping(Set<String> namesToWrap0, boolean caseInsensitive
// problems with Lists-in-Lists properties
// 12-May-2020, tatu: But as per [dataformat-xml#86] NOT for root element
// (would still like to know why work-around needed ever, but...)
// 15-Mar-2026, tatu: [dataformat-xml#455] Relax the parent-root check when
// we're at an element's PROPERTY_NAME (_mayBeLeaf distinguishes elements
// from attributes). This handles polymorphic type resolution where the type
// deserializer consumed properties before wrapping was set up, so we need
// to wrap the current element retroactively.
if (!_streamReadContext.inRoot()
&& !_streamReadContext.getParent().inRoot()) {
&& (!_streamReadContext.getParent().inRoot()
|| (_currToken == JsonToken.PROPERTY_NAME && _mayBeLeaf))) {
String name = _xmlTokens.getLocalName();
if ((name != null) && namesToWrap.contains(name)) {
//System.out.println("REPEAT from addVirtualWrapping() for '"+name+"'");
_xmlTokens.repeatStartElement();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,23 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, Typ
@SuppressWarnings("resource")
protected final void _configureParser(JsonParser p) throws JacksonException
{
if (_namesToWrap == null) {
return;
}
// 05-Sep-2019, tatu: May get XML parser, except for case where content is
// buffered. In that case we may still have access to real parser if we
// are lucky (like in [dataformat-xml#242])
while (p instanceof JsonParserDelegate) {
p = ((JsonParserDelegate) p).delegate();
// 15-Mar-2026, tatu: [dataformat-xml#455] Check for ElementWrappable at
// each delegation level, not just at the innermost parser. This handles
// the case where XmlTokenBuffer wraps the TokenBuffer.Parser with an
// ElementWrappable delegate during polymorphic type resolution.
while (p instanceof JsonParserDelegate jpd) {
if (p instanceof ElementWrappable) {
break;
}
p = jpd.delegate();
}
if ((p instanceof ElementWrappable) && (_namesToWrap != null)) {
if (p instanceof ElementWrappable ew) {
// 03-May-2021, tatu: as per [dataformat-xml#469] there are special
// cases where we get String token to represent XML empty element.
// If so, need to refrain from adding wrapping as that would
Expand All @@ -165,17 +175,17 @@ protected final void _configureParser(JsonParser p) throws JacksonException
// is consumed during buffering, so need to consider that too
// it seems (just hope we are at correct level and not off by one...)
|| t == JsonToken.PROPERTY_NAME) {
((ElementWrappable) p).addVirtualWrapping(_namesToWrap, _caseInsensitive);
ew.addVirtualWrapping(_namesToWrap, _caseInsensitive);
}
}
}

protected BeanDeserializerBase _verifyDeserType(ValueDeserializer<?> deser)
{
if (!(deser instanceof BeanDeserializerBase)) {
if (!(deser instanceof BeanDeserializerBase bdb)) {
throw new IllegalArgumentException("Can not change delegate to be of type "
+deser.getClass().getName());
}
return (BeanDeserializerBase) deser;
return bdb;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import tools.jackson.databind.deser.DeserializationContextExt;
import tools.jackson.databind.deser.DeserializerCache;
import tools.jackson.databind.deser.DeserializerFactory;
import tools.jackson.databind.util.TokenBuffer;
import tools.jackson.dataformat.xml.XmlFactory;

/**
Expand Down Expand Up @@ -81,4 +82,17 @@ public String extractScalarFromObject(JsonParser p, ValueDeserializer<?> deser,
}
return text;
}

/**
* Override to return XML-aware {@link XmlTokenBuffer} that produces
* parsers implementing {@link ElementWrappable}, allowing virtual wrapping
* to be configured even after content has been buffered (e.g., during
* polymorphic type resolution).
*
* @since 3.2
*/
@Override
public TokenBuffer bufferForInputBuffering(JsonParser p) {
return XmlTokenBuffer.xmlBufferForInputBuffering(p, this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package tools.jackson.dataformat.xml.deser;

import java.util.Set;

import tools.jackson.core.JsonParser;
import tools.jackson.core.ObjectReadContext;
import tools.jackson.core.util.JsonParserDelegate;

import tools.jackson.databind.util.TokenBuffer;

/**
* XML-specific {@link TokenBuffer} sub-class that ensures parsers created
* from buffered content implement {@link ElementWrappable}, allowing
* virtual wrapping to be configured on the underlying XML parser even
* when content has been buffered (e.g., during polymorphic type resolution).
*
* @since 3.2
*/
public class XmlTokenBuffer extends TokenBuffer
{
/**
* Reference to the original XML parser that implements {@link ElementWrappable},
* if one was found when this buffer was created.
*/
protected final ElementWrappable _wrappableParser;

protected XmlTokenBuffer(JsonParser p, ObjectReadContext ctxt)
{
super(p, ctxt);
// Find the ElementWrappable parser by unwrapping delegates
JsonParser unwrapped = p;
while (unwrapped instanceof JsonParserDelegate del) {
unwrapped = del.delegate();
}
_wrappableParser = (unwrapped instanceof ElementWrappable ew) ? ew : null;
}

public static XmlTokenBuffer xmlBufferForInputBuffering(JsonParser p,
ObjectReadContext ctxt) {
return new XmlTokenBuffer(p, ctxt);
}

/*
/**********************************************************************
/* Parser construction overrides
/**********************************************************************
*/

@Override
public JsonParser asParser(ObjectReadContext readCtxt)
{
return _wrapIfNeeded(super.asParser(readCtxt));
}

@Override
public JsonParser asParser(ObjectReadContext readCtxt, JsonParser p0)
{
return _wrapIfNeeded(super.asParser(readCtxt, p0));
}

protected JsonParser _wrapIfNeeded(JsonParser p) {
return (_wrappableParser == null) ? p
: new ElementWrappableParser(p, _wrappableParser);
}

/*
/**********************************************************************
/* Helper classes
/**********************************************************************
*/

/**
* A thin parser delegate that implements {@link ElementWrappable} by
* forwarding wrapping configuration to the original XML parser.
* This allows {@link WrapperHandlingDeserializer} to find and configure
* virtual wrapping even when the active parser is reading from a
* {@link TokenBuffer}.
*/
static class ElementWrappableParser extends JsonParserDelegate
implements ElementWrappable
{
protected final ElementWrappable _wrappable;

ElementWrappableParser(JsonParser delegate, ElementWrappable wrappable) {
super(delegate);
_wrappable = wrappable;
}

@Override
public void addVirtualWrapping(Set<String> namesToWrap, boolean caseInsensitive) {
_wrappable.addVirtualWrapping(namesToWrap, caseInsensitive);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tools.jackson.dataformat.xml.tofix;
package tools.jackson.dataformat.xml.deser;

import java.util.List;

Expand All @@ -9,8 +9,6 @@
import tools.jackson.databind.ObjectMapper;

import tools.jackson.dataformat.xml.XmlTestUtil;
import tools.jackson.dataformat.xml.testutil.failure.JacksonTestFailureExpected;

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

// Tests for [dataformat-xml#525], related to relative order of "type"
Expand Down Expand Up @@ -49,7 +47,7 @@ static class BindingInfo525 {
.defaultUseWrapper(false)
.build();

@JacksonTestFailureExpected
// [dataformat-xml#525]
@Test
public void testTypeAfterOtherProperties() throws Exception {
String xml =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tools.jackson.dataformat.xml.tofix;
package tools.jackson.dataformat.xml.lists;

import java.util.List;

Expand All @@ -13,8 +13,6 @@
import tools.jackson.dataformat.xml.XmlTestUtil;
import tools.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import tools.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import tools.jackson.dataformat.xml.testutil.failure.JacksonTestFailureExpected;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

Expand Down Expand Up @@ -91,7 +89,6 @@ public JsonTypeInfo.Id getMechanism() {
private final ObjectMapper MAPPER = newMapper();

// [dataformat-xml#426]
@JacksonTestFailureExpected
@Test
public void testPolymorphicList426() throws Exception
{
Expand Down
Loading
Loading