`XmlMapper` 2.12 regression: no default no-arg ctor found
See original GitHub issueAs described first in FasterXML/jackson-module-kotlin#396, 2.12.0 has introduced a regression to the XmlMapper
. I have managed to reproduce the regression in the below Java snippet, which works on 2.11.4, but fails on 2.12.{0…4}:
package com.vlkan.jackson;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertEquals;
class MixinTest {
interface Problem {
String DEFAULT_TYPE = "about:blank";
int DEFAULT_STATUS = 500;
String getType();
int getStatus();
}
static class DefaultProblem implements Problem {
private final String type;
private final int status;
/**
* This is required to workaround Jackson's missing support for static
* {@link JsonCreator}s in mix-ins. That is, we need to define the
* creator on a constructor in the mix-in that is matching with a
* constructor here too.
*
* @see <a href="https://github.com/FasterXML/jackson-databind/issues/1820">jackson-databind issue 1820</a>
*/
DefaultProblem(String type, Integer status) {
this.type = type != null ? type : Problem.DEFAULT_TYPE;
this.status = status != null ? status : Problem.DEFAULT_STATUS;
}
@Override
public String getType() {
return type;
}
@Override
public int getStatus() {
return status;
}
}
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type",
defaultImpl = DefaultProblem.class,
visible = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonRootName("problem")
interface ProblemMixIn extends Problem {
@Override
@JsonProperty("type")
String getType();
@Override
@JsonProperty("status")
int getStatus();
}
abstract static class DefaultProblemMixIn extends DefaultProblem {
@JsonCreator
DefaultProblemMixIn(
@JsonProperty("type") String type,
@JsonProperty("status") Integer status) {
super(type, status);
throw new IllegalStateException(
"mix-in constructor is there only for extracting the JSON mapping, " +
"it should not have been called");
}
}
static class ProblemModule extends SimpleModule {
@Override
public void setupModule(SetupContext context) {
super.setupModule(context);
registerMixIns(context);
}
private static void registerMixIns(SetupContext context) {
context.setMixInAnnotations(DefaultProblem.class, DefaultProblemMixIn.class);
context.setMixInAnnotations(Problem.class, ProblemMixIn.class);
}
}
private static final ProblemModule MODULE = new ProblemModule();
private static final ObjectMapper JSON_MAPPER = new ObjectMapper().registerModule(MODULE);
private static final XmlMapper XML_MAPPER = (XmlMapper) new XmlMapper().registerModule(MODULE);
@Test
void test_empty_Problem_JSON_deserialization() throws IOException {
byte[] problemJsonBytes = "{}".getBytes(StandardCharsets.UTF_8);
Problem problem = JSON_MAPPER.readValue(problemJsonBytes, Problem.class);
assertEquals(Problem.DEFAULT_TYPE, problem.getType());
assertEquals(Problem.DEFAULT_STATUS, problem.getStatus());
}
@Test
void test_empty_Problem_XML_deserialization() throws IOException {
byte[] problemXmlBytes = "<problem/>".getBytes(StandardCharsets.UTF_8);
Problem problem = XML_MAPPER.readValue(problemXmlBytes, Problem.class);
assertEquals(Problem.DEFAULT_TYPE, problem.getType());
assertEquals(Problem.DEFAULT_STATUS, problem.getStatus());
}
}
Both tests pass on 2.11.4, whereas, on 2.12.{0…4}, test_empty_Problem_JSON_deserialization()
passes and
test_empty_Problem_XML_deserialization()
fails with the following message:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.vlkan.jackson.MixinTest$DefaultProblem` (although at least one Creator exists): no default no-arguments constructor found
at [Source: (byte[])"<problem/>"; line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1588)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1213)
at com.fasterxml.jackson.databind.deser.ValueInstantiator.createUsingDefault(ValueInstantiator.java:248)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createUsingDefault(StdValueInstantiator.java:275)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.getEmptyValue(BeanDeserializerBase.java:1042)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer._deserializeFromEmptyString(StdDeserializer.java:322)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer._deserializeFromString(StdDeserializer.java:270)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1495)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:207)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:197)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedUsingDefaultImpl(AsPropertyTypeDeserializer.java:194)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:96)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:263)
at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:74)
at com.fasterxml.jackson.dataformat.xml.deser.XmlDeserializationContext.readRootValue(XmlDeserializationContext.java:91)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3609)
at com.vlkan.jackson.MixinTest.test_empty_Problem_XML_deserialization(MixinTest.java:129)
...
In order to make test_empty_Problem_XML_deserialization()
pass on 2.12.{0…4}, one needs to add a no-arg ctor to DefaultProblem
.
Issue Analytics
- State:
- Created 2 years ago
- Comments:18 (13 by maintainers)
Top Results From Across the Web
Jackson XML works with class but not with record
Adding a default no-arguments constructor makes no difference, but changing from record to class (adding all the necessary stuff) doew. What am ...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Ok. I added some notes on the failing test. This is not an easy problem to solve; basically there seems to be 2 ways to work around it. Either:
<root></root>
(empty element) to produceSTART_OBJECT
/END_OBJECT
pair (instead ofVALUE_STRING
). This would likely cause other regression, but is a possibility.StdDeserializer
handling of “empty value” to allow use of Properties-based Creator, passing “all absent” values. Actually code goes toBeanDeserializerBase.getEmptyValue()
, which is where this would need to be done.Of these (2) would work better and would probably be the way to go. I don’t have time to pursue this quite now, but wanted to add a note if anyone else might want to try it.
Other than this, adding the no-arguments constructor also works around the issue.
@cowtowncoder Sure, posted in #547
EDIT: By the way, I just realized that this is the exact same issue that I reported in https://github.com/FasterXML/jackson-module-kotlin/issues/396 two years ago. This is still covered by https://github.com/FasterXML/jackson-integration-tests/blob/master/src/test/kotlin/com/fasterxml/jackson/failing/Jackson212MissingConstructorTest.kt