问题描述
我正在尝试使用 apache camel jackson xml 反序列化 xml,但发生了一些我无法解释的奇怪事情。这是我试图反序列化的 xml(它是简单的 xmltv xml 文件):
<?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>
这是我的 POJO:
电视.java
@XmlRootElement(name = "tv")
@JsonIgnoreProperties(ignoreUnkNown = true)
@Data
public class Tv {
@XmlElement(name = "programme")
@JacksonXmlElementWrapper(useWrapping = false)
private List<Programme> programmes = new ArrayList<>();
}
程序.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;
}
其他 POJO 并不重要。所以这里是路由配置:
@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")
}
使用此设置和我在开头提供的 xml 发生以下异常:
com.fasterxml.jackson.databind.exc.MismatchedInputException:无法构造 Programme
的实例(尽管至少存在一个 Creator):没有从字符串值反序列化的字符串参数构造函数/工厂方法('20190427' )
在 [来源: (BufferedInputStream);第 9 行,第 20 列](通过参考链:com.azdio.mdw.ingest.epg.xmltv.common.domain.Tv["programme"]->java.util.ArrayList[1])
在 com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
在 com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1429)
在 com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1059)
在 com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)
在 com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323)
在 com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializefromString(BeanDeserializerBase.java:1373)
在 com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)
在 com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
在 com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:114)
在 com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:286)
在 com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:245)
在 com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27)
在 com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
在 com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
在 com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
在 com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:114)
在 com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4202)
在 com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3242)
在 org.apache.camel.component.jacksonxml.JacksonXMLDataFormat.unmarshal(JacksonXMLDataFormat.java:193)
在 org.apache.camel.processor.UnmarshalProcessor.process(UnmarshalProcessor.java:69)
在 org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:548)
在 org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201)
在 org.apache.camel.processor.Pipeline.process(Pipeline.java:138)
在 org.apache.camel.processor.Pipeline.process(Pipeline.java:101)
在 org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201)
在 org.apache.camel.component.file.GenericFileConsumer.processExchange(GenericFileConsumer.java:454)
在 org.apache.camel.component.file.GenericFileConsumer.processBatch(GenericFileConsumer.java:223)
在 org.apache.camel.component.file.GenericFileConsumer.poll(GenericFileConsumer.java:187)
在 org.apache.camel.impl.ScheduledPollConsumer.doRun(ScheduledPollConsumer.java:174)
在 org.apache.camel.impl.ScheduledPollConsumer.run(ScheduledPollConsumer.java:101)
我花了很多时间才明白这意味着什么。所以它似乎接受结束标签作为标签的结尾,然后它试图从标签值中创建一个新的 Program 实例,但它失败了,因为它不知道如何去做。为了证明我自己,我移动了标签末尾的标签,因此 xml 如下所示:
<?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>
这次当我运行应用程序时,xml 被正确反序列化,没有任何异常。那么有人可以解释一下为什么会这样吗?我是不是在某个地方犯了错误?
我使用的是 apache camel 2.25.3 和相同版本的 camel-jacksonxml。
编辑:
我成功地将问题隔离给了jackson:
JacksonXmlModule jacksonXmlModule = new JacksonXmlModule();
jacksonXmlModule.setDefaultUseWrapper(false);
ObjectMapper objectMapper = new XmlMapper(jacksonXmlModule);
objectMapper.registerModule(new JaxbAnnotationModule());
objectMapper.readValue(new File("xmlFilePath"),Tv.class);
这会导致同样的异常。
解决方法
所以我发现了导致此异常的原因,尽管我不完全确定为什么会这样。在 jackson 的对象映射器的调试会话之后,我发现问题出在 actor 标签的 nameId 属性中。当我有以下情况时,解串器似乎无法决定如何去做:
@XmlElement(name = "actor")
@JacksonXmlElementWrapper(useWrapping = false)
private List<String> actors;
所以我找到的一个解决方案(我不确定这是最正确的)是创建另一个 pojo CreditsElement.java:
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class CreditsElement {
@XmlValue
private String value;
}
我不包括 nameId 属性,因为我不需要它。除了该解决方案之外,还有没有更好的方法来映射它而无需创建另一个类并使用 List 属性?