FasterXML / FasterXML/jackson-modules-base
MismatchedInputException for XmlIDREF in generic interface attribute
- Dominant language
- Java
- Stars
- 180
- Forks
- 80
- Avg merge
- 3h 26m
- Merged PRs (30d)
- 1
Description
I do not know if this is the right location for the issue. If it is more related to handling JAXB annotations, I can move it.
I came across a problem when writing a test that first serializes an object to a string to JSON using Jackson and afterwards, tries to deserialize it. I tried to create a MWE based on the actual problem.
The underlying code uses JAXB annotations with additions for Jackson where necessary. The problem occurs for a generic attribute which itself is an interface. In the MWE there is a class `Model` with a generic attribute `value` of type `ValueInterface`. `value` itself is used as an `@XmlIDREF` inside `Model`. In the underlying tool I use a `Memory` to store `Model`s and `Value`s independently for reuse, as multiple `Model` instances may use the same `Value` object.
What I get is:
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (VALUE_STRING), expected START_OBJECT: need JSON Object to contain As.WRAPPER_OBJECT type information for class org.raedma.jacksonjsondeserializationquestion.ValueInterface
It seems Jackson is unable to resolve the `XmlIDREF` string, but rather expects the `value` as a new node. I already tried different solution approaches, e.g. [here][1] and [here][2], to tell Jackson that this is a reference to an object having an `@XmlID`. Unfortunately, nothing helped.
Any ideas?
I should mention that in the underlying code, `Value` itself is a subtype of some superclasses. Something like `@JsonDeserialize(as = MyInterfaceImpl.class)` as mentioned [here][3] is not possible, otherwise I would not need an interface. The same approach works in JAXB for xml.
----------
## MWE ##
- `Main`:
public class Main{
public static void main(String[] args) throws JsonProcessingException, JAXBException{
Value value = new Value("valueID",1);
Collection values = new ArrayList<>();
values.add(value);
Model model = new Model<>("modelID", value);
Collection models = new ArrayList<>();
models.add(model);
Memory memory = new Memory();
memory.setValues(values);
memory.setModels(models);
//
json(memory);
//
//xml(memory);
}
private static void json(Memory memory) throws JsonProcessingException{
// Serialize
ObjectMapper outObjectMapper = new ObjectMapper();
outObjectMapper.registerModule(new JaxbAnnotationModule());
outObjectMapper.enable(SerializationFeature.INDENT_OUTPUT);
outObjectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
outObjectMapper.setSerializationInclusion(Include.NON_NULL);
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE);
outObjectMapper.setDefaultPrettyPrinter(prettyPrinter);
String result = outObjectMapper.writeValueAsString(memory);
//System.out.println(result);
// Deserialize
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
objectMapper.registerModule(new JaxbAnnotationModule());
Memory deserial = objectMapper.readValue(result, Memory.class);
}
private static void xml(Memory memory) throws PropertyException, JAXBException{
// Creating the Document object
StringWriter document = new StringWriter();
// create JAXB context
JAXBContext context = JAXBContext.newInstance(memory.getClass());
//Marshaller marshaller = context.createMarshaller();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Marshaling the User object into a Document object
marshaller.marshal(memory, document);
//
String result = document.toString();
//System.out.println(result);
// create JAXB context
JAXBContext context = JAXBContext.newInstance(memory.getClass());
// Create deserialization object
Unmarshaller unmarshaller = context.createUnmarshaller();
// Create the object
InputStream stream = new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8));
Memory deserial = (Memory)unmarshaller.unmarshal(stream);
System.out.println(((Value)deserial.getModels().iterator().next().getValue()).getName());
}
}
- `Value.class`:
@EqualsAndHashCode
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
public class Value
implements
ValueInterface
{
@XmlID
@XmlAttribute
private String name;
private int attribute;
public Value() {}
public Value(
String name
,int attribute
) {
this.name = name;
this.attribute = attribute;
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAttribute() {return attribute;}
public void setAttribute(int attribute) {this.attribute = attribute;}
}
- `ValueInterface`:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes({
@JsonSubTypes.Type(value = Value.class)
})
public interface ValueInterface<
T extends ValueInterface
>
{
}
- `Model`:
@EqualsAndHashCode
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
public class Model<
V extends ValueInterface
>
{
@XmlID
@XmlAttribute
private String name;
@XmlIDREF
@XmlElements({
@XmlElement(type=Value.class)
})
private V value;
public Model() {}
public Model(String name, V value) {
this.name = name;
this.value = value;
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public V getValue() {return value;}
public void setValue(V value) {this.value = value;}
}
- `Memory`:
@EqualsAndHashCode
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
public class Memory{
@XmlElementWrapper(name="models")
@XmlElement(type=Model.class, name="model")
private Collection models;
@XmlElementWrapper(name="values")
@XmlElement(type=Value.class, name="value")
private Collection values;
public void setModels(Collection v){this.models = v;}
public Collection getModels(){return models;}
public void setValues(Collection v){this.values = v;}
public Collection getValues(){return values;}
}
- `POM.xml`:
4.0.0
org.mr
JacksonJSONDeserializationQuestion
0.0.1
jar
UTF-8
13
13
2.4.0-b180830.0359
1.1.1
3.0.2-b01
org.projectlombok
lombok
1.18.18
jar
org.junit.jupiter
junit-jupiter-api
5.6.0
test
org.junit.jupiter
junit-jupiter-params
5.6.0
test
org.junit.jupiter
junit-jupiter-engine
5.6.0
test
com.fasterxml.jackson.core
jackson-databind
2.12.3
jar
com.fasterxml.jackson.module
jackson-module-jaxb-annotations
2.12.3
jar
javax.xml.bind
jaxb-api
2.3.0
com.sun.xml.bind
jaxb-core
2.3.0
com.sun.xml.bind
jaxb-impl
2.3.0
[1]: https://stackoverflow.com/a/19396423
[2]: https://stackoverflow.com/a/63732985
[3]: https://zenidas.wordpress.com/recipes/jackson-deserialization-of-interfaces/
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.