Open
Description
Elastic.Clients.Elasticsearch version: 8.17.1
Elasticsearch version: 8.17.4
.NET runtime version: 8
Operating system version: Windows 10
Description of the problem including expected versus actual behavior:
The client throws UnexpectedTransportException
when deserializing the ES response, if the index contains a custom token filter (i.e., when "type"
does not match any predefined value).
Steps to reproduce:
- Install any plugin that includes a non-standard token filter.
- Create an index manually and specify the custom filter in
settings.index.analysis.filter
section.
Example:
{
"my_index": {
"settings": {
"index": {
"analysis": {
"filter": {
"my_custom_filter": {
"type": "my_custom_filter"
}
}
}
}
}
}
}
- Attempt to read the index settings using
Indices.GetAsync
API.
Expected behavior
Deserialization should not fail. The custom filter should be deserialized into a CLR object.
I found a workaround, although it feels a bit like a hack:
- Create a custom extension of
ElasticsearchClientSettings
. This allows customization of theRequestResponseSerializer
with a custom converter.
public class CustomElasticsearchClientSettings : ElasticsearchClientSettings
{
public CustomElasticsearchClientSettings(NodePool nodePool,
SourceSerializerFactory? sourceSerializer = null,
SourceSerializerFactory? requestResponseSerializer = null)
: base(nodePool, sourceSerializer)
{
if (requestResponseSerializer != null)
UseThisRequestResponseSerializer = requestResponseSerializer(UseThisRequestResponseSerializer, this);
}
}
- Define a basic model for non-standard token filters.
public class CustomTokenFilter(string type)
: ITokenFilter
{
public string? Type { get; } = type;
}
- Add a converter that deserializes an unknown filter into the common model.
public class CustomTokenFilterConverter : JsonConverter<ITokenFilter>
{
public override ITokenFilter? Read(ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
using var doc = JsonDocument.ParseValue(ref reader);
var root = doc.RootElement;
var type = root.GetProperty("type").GetString();
return new CustomTokenFilter(type);
}
public override void Write(Utf8JsonWriter writer, ITokenFilter value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteString("type", filter.Type);
writer.WriteEndObject();
}
}
- Register the custom converter.
var settings = new CustomElasticsearchClientSettings(nodePool, requestResponseSerializer: (serializer, settings) =>
{
serializer.TryGetJsonSerializerOptions(out var options);
options.Converters.Add(new CustomTokenFilterConverter());
return serializer;
});
var client = new ElasticsearchClient(settings);