使用 Apache Camel 2.25.0 将 JSON 转换为 CSV 时获取 com.thoughtworks.xstream.mapper.CannotResolveClassException

Getting com.thoughtworks.xstream.mapper.CannotResolveClassException while converting JSON to CSV using Apache Camel 2.25.0

我正在使用 Spring Boot 2.6.2、Apache Camel 2.25.0、JDK 1.8.

将 JSON 转换为 CSV

下面是我为此使用的 Camel Route 代码片段:

@Override
public void configure() throws Exception {
    // Receive from JSON file 
    from("file:files/input")
            .log("Received file - ${body}")
            // transform JSON to CSV format
            .unmarshal().json()

            .marshal().csv()
    
            // Write to output file
            .log("Writing file - ${body}")
            .to("file:files/output");
}

源代码:https://github.com/DebeshNayak/camel-json-to-csv

此应用程序构建成功,但在运行时出现以下错误:

2022-01-04 11:10:47.813  INFO 14788 --- [e://files/input] route1                                   : Received file - {
  "fname": "debesh",
  "lname": "nayak"
}
2022-01-04 11:10:47.862 ERROR 14788 --- [e://files/input] o.a.camel.processor.DefaultErrorHandler  : Failed delivery for (MessageId: ID-Debesh-Lenovo-i5-1641274844914-0-2 on ExchangeId: ID-Debesh-Lenovo-i5-1641274844914-0-1). Exhausted after delivery attempt: 1 caught: com.thoughtworks.xstream.mapper.CannotResolveClassException: fname

Message History
---------------------------------------------------------------------------------------------------------------------------------------
RouteId              ProcessorId          Processor                                                                        Elapsed (ms)
[route1            ] [route1            ] [file://files/input                                                            ] [        60]
[route1            ] [log1              ] [log                                                                           ] [        20]
[route1            ] [unmarshal1        ] [unmarshal[org.apache.camel.model.dataformat.JsonDataFormat@5bb0a7b2]          ] [        28]

Stacktrace
---------------------------------------------------------------------------------------------------------------------------------------

com.thoughtworks.xstream.mapper.CannotResolveClassException: fname
    at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:81) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.mapper.DynamicProxyMapper.realClass(DynamicProxyMapper.java:55) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.mapper.PackageAliasingMapper.realClass(PackageAliasingMapper.java:88) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.mapper.ClassAliasingMapper.realClass(ClassAliasingMapper.java:79) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.mapper.ArrayMapper.realClass(ArrayMapper.java:74) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.mapper.SecurityMapper.realClass(SecurityMapper.java:71) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:47) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.core.util.HierarchicalStreams.readClassType(HierarchicalStreams.java:29) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:133) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:32) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1487) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1455) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at org.apache.camel.dataformat.xstream.AbstractXStreamWrapper.unmarshal(AbstractXStreamWrapper.java:374) ~[camel-xstream-2.25.0.jar:2.25.0]
    at org.apache.camel.processor.UnmarshalProcessor.process(UnmarshalProcessor.java:69) ~[camel-core-2.25.0.jar:2.25.0]
    at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:548) ~[camel-core-2.25.0.jar:2.25.0]
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201) [camel-core-2.25.0.jar:2.25.0]
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:138) [camel-core-2.25.0.jar:2.25.0]
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:101) [camel-core-2.25.0.jar:2.25.0]
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201) [camel-core-2.25.0.jar:2.25.0]
    at org.apache.camel.component.file.GenericFileConsumer.processExchange(GenericFileConsumer.java:454) [camel-core-2.25.0.jar:2.25.0]
    at org.apache.camel.component.file.GenericFileConsumer.processBatch(GenericFileConsumer.java:223) [camel-core-2.25.0.jar:2.25.0]
    at org.apache.camel.component.file.GenericFileConsumer.poll(GenericFileConsumer.java:187) [camel-core-2.25.0.jar:2.25.0]
    at org.apache.camel.impl.ScheduledPollConsumer.doRun(ScheduledPollConsumer.java:174) [camel-core-2.25.0.jar:2.25.0]
    at org.apache.camel.impl.ScheduledPollConsumer.run(ScheduledPollConsumer.java:101) [camel-core-2.25.0.jar:2.25.0]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_312]
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) [na:1.8.0_312]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access1(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_312]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) [na:1.8.0_312]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_312]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_312]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_312]

在我的项目中,我在 pom.xml:

中使用下面的 Maven 依赖项
<properties>
    <java.version>1.8</java.version>
    <camel.version>2.25.0</camel.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-spring-boot-starter</artifactId>
        <version>${camel.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-stream-starter</artifactId>
        <version>${camel.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-jackson</artifactId>
        <version>${camel.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-xstream</artifactId>
        <version>${camel.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-csv</artifactId>
        <version>${camel.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

但是在使用 Apache Camel 3.4.2 时,它工作正常。

Camel 支持几个不同的 JSON 库,恰好 Camel 3 将其 default JSON 库从 XStream 给 Jackson,这可能就是代码在这里不起作用的原因。 (有关更改的信息,请参阅 CAMEL-5836)。每当您单独使用 .json() 关键字时,都会使用默认库。

所以如果你想在 Camel 2.x 中使用这段代码,或者以任何方式自定义 JSON marshalling/unmarshalling,你可以先显式实例化 Jackson,然后参考它在路线中,例如:

JacksonDataFormat json = new JacksonDataFormat();
// You can also set any Jackson options here

from("file:files/input")
    .log("Received file - ${body}")
    // transform JSON to CSV format, referring to the "json" object above
    .unmarshal(json)

    .marshal().csv()
    
    // Write to output file
    .log("Writing file - ${body}")
    .to("file:files/output");
}

使用 Jackson 比 XStream 简单得多,因为它可以解组为简单的 Java 对象,如 Map,无需太多配置。