Apache camel xml 反序列化失败

问题描述

我正在尝试使用 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 属性?