I'm trying to deserialize an xml using apache camel jackson xml and something strange happens which I don't have an explanation for. Here's the xml I'm trying to deserialize(it's simple xmltv xml file):
<?xml version="1.0" encoding="UTF-8"?>
<tv>
<programme start="20210102000300 +0000" stop="20210102003000 +0000" channel="XTV100005403" recordable="Y" npvrenable="Y" id="SH032096260000.20210102000300.44345" type="program">
<title lang="en">Barbados Ninja Throwdown</title>
<desc lang="en">Contestants compete to complete a challenging obstacle course the fastest.</desc>
<credits>
<actor nameId="1288485">Wayne Simmons</actor>
</credits>
<date>20190427</date>
<category>Reality</category>
<category>Action sports</category>
<category>Action</category>
<category>Adventure</category>
<releaseType>TV</releaseType>
<extentionInfo>
<key>releaseStatus</key>
<value>1</value>
</extentionInfo>
</programme>
</tv>
Here are my POJOs:
Tv.java
@XmlRootElement(name = "tv")
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class Tv {
@XmlElement(name = "programme")
@JacksonXmlElementWrapper(useWrapping = false)
private List<Programme> programmes = new ArrayList<>();
}
Programme.java
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class Programme {
@XmlAttribute(name = "id")
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
private String id;
@XmlAttribute(name = "type")
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
private String type;
@XmlAttribute(name = "start", required = true)
@JsonDeserialize(using = OffsetDateTimeDeserializer.class)
private OffsetDateTime start;
@XmlAttribute(name = "stop")
@JsonDeserialize(using = OffsetDateTimeDeserializer.class)
private OffsetDateTime stop;
@XmlAttribute(name = "pdc-start")
@JsonDeserialize(using = OffsetDateTimeDeserializer.class)
private OffsetDateTime pdcStart;
@XmlAttribute(name = "vps-start")
@JsonDeserialize(using = OffsetDateTimeDeserializer.class)
private OffsetDateTime vpsStart;
@XmlAttribute(name = "showview")
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
private String showview;
@XmlAttribute(name = "videoplus")
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
private String videoplus;
@XmlAttribute(name = "channel", required = true)
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
private String channel;
@XmlAttribute(name = "clumpidx")
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
private String clumpidx;
@XmlElement(name = "title", required = true)
@JacksonXmlElementWrapper(useWrapping = false)
private List<Label> titles;
@XmlElement(name = "sub-title")
@JacksonXmlElementWrapper(useWrapping = false)
private List<Label> subTitle;
@XmlElement(name = "desc")
@JacksonXmlElementWrapper(useWrapping = false)
private List<Label> descriptions;
@XmlElement(name = "credits")
private Credits credits;
private String date;
@XmlElement(name = "category")
@JacksonXmlElementWrapper(useWrapping = false)
private List<Label> categories;
@XmlElement(name = "keyword")
@JacksonXmlElementWrapper(useWrapping = false)
private List<Label> keywords;
private Label language;
@XmlElement(name = "orig-language")
private Label origLanguage;
private Length length;
@XmlElement(name="icons")
@JacksonXmlElementWrapper(useWrapping = false)
private List<Icon> icons;
@XmlElement(name = "url")
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> urls;
@XmlElement(name = "country")
@JacksonXmlElementWrapper(useWrapping = false)
private List<Label> countries;
@XmlElement(name = "episode-num")
@JacksonXmlElementWrapper(useWrapping = false)
private List<EpisodeNum> episodeNumbers;
private Video video;
private Audio audio;
@XmlElement(name = "previously-shown")
private PreviouslyShown previouslyShown;
private Label premiere;
@XmlElement(name = "last-chance")
private Label lastChance;
@XmlElement(name = "new")
private Label isNew;
@JacksonXmlElementWrapper(useWrapping = false)
private List<Subtitles> subtitles;
@XmlElement(name = "rating")
@JacksonXmlElementWrapper(useWrapping = false)
private List<Rating> ratings;
@XmlElement(name = "star-rating")
@JacksonXmlElementWrapper(useWrapping = false)
private List<StarRating> starRatings;
@XmlElement(name = "review")
@JacksonXmlElementWrapper(useWrapping = false)
private List<Review> reviews;
}
Credits.java
@Data
public class Credits {
@XmlElement(name = "director")
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> directors;
@XmlElement(name = "actor")
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> actors;
@XmlElement(name = "writer")
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> writers;
@XmlElement(name = "adapter")
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> adapters;
@XmlElement(name = "producer")
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> producers;
@XmlElement(name = "composer")
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> composers;
@XmlElement(name = "editor")
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> editors;
@XmlElement(name = "presenter")
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> presenters;
@XmlElement(name = "commentator")
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> commentators;
@XmlElement(name = "guest")
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> guests;
}
The other POJOs are not important. So here is the route configuration:
@Override
public void configure() throws Exception {
final JacksonXMLDataFormat dataFormat = new JacksonXMLDataFormat();
dataFormat.setUnmarshalType(Tv.class);
from("file:///someLocation")
.routeId(getClass().getSimpleName())
.autoStartup(true)
.unmarshal(dataFormat)
.process("someProcessorBean")
}
With this setup and the xml I provided at the beginning the following exception occurs:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of
Programme
(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('20190427') at [Source: (BufferedInputStream); line: 9, column: 20] (through reference chain: com.azdio.mdw.ingest.epg.xmltv.common.domain.Tv["programme"]->java.util.ArrayList[1]) at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63) at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1429) at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1059) at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371) at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373) at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161) at com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:114) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:286) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:245) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27) at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129) at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151) at com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:114) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4202) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3242) at org.apache.camel.component.jacksonxml.JacksonXMLDataFormat.unmarshal(JacksonXMLDataFormat.java:193) at org.apache.camel.processor.UnmarshalProcessor.process(UnmarshalProcessor.java:69) at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:548) at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201) at org.apache.camel.processor.Pipeline.process(Pipeline.java:138) at org.apache.camel.processor.Pipeline.process(Pipeline.java:101) at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201) at org.apache.camel.component.file.GenericFileConsumer.processExchange(GenericFileConsumer.java:454) at org.apache.camel.component.file.GenericFileConsumer.processBatch(GenericFileConsumer.java:223) at org.apache.camel.component.file.GenericFileConsumer.poll(GenericFileConsumer.java:187) at org.apache.camel.impl.ScheduledPollConsumer.doRun(ScheduledPollConsumer.java:174) at org.apache.camel.impl.ScheduledPollConsumer.run(ScheduledPollConsumer.java:101)
It took me a lot of time to understand what this means. So it seems that it accepts the closing tag as end of the tag and after that it tries to create a new Programme instance out of the tag value and it fails because it doesn't know how to do it. In order to prove that for myself I moved the tag at the end of the tag so the xml looked like the following:
<?xml version="1.0" encoding="UTF-8"?>
<tv>
<programme start="20210102000300 +0000" stop="20210102003000 +0000" channel="XTV100005403" recordable="Y" npvrenable="Y" id="SH032096260000.20210102000300.44345" type="program">
<title lang="en">Barbados Ninja Throwdown</title>
<desc lang="en">Contestants compete to complete a challenging obstacle course the fastest.</desc>
<date>20190427</date>
<category>Reality</category>
<category>Action sports</category>
<category>Action</category>
<category>Adventure</category>
<releaseType>TV</releaseType>
<extentionInfo>
<key>releaseStatus</key>
<value>1</value>
</extentionInfo>
<credits>
<actor nameId="1288485">Wayne Simmons</actor>
</credits>
</programme>
</tv>
This time when I run the application the xml is correctly deserialized without any exceptions. So can someone explain me why it's acting like that and did I make a mistake somewhere?
I'm using apache camel 2.25.3 and respectively the same version of camel-jacksonxml.
Edit:
I succeeded to isolate the problem just to jackson:
JacksonXmlModule jacksonXmlModule = new JacksonXmlModule();
jacksonXmlModule.setDefaultUseWrapper(false);
ObjectMapper objectMapper = new XmlMapper(jacksonXmlModule);
objectMapper.registerModule(new JaxbAnnotationModule());
objectMapper.readValue(new File("xmlFilePath"), Tv.class);
This leads to the same exception.