You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ElasticSearch MultiSearchResponse序列化异常的解决方法咨询

解决Elasticsearch MultiSearchResponse序列化异常:No serializer found for class org.elasticsearch.common.text.Text

这个问题我之前也碰到过,本质是Jackson无法序列化Elasticsearch内部的Text类——你代码里启用了高亮,返回的HighlightField里的fragments是Text[]类型,而Jackson默认没有这个类的序列化器,而且Text类没有暴露可序列化的属性,所以就抛出了这个异常。

下面给你三种解决思路,按推荐程度排序:


1. 手动转换为自定义DTO(最推荐)

直接返回ES原生的MultiSearchResponse不是个好主意,一来容易遇到序列化问题,二来返回的结构太耦合ES内部实现,后续版本升级可能出问题。最好的做法是把搜索结果提取出来,封装成自己的DTO再返回。

首先定义两个DTO类:

// 高亮片段的DTO
public class HighlightItem {
    private String field;
    private List<String> fragments;

    // 构造器、Getter、Setter省略,按需添加
    public HighlightItem(String field, List<String> fragments) {
        this.field = field;
        this.fragments = fragments;
    }
}

// 单个搜索结果的DTO
public class SearchResult {
    private Map<String, Object> source;
    private List<HighlightItem> highlights;

    // 构造器、Getter、Setter省略
    public SearchResult(Map<String, Object> source, List<HighlightItem> highlights) {
        this.source = source;
        this.highlights = highlights;
    }
}

然后修改你的业务方法,把ES响应转换成DTO列表:

public ResponseEntity<List<List<SearchResult>>> findByContens(Integer page, Integer size, String[] split) {
    MultiSearchRequestBuilder multiSearchRequestBuilder = client.prepareMultiSearch();
    for (String key : split) {
        SearchRequestBuilder builder = client.prepareSearch()
                .setQuery(QueryBuilders.matchQuery("attachment.content", key))
                .highlighter(new HighlightBuilder().preTags("<strong>").postTags("</strong>").field("attachment.content"))
                .setFrom(page).setSize(size);
        multiSearchRequestBuilder.add(builder);
    }
    MultiSearchResponse multiSearchResponse = multiSearchRequestBuilder.get();

    // 转换为自定义DTO集合
    List<List<SearchResult>> allResults = new ArrayList<>();
    for (MultiSearchResponse.Item item : multiSearchResponse.getResponses()) {
        if (item.isFailure()) {
            // 处理搜索失败的情况,比如添加空列表或错误标记
            allResults.add(Collections.emptyList());
            continue;
        }

        SearchResponse response = item.getResponse();
        List<SearchResult> resultList = new ArrayList<>();
        for (SearchHit hit : response.getHits().getHits()) {
            // 提取源数据
            Map<String, Object> sourceMap = hit.getSourceAsMap();
            // 处理高亮片段:把Text[]转成String列表
            List<HighlightItem> highlights = new ArrayList<>();
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            for (Map.Entry<String, HighlightField> entry : highlightFields.entrySet()) {
                List<String> fragmentStrings = Arrays.stream(entry.getValue().getFragments())
                        .map(Text::toString)
                        .collect(Collectors.toList());
                highlights.add(new HighlightItem(entry.getKey(), fragmentStrings));
            }
            resultList.add(new SearchResult(sourceMap, highlights));
        }
        allResults.add(resultList);
    }

    return ResponseEntity.ok(allResults);
}

这种方式完全可控,返回的JSON结构清晰,也不会依赖ES的内部类,是生产环境的首选方案。


2. 自定义Jackson序列化器处理Text类

如果不想改返回结构,可以给Jackson写一个自定义序列化器,专门处理Text类型,把它直接转成字符串输出。

先写序列化器:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.elasticsearch.common.text.Text;

import java.io.IOException;

public class ElasticsearchTextSerializer extends JsonSerializer<Text> {
    @Override
    public void serialize(Text text, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        // 直接输出Text的字符串内容
        gen.writeString(text.toString());
    }
}

然后注册到Spring的Jackson配置中:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.elasticsearch.common.text.Text;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

@Configuration
public class JacksonConfig {
    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = converter.getObjectMapper();
        
        // 注册自定义序列化器
        SimpleModule module = new SimpleModule();
        module.addSerializer(Text.class, new ElasticsearchTextSerializer());
        objectMapper.registerModule(module);
        
        converter.setObjectMapper(objectMapper);
        return converter;
    }
}

这样Jackson就知道怎么序列化Text对象了,你的原有代码不需要大改,就能正常返回JSON。


3. 禁用Jackson的FAIL_ON_EMPTY_BEANS特性(不推荐)

异常提示里提到可以禁用SerializationFeature.FAIL_ON_EMPTY_BEANS,这能快速解决报错,但会隐藏其他潜在的序列化问题,而且ES内部类序列化后的结构可能不符合预期,所以只适合临时调试用。

配置方式:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

@Configuration
public class JacksonConfig {
    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = converter.getObjectMapper();
        
        // 禁用空bean序列化失败的特性
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        
        converter.setObjectMapper(objectMapper);
        return converter;
    }
}

内容的提问来源于stack exchange,提问作者wangbin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:09:11