Search code examples
c#elasticsearchserializationnest

What is the most dirt simple way to override one specific type for NEST serialization


I have a complex document, which has a custom datefield in various different subkeys, ie:

var doc = new Document
{
    MyCustomDateTime StartTime
    Matches = new []
    {
        MyCustomDateTime StartTime
        MyCustomDateTime EndTime
    }
}

Without using property attributes as this type is being pulled in from a dll, what's the simplest way I can override serialization and deserialization of the type MyCustomDateTime.

I'm looking for something similar to, or extending the default serializer class to handle one additional type:

settings.DefaultSerializerFor<MyCustomDateTime>(d => MyConverter.ToString(d), d => MyConverter.FromString(d));

or something like:

public class MyCustomSerializer : DefaultElasticSearchSerializer
{
     public string OnSerialize(object member)
     {
          if (member is MyCustomDateTime) { return MyConverter.ToString(d); }
          return base.OnSerialize(member);
     }
     ...
}

Right now, it seems to only publish the public properties on the object. This is unfortunately not the behavior I'm looking for, for this specific class.


Solution

  • The simplest way to implement custom serialization is to

    1. hook up the JsonNetSerializer
    2. define your converter for your type
    3. register it with the JsonNetSerializer

    An example

    private static void Main()
    {
        var defaultIndex = "default_index";
        var pool = new SingleNodeConnectionPool(new Uri($"http://localhost:9200"));
        var settings = new ConnectionSettings(
            pool, 
            (builtin, settings) => new JsonNetSerializer(builtin, settings, contractJsonConverters: new List<JsonConverter>
        {
            new MyCustomDateTimeConverter()
        }))
            .DefaultIndex(defaultIndex);
            
        var client = new ElasticClient(settings);
            
        var indexResponse = client.Index(
            new MyDocument {
                Message = "message",
                CustomDateTime = new MyCustomDateTime 
                { 
                    Custom = new DateTimeOffset(2022,1,9,14,0,0,0, TimeSpan.FromHours(10)) 
                }
            }, i => i.Id(1));
    }
    
    public class MyCustomDateTime 
    {
        public DateTimeOffset Custom { get; set; }
    }
    
    public class MyCustomDateTimeConverter : JsonConverter<MyCustomDateTime>
    {
        public override void WriteJson(JsonWriter writer, MyCustomDateTime value, JsonSerializer serializer)
        {
            writer.WriteValue(value.Custom);
        }
    
        public override MyCustomDateTime ReadJson(JsonReader reader, Type objectType, MyCustomDateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            var dateTime = reader.ReadAsDateTimeOffset();
            return dateTime is null ? null : new MyCustomDateTime { Custom = dateTime.Value };
        }
    }
    
    public class MyDocument
    {
        public string Message {get;set;}    
        public MyCustomDateTime CustomDateTime {get;set;}
    }
    

    yields

    PUT http://localhost:9200/default_index/_doc/1?pretty=true
    {
      "message": "message",
      "customDateTime": "2022-01-09T14:00:00+10:00"
    }