无法解码密钥的 json 类型:Spring Cloud Data Flow 流中的 file_name

Posted

技术标签:

【中文标题】无法解码密钥的 json 类型:Spring Cloud Data Flow 流中的 file_name【英文标题】:Could not decode json type for key: file_name in a Spring Cloud Data Flow stream 【发布时间】:2020-03-28 01:20:22 【问题描述】:

我使用 Spring Cloud Data Flow 设置读取 CSV 文件的流,使用自定义处理器对其进行转换并记录它:

stream create --name testsourcecsv --definition "file --mode=lines --directory=D:/toto/ --file.filename-pattern=adresses-28.csv --maxMessages=1000 | csvToMap --spring.cloud.stream.bindings.output.content-type=application/json | log --spring.cloud.stream.bindings.input.content-type=application/json" --deploy

文件和 csvToMap 应用程序工作正常,但在日志应用程序中我看到这种异常,对于每条记录:

2019-12-03 11:32:46.500 ERROR 1328 --- [container-0-C-1] o.s.c.s.b.k.KafkaMessageChannelBinder$5  : Could not decode json type: adresses-28.csv for key: file_name

com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'adresses': was expecting ('true', 'false' or 'null')
 at [Source: (byte[])"adresses-28.csv"; line: 1, column: 10]
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1804) ~[jackson-core-2.9.9.jar!/:2.9.9]
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:703) ~[jackson-core-2.9.9.jar!/:2.9.9]
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._reportInvalidToken(UTF8StreamJsonParser.java:3532) ~[jackson-core-2.9.9.jar!/:2.9.9]
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._handleUnexpectedValue(UTF8StreamJsonParser.java:2627) ~[jackson-core-2.9.9.jar!/:2.9.9]
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._nextTokenNotInObject(UTF8StreamJsonParser.java:832) ~[jackson-core-2.9.9.jar!/:2.9.9]
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.nextToken(UTF8StreamJsonParser.java:729) ~[jackson-core-2.9.9.jar!/:2.9.9]
    at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4141) ~[jackson-databind-2.9.9.jar!/:2.9.9]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4000) ~[jackson-databind-2.9.9.jar!/:2.9.9]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3091) ~[jackson-databind-2.9.9.jar!/:2.9.9]
    at org.springframework.cloud.stream.binder.kafka.BinderHeaderMapper.lambda$toHeaders$1(BinderHeaderMapper.java:268) ~[spring-cloud-stream-binder-kafka-2.1.4.RELEASE.jar!/:2.1.4.RELEASE]
    at java.lang.Iterable.forEach(Iterable.java:75) ~[na:1.8.0_202]
    at org.springframework.cloud.stream.binder.kafka.BinderHeaderMapper.toHeaders(BinderHeaderMapper.java:251) ~[spring-cloud-stream-binder-kafka-2.1.4.RELEASE.jar!/:2.1.4.RELEASE]

file_relativePath 标头也会引发此异常。我不明白为什么 spring-kafka 试图将它们读取为 JSON。

此外,日志接收器以正确的方式记录我的记录:

2019-12-03 11:32:46.516  INFO 1328 --- [container-0-C-1] log-sink                                 : "code_postal":"28200","id_fantoir":"28211_0127","source_nom_voie":"inconnue","numero":"1","code_insee":28211,"lon":1.260462,"code_insee_ancienne_commune":"","nom_afnor":"RUE DU VIEUX MOULIN","nom_voie":"Rue du Vieux Moulin","nom_ld":"","libelle_acheminement":"LOGRON","source_position":"inconnue","nom_commune":"Logron","nom_ancienne_commune":"","x":570633.27,"y":6784246.2,"alias":"","id":"28211_0127_00001","rep":"","lat":48.145756

我出于调试目的在我的 csvToMap 处理器中记录了 kafka 标头,给了我:

2019-12-03 11:32:37.042  INFO 10788 --- [container-0-C-1] c.d.streams.processor.CsvToMapProcessor  : headers sequenceNumber=152963, file_name=adresses-28.csv, sequenceSize=0, deliveryAttempt=1, kafka_timestampType=CREATE_TIME, file_originalFile=NonTrustedHeaderType [headerValue="D:\\toto\\adresses-28.csv", untrustedType=java.io.File], kafka_receivedMessageKey=null, kafka_receivedTopic=testsourcecsv.file, file_relativePath=adresses-28.csv, kafka_offset=430949, scst_nativeHeadersPresent=true, kafka_consumer=org.apache.kafka.clients.consumer.KafkaConsumer@7c3e63db, correlationId=9547c02d-e617-d981-f9b5-8df231530f66, kafka_receivedPartitionId=0, contentType=text/plain, kafka_receivedTimestamp=1575299282558, kafka_groupId=testsourcecsv

所以我绝对不明白为什么日志接收器会尝试解码 file_name 和 file_relativePath 标头。

我设置了一个本地环境:

Windows 7 Spring CDF 服务器 v 2.2.1.REALEASE Spring Cloud Skipper v 2.1.2.RELEASE Spring CDF shell v 2.2.1.RELEASE Kafka 2.12-2.3.0

我的 csvToMap 处理器定义如下:

    @Component
    public class CsvToMapProcessor 
        private static final Logger LOGGER = LoggerFactory.getLogger(CsvToMapProcessor.class);

        @Autowired
        @Qualifier("csvMapper")
        private ObjectReader csvMapper;

        @Autowired
        @Qualifier("jsonWriter")
        private ObjectWriter jsonWriter;

        @Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
        public Map<String, Object> transform(String csvLine, @Headers Map<String, Object> headers) 
            try 
                LOGGER.info("headers ", headers);
                Map<String, Object> map = csvMapper.readValue(csvLine);
                return map;
             catch (JsonProcessingException e) 
                LOGGER.error("An error occurs while reading CSV line  : ", csvLine, e.getMessage());
                LOGGER.debug(e.getMessage(), e);
                return null;
            
        
    

与这个父母:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

还有这个 Spring 云版本:

<spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>

我做错了什么导致这个问题?

【问题讨论】:

【参考方案1】:

所以:

结合 spring-cloud Hoxton 版本,spring-cloud-stream 版本为 3.0.0.RELEASE :
[INFO] +- org.springframework.cloud:spring-cloud-starter-stream-kafka:jar:3.0.0.RELEASE:compile
[INFO] |  \- org.springframework.cloud:spring-cloud-stream-binder-kafka:jar:3.0.0.RELEASE:compile
[INFO] |     +- org.springframework.cloud:spring-cloud-stream-binder-kafka-core:jar:3.0.0.RELEASE:compile
[INFO] |     |  \- org.springframework.integration:spring-integration-kafka:jar:3.2.1.RELEASE:compile
[INFO] |     \- org.springframework.kafka:spring-kafka:jar:2.3.3.RELEASE:compile
log-sink-app 2.1.2 使用 spring-cloud-stream v 2.1.4.RELEASE :
[INFO] +- org.springframework.cloud:spring-cloud-starter-stream-kafka:jar:2.1.4.RELEASE:compile
[INFO] |  \- org.springframework.cloud:spring-cloud-stream-binder-kafka:jar:2.1.4.RELEASE:compile
[INFO] |     +- org.springframework.cloud:spring-cloud-stream-binder-kafka-core:jar:2.1.4.RELEASE:compile
[INFO] |     |  \- org.springframework.integration:spring-integration-kafka:jar:3.1.0.RELEASE:compile
[INFO] |     \- org.springframework.kafka:spring-kafka:jar:2.2.8.RELEASE:compile

正如spring-kafka 2.3.3 documentation DefaultKafkaHeaderMapper.setEncodeStrings 方法所说:

如果出站记录的使用者使用 Spring for Apache Kafka 版本低于 2.3,则设置为 true

log-sink 应用程序实际上使用 spring-kafka v 2.2.8,所以我必须将其设置为 true,使用自定义的标头映射器:

    @Bean("kafkaBinderHeaderMapper")
    public KafkaHeaderMapper kafkaBinderHeaderMapper() 
        DefaultKafkaHeaderMapper mapper = new DefaultKafkaHeaderMapper();
        mapper.setEncodeStrings(true);
        return mapper;
    

但是如果我这样做,日志接收器不会记录任何内容,因为它无法理解由 DefaultKafkaHeaderMapper 编码的 contentType 标头。并且团队提供了BinderHeaderMapper 来解决这个问题:

Apache Kafka 的自定义标头映射器。这与 spring Kafka 中的 DefaultKafkaHeaderMapper 相同。这是为了解决 Spring Cloud Stream 3.0.x 和 2.x 应用程序之间的一些互操作性问题,其中在标头中作为常规 MimeType 传递的 mime 类型未正确反序列化

所以我必须在我的应用中配置一个自定义的 BinderHeaderMapper :

    @Bean("kafkaBinderHeaderMapper")
    public KafkaHeaderMapper kafkaBinderHeaderMapper() 
        BinderHeaderMapper mapper = new BinderHeaderMapper();
        mapper.setEncodeStrings(true);
        return mapper;
    

一切正常。

【讨论】:

【参考方案2】:

您在--file.filename-pattern 中设置的值似乎有问题。您能否检查您是否确实传递了符合AntPathMatcher 的值(文件名模式属性基于此路径匹配器)?

如果你尝试类似--file.filename-pattern=*.csv 会发生什么?

【讨论】:

感谢您的回复。我认为我的文件名模式没有任何问题,因为文件源实际上读取了我的 csv 文件。所以模式有效。而且我不明白模式中的错误如何导致某些标头的 JSON 解析。

以上是关于无法解码密钥的 json 类型:Spring Cloud Data Flow 流中的 file_name的主要内容,如果未能解决你的问题,请参考以下文章

IIS错误:在唯一密钥属性“fileExtension”设置为“.json”时,无法添加类型为“mimeMap”的重复集合项

“[Root]”类型的值没有成员“commit”。我如何解码 JSON 根数组

url解码和发布数据后出错

base64无法解码pb+

Swift:无法使用类型为“([Idea],来自:Data)”的参数列表调用“解码”

Swift 4 可编码;如何使用单个根级密钥解码对象