1

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.

Cory-G
  • 1,025
  • 14
  • 26

1 Answers1

1

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"
}
Russ Cam
  • 124,184
  • 33
  • 204
  • 266
  • In the end I decided to not use `RegionInfo` at all. It has a bunch of other serialization and conversion issues. I decided to just validate a string as it entered my system and call it good. That made the ES stuff way easier, too. It was good to learn about ES datatypes, though. Good to know. – Cory-G Jan 11 '19 at 23:29