NEST 6 QueryContainer JSON serialization

Our entity classes have a QueryContainer property on them which we use for storing percolate queries. Serialization of ElasticSearch request/response works fine, but when the object is then returned from our API controller as JSON the QueryContainer property is an empty object: {}

public class Person
    public string Id { get; set; }
    public QueryContainer Query { get; set; }

// Returns correct nested container
var response = client.GetAsync(new DocumentPath<Person>("1").GetAwaiter().GetResult();

// Results in "{}"
var query = JsonConvert.SerializeObject(response.Source.Query);

I have read this document and have added the NEST.JsonNetSerializer with the JsonNetSerializer.Default but this does not fix it.

I can't see a way for the property to be serialized in the model without the model being injected the ElasticSearch client and using something like client.SourceSerializer.SerializeToString

In this instance, JsonConvert would have to know how to deserialize a QueryContainer. Since QueryContainer is handled specially by an internal deserialization method however, JsonConvert won't know about this and will simply emit an empty JSON object.

One way that you can have response.Source.Query serialized correctly is to use the client's serializer and the SerializeToString extension method on ElasticsearchSerializerExtensions in Elasticsearch.Net

string json = client.RequestResponseSerializer.SerializeToString(response.Source.Query);

I was hoping that method could be avoided but thanks for confirming Russ.

An alternative approach would be to

  1. make the GetAsync() call with the low level client available through client.LowLevel, and return a StringResponse
  2. deserialize the response to a JObject with JsonConvert.DeserializeObject<JObject>(json)
  3. serialize just the "query" property of the JObject with JsonConvert.SerializeObject()

For points 2 and 3, you may want to use another Json reader, like Utf8JsonReader, if possible in your application.

I ended up creating a JsonConverter with an in-memory client as it was the cleanest way of updating our existing code.

public class QueryContainerJsonConverter : JsonConverter
// Converter should as added as global setting converter, not attribute on property.

    private static ElasticClient client = new ElasticClient(new ConnectionSettings(new SingleNodeConnectionPool(new Uri("http://localhost:9200")), new InMemoryConnection(), sourceSerializer: JsonNetSerializer.Default));

    public override bool CanConvert(Type objectType)
        return objectType == typeof(QueryContainer);

    public override bool CanWrite => true;

    public override bool CanRead => false;

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        var queryContainer = JToken.Parse(client.SourceSerializer.SerializeToString(value));


    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            // Not needed in our use case but could use  sourceSerializer.Deserialize(typeof(QueryContainer), stream);
             throw new NotImplementedException();

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.