elasticsearchjson.netnestregioninfo

RegionInfo serializing in ElasticSearch


It seems that the RegionInfo object has kinda been forgotten in terms of serialization. CultureInfo works great and is serialized to and from a string. When attempting to throw in a RegionInfo object, I get a mess of all the properties of RegionInfo that can't be deserialized because there is no constructor that takes all those properties in reverse. I would love to just serialize and deserialize the RegionInfos as strings, like CultureInfo, but can't quite figure that out.

My attempt:

I created a regioninfo converter

public class RegionInfoConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, ((RegionInfo)value).Name);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        return new RegionInfo(token.ToObject<string>());
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(RegionInfo) == objectType;
    }
}

I stuffed that into the ConnectionSettings:

var connectionSettings = new ConnectionSettings(pool,
    (builtin, settings) => new JsonNetSerializer(
        builtin,
        settings,
        contractJsonConverters: new JsonConverter[] { new RegionInfoConverter() })
);

but I get the error: object mapping for [region] tried to parse field [region] as object, but found a concrete value

That sounds like one of my serializer pieces is wrong, but I don't feel like I quite understand enough to figure out which part it is. Thanks.


Solution

  • I think the issue here may be that Elasticsearch has initially inferred an object datatype mapping for RegionInfo from a document to be indexed, and is now being passed a string value for RegionInfo. You may need to delete the index and create again, mapping the RegionInfo property as a keyword datatype.

    Here's a working example

    private static void Main()
    {
        var defaultIndex = "my_index";
        var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
    
        var settings = new ConnectionSettings(pool, (b, s) => 
            new JsonNetSerializer(b, s, contractJsonConverters: new JsonConverter[] { new RegionInfoConverter() })
            )
            .DefaultIndex(defaultIndex);
    
        var client = new ElasticClient(settings);
    
        if (client.IndexExists(defaultIndex).Exists)
            client.DeleteIndex(defaultIndex);
    
        var createIndexResponse = client.CreateIndex(defaultIndex, c => c
            .Settings(s => s
                .NumberOfShards(1)
                .NumberOfReplicas(0)
            )
            .Mappings(m => m
                .Map<MyEntity>(mm => mm
                    .AutoMap()
                    .Properties(p => p
                        .Keyword(k => k
                            .Name(n => n.RegionInfo)
                        )
                    )
                )
            )
        );
    
        var indexResponse = client.Index(new MyEntity 
        {
            RegionInfo = RegionInfo.CurrentRegion
        }, i => i.Refresh(Refresh.WaitFor));
    }
    
    public class MyEntity
    {
        public RegionInfo RegionInfo { get; set; }
    }
    
    public class RegionInfoConverter : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value == null)
            {
                writer.WriteNull();
                return;
            }
    
            writer.WriteValue(((RegionInfo)value).Name);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
                return null;
    
            if (reader.TokenType != JsonToken.String)
                throw new JsonSerializationException($"Cannot deserialize {nameof(RegionInfo)} from {reader.TokenType}");
    
            return new RegionInfo((string)reader.Value);
        }
    
        public override bool CanConvert(Type objectType)
        {
            return typeof(RegionInfo) == objectType;
        }
    }
    

    The index request sends the following JSON

    {
      "regionInfo": "AU"
    }