Closed
Description
As 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
.
Metadata
Metadata
Assignees
Labels
No labels