elasticsearch-net
elasticsearch-net copied to clipboard
JsonNetSerializer - Failed to serialize anonymous type: Nest.SearchDescriptor
NEST/Elasticsearch.Net version: 7.17.5
Elasticsearch version: 7.17.2
.NET runtime version: 6.0.203
Operating system version: Windows 11
Description of the problem including expected versus actual behavior:
I'm trying to serialize an object that represents a Function Score. This function score has the following structure:
{ "size": 100,
"query": {
"bool": {
"must": [
{
"function_score": {
"functions": [
{
"live_scorer": {
"target_field_name": "product_id",
"endpoint": "{endpoint}",
"params": "{\"parameter\": \"value\"}"
},
"weight": 1.0
}
],
"score_mode": "max"
}
}
]
}
}
}
To represent this structure, a class and interface were created like this:
public class LiveScorerScoreFunction : ILiveScorerScoreFunction
{
public LiveScorerScoreFunction(Dictionary<string, object> fields, double weight)
{
this.Fields = fields;
this.Weight = weight;
}
[DataMember(Name = "live_scorer")]
public Dictionary<string, object> Fields { get; set; }
[DataMember(Name = "filter")]
public QueryContainer Filter { get; set ; }
[DataMember(Name = "weight")]
public double? Weight { get; set; }
}
[JsonFormatter(typeof(LiveScorerFormatter))]
public interface ILiveScorerScoreFunction : IScoreFunction
{
[DataMember(Name = "live_scorer")]
[JsonFormatter(typeof(Dictionary<string, object>))]
Dictionary<string, object> Fields { get; set; }
}
And about JsonFormatter
, it was created like this with the following Serialize
method:
public class LiveScorerFormatter : IJsonFormatter<ILiveScorerScoreFunction>
{
private static readonly AutomataDictionary AutomataDictionary = new AutomataDictionary
{
{ "params", 0 }
};
public ILiveScorerScoreFunction Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
{
...
}
public void Serialize(ref JsonWriter writer, ILiveScorerScoreFunction value, IJsonFormatterResolver formatterResolver)
{
if (value == null
|| value.Fields == null
|| value.Fields.Count == 0)
{
writer.WriteNull();
return;
}
var dictionaryFormatter = DynamicGenericResolver
.Instance
.GetFormatter<Dictionary<string, object>>();
dictionaryFormatter.Serialize(ref writer, value.Fields, formatterResolver);
}
}
This is how the query is being set up e.g.:
private IScoreFunction FunctionLiveScore(string parameterValue, double weight = 1)
{
var liveScorerFields = new Dictionary<string, object> {
{ "target_field_name", "product_id"},
{ "endpoint", @"http://localhost:6639/v1/"},
{ "params", string.Format("{{ \"contextId\": \"{0}\" }}", parameterValue)}
};
return new LiveScorerScoreFunction(liveScorerFields, weight);
}
public SearchDescriptor<Product> CreateSearchDescriptor(string parameterValue)
{
var queryContainer = Query<Product>
.FunctionScore(
fs => fs
.Functions(new List<IScoreFunction>() { this.CreateFunctionLiveScore(parameterValue) })
.ScoreMode(FunctionScoreMode.Max));
return new SearchDescriptor<Product>()
.Index("indexName")
.Size(100)
.DocValueFields(t => t.Field("product_id"))
.Query(
q => q.Bool(
b => b
.Must(queryContainer)));
}
And when I try to run this code, I get the following error:
{
"AnonymousType": "Nest.SearchDescriptor`1[[Domain.Model.Product, Domain.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], Nest, Version=7.0.0.0, Culture=neutral, PublicKeyToken=96c599bbe3e70f5d",
"Message": "Failed to serialize anonymous type: Nest.SearchDescriptor`1[Domain.Model.Product].",
"Data": {},
"InnerException": {
"ClassName": "System.Exception",
"Message": "Can not write function score json for LiveScorerScoreFunction",
"Data": {},
"InnerException": null,
"HelpURL": null,
"StackTraceString": " at Nest.ScoreFunctionJsonFormatter.Serialize(JsonWriter& writer, IScoreFunction value, IJsonFormatterResolver formatterResolver)\r\n at Elasticsearch.Net.Utf8Json.Formatters.CollectionFormatterBase`4.Serialize(JsonWriter& writer, TCollection value, IJsonFormatterResolver formatterResolver)\r\n at Elasticsearch.Net.Nest_IFunctionScoreQueryFormatter3.Serialize(JsonWriter& , IFunctionScoreQuery , IJsonFormatterResolver )\r\n at Elasticsearch.Net.Nest_IQueryContainerFormatter1.Serialize(JsonWriter& , IQueryContainer , IJsonFormatterResolver )\r\n at Nest.QueryContainerCollectionFormatter.Serialize(JsonWriter& writer, IEnumerable`1 value, IJsonFormatterResolver formatterResolver)\r\n at Elasticsearch.Net.Nest_IBoolQueryFormatter2.Serialize(JsonWriter& , IBoolQuery , IJsonFormatterResolver )\r\n at Elasticsearch.Net.Nest_IQueryContainerFormatter1.Serialize(JsonWriter& , IQueryContainer , IJsonFormatterResolver )\r\n at Serialize(Byte[][] , Object[] , JsonWriter& , SearchDescriptor`1 , IJsonFormatterResolver )\r\n at Elasticsearch.Net.Utf8Json.Resolvers.DynamicMethodAnonymousFormatter`1.Serialize(JsonWriter& writer, T value, IJsonFormatterResolver formatterResolver)",
"RemoteStackTraceString": null,
"RemoteStackIndex": 0,
"ExceptionMethod": null,
"HResult": -2146233088,
"Source": "Nest",
"WatsonBuckets": null
},
"HelpLink": null,
"Source": "Elasticsearch.Net",
"HResult": -2146233076,
"StackTrace": " at Elasticsearch.Net.Utf8Json.Resolvers.DynamicMethodAnonymousFormatter`1.Serialize(JsonWriter& writer, T value, IJsonFormatterResolver formatterResolver)\r\n at Elasticsearch.Net.Utf8Json.JsonSerializer.SerializeUnsafe[T](T value, IJsonFormatterResolver resolver)\r\n at Elasticsearch.Net.Utf8Json.JsonSerializer.Serialize[T](Stream stream, T value, IJsonFormatterResolver resolver)\r\n at Elasticsearch.Net.DiagnosticsSerializerProxy.Serialize[T](T data, Stream stream, SerializationFormatting formatting)\r\n at Elasticsearch.Net.ElasticsearchSerializerExtensions.SerializeToString[T](IElasticsearchSerializer serializer, T data, IMemoryStreamFactory memoryStreamFactory, SerializationFormatting formatting)\r\n at Elasticsearch.Net.ElasticsearchSerializerExtensions.SerializeToString[T](IElasticsearchSerializer serializer, T data, SerializationFormatting formatting)\r\n at Data.Elasticsearch.ElasticsearchCluster.SearchAsync(Int32 tenantId, String contextId, Boolean isFreeTextSearch) in C:\\Users\\username\\source\\Playground\\custom-function-score-elasticsearch-using-nest\\src\\Data.Elasticsearch\\ElasticsearchCluster.cs:line 49"
}
I tried an implementation based on the guidelines in the documentation: https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.17/custom-serialization.html and created the ConnectionSettings
below however, the serializer it had created to be able to serialize this untriggered object and an empty JSON is returned.
Provide ConnectionSettings
(if relevant):
new ConnectionSettings(pool,
sourceSerializer: (builtin, settings) =>
new JsonNetSerializer(builtin, settings,
() => new JsonSerializerSettings { NullValueHandling = NullValueHandling.Include },
(connectionSettingsAwareContractResolver) => { new LiveScoreFunctionNetSerializer(connectionSettingsAwareContractResolver); }));
Expected behavior
Should serialize the object using the JsonFormatter (LiveScorerFormatter
) that was created. Or should trigger the custom serializer (LiveScoreFunctionNetSerializer
) that was created, via ConnectionSettings.
Hi @MackMendes
just to clarify. If you set a breakpoint on your custom Serialize
function, it doesn't get hit?
Hi @flobernd,
Thank you for your interaction on this issue. 🙂
Unfortunately, it doesn't hit the Serialize methods:
-
void Serialize<T>(T data, Stream stream, SerializationFormatting formatting = SerializationFormatting.Indented)
-
Task SerializeAsync<T>(T data, Stream stream, SerializationFormatting formatting = SerializationFormatting.None, CancellationToken cancellationToken = default)
We're implementing the IElasticsearchSerializer
interface, as recommended on the document: https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.17/custom-serialization.html#_injecting_a_new_serializer.
However, If I put the breakpoint in the constructor of the class, it is hit twice. And looking at the Call Stack, these hits come from the methods:
Nest.JsonNetSerializer.dll!Nest.JsonNetSerializer.ConnectionSettingsAwareSerializerBase.ConnectionSettingsAwareSerializerBase(Elasticsearch.Net.IElasticsearchSerializer builtinSerializer, Nest.IConnectionSettingsValues connectionSettings, System.Func<Newtonsoft.Json.JsonSerializerSettings> jsonSerializerSettingsFactory, System.Action<Nest.JsonNetSerializer.ConnectionSettingsAwareContractResolver> modifyContractResolver, System.Collections.Generic.IEnumerable<Newtonsoft.Json.JsonConverter> contractJsonConverters) Line 38 C#
Nest.JsonNetSerializer.dll!Nest.JsonNetSerializer.ConnectionSettingsAwareSerializerBase.ConnectionSettingsAwareSerializerBase(Elasticsearch.Net.IElasticsearchSerializer builtinSerializer, Nest.IConnectionSettingsValues connectionSettings, System.Func<Newtonsoft.Json.JsonSerializerSettings> jsonSerializerSettingsFactory, System.Action<Nest.JsonNetSerializer.ConnectionSettingsAwareContractResolver> modifyContractResolver, System.Collections.Generic.IEnumerable<Newtonsoft.Json.JsonConverter> contractJsonConverters) Line 39 C#
And then in the execution flow of the method:
await this.elasticClient.SearchAsync<Product>(searchDescriptor);
Or even if I try to serialize using the following method, it's never hit:
JObject.Parse(this.elasticClient.ConnectionSettings.RequestResponseSerializer.SerializeToString(searchDescriptor, SerializationFormatting.Indented))
The same behavior happens with Formatter (LiveScorerFormatter
).
Do you have any idea what could be the problem?
In advance, thank you for your help.
Hi @MackMendes,
please have a look at this comment: https://github.com/elastic/elasticsearch-net/issues/3517#issuecomment-473359744
This describes how to achieve your goal in a slightly different/easier way.
Hi @flobernd,
I had already tried this approach and when I tried it the following error occurred:
Unhandled exception. Elasticsearch.Net.UnexpectedElasticsearchClientException: Can not write function score json for LiveScorerScoreFunction
---> System.Exception: Can not write function score json for LiveScorerScoreFunction
at Nest.ScoreFunctionJsonFormatter.Serialize(JsonWriter& writer, IScoreFunction value, IJsonFormatterResolver formatterResolver)
at Elasticsearch.Net.Utf8Json.Formatters.CollectionFormatterBase`4.Serialize(JsonWriter& writer, TCollection value, IJsonFormatterResolver formatterResolver)
at Elasticsearch.Net.Nest_IFunctionScoreQueryFormatter3.Serialize(JsonWriter& , IFunctionScoreQuery , IJsonFormatterResolver )
at Elasticsearch.Net.Nest_IQueryContainerFormatter1.Serialize(JsonWriter& , IQueryContainer , IJsonFormatterResolver )
at Elasticsearch.Net.Nest_ISearchRequestFormatter4.Serialize(JsonWriter& , ISearchRequest , IJsonFormatterResolver )
at Elasticsearch.Net.Utf8Json.JsonSerializer.SerializeUnsafe[T](T value, IJsonFormatterResolver resolver)
at Elasticsearch.Net.Utf8Json.JsonSerializer.Serialize[T](Stream stream, T value, IJsonFormatterResolver resolver)
at Elasticsearch.Net.DiagnosticsSerializerProxy.Serialize[T](T data, Stream stream, SerializationFormatting formatting)
at Elasticsearch.Net.SerializableData`1.Write(Stream writableStream, IConnectionConfigurationValues settings)
at Elasticsearch.Net.HttpConnection.SetContent(HttpRequestMessage message, RequestData requestData)
at Elasticsearch.Net.HttpConnection.Request[TResponse](RequestData requestData)
at Elasticsearch.Net.RequestPipeline.CallElasticsearch[TResponse](RequestData requestData)
at Elasticsearch.Net.Transport`1.Request[TResponse](HttpMethod method, String path, PostData data, IRequestParameters requestParameters)
--- End of inner exception stack trace ---
at Elasticsearch.Net.Transport`1.Request[TResponse](HttpMethod method, String path, PostData data, IRequestParameters requestParameters)
at Nest.ElasticClient.Search[TDocument](ISearchRequest request)
at Data.Elasticsearch.ElasticsearchCluster.SearchAsync(Int32 tenantId, String contextId, Boolean isFreeTextSearch) in C:\Users\user-name\source\Playground\Elasticsearch-CustomScoreFunction\src\Data.Elasticsearch\ElasticsearchCluster.cs:line 21
at Program.<Main>$(String[] args) in C:\Users\user-name\source\Playground\Elasticsearch-CustomScoreFunction\src\CustomScoreFunctionElasticsearch\Program.cs:line 10
at Program.<Main>(String[] args)
I've put this test in my GitHub repository:
- https://github.com/MackMendes/Elasticsearch-CustomScoreFunction
Do you have any idea what could be the problem?