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
相关产品推荐
相关产品推荐

