FasterXML / FasterXML/jackson-modules-base

MismatchedInputException for XmlIDREF in generic interface attribute

Aperta
#144 4 commenti 0 reazioni 0 assegnatari Vedi su GitHub
jaxb-annotations
Lingua principale
Java
Stelle
180
Fork
80
Merge medio
3h 26m
PR unite (30g)
1

Descrizione

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/

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.