Covariant help in nest 6.2


(Gaetan Gingras) #1

Hi,

I have been using .Net Nest client since version 2.X with the following Poco objects

public interface ILocation
{
        long Id { get; set; }
	    string CityName { get; set; }
	    long CityId{ get; set; }
        long PrecinctId { get; set; }
	    string PrecinctCode { get; set; }
	    string PrecinctName { get; set; }
	    string ProvinceState { get; set; }
        Location Location { get; set; }
        string Text { get; set; }
	    public Type { get; set; }
}

[JsonObject(ItemTypeNameHandling = TypeNameHandling.All)]
public class Address : ILocation
{
            public long Id { get; set; }

            public long CityId { get; set; }
            public string CityName { get; set; }
            public long PrecinctId { get; set; }
    	    public string PrecinctCode { get; set; }
    	    public string PrecinctName { get; set; }
            public string StreetName { get; set; }
            public string StreetAlias { get; set; }
            public string StreetNumber { get; set; }
            public string ApartmentNumber { get; set; }
            public int ApartmentCount { get; set; }
            public string ApartmentList { get; set; }
            public string ProvinceState { get; set; }
            public string PostalCode { get; set; }
            //public List<Apartment> Apartments { get; set; }
            public string StreetType { get; set; }
            public StreetOrientation StreetOrientation { get; set; }

            [GeoPoint]
            public Location Location { get; set; }
            public string Text { get; set; }

    	[Keyword(Name = "$type")]
    	public Type { get; set; }
    }

[JsonObject(ItemTypeNameHandling = TypeNameHandling.All)]
public class Intersection : ILocation
{
        public long Id { get; set; }

        public long CityId { get; set; }
        public string CityName { get; set; }
        public long PrecinctId { get; set; }
	    public string PrecinctCode { get; set; }
	    public string PrecinctName { get; set; }
        public string IntersectionName { get; set; }
        public string ProvinceState { get; set; }
	    public string IntersectionAlias { get; set; }

	    [GeoPoint]
        public Location Location { get; set; }
        public string Text { get; set; }

	    [Keyword(Name = "$type")]
	    public Type { get; set; }
}

... more poco objects implementing ILocation interface ...

When migrating to version 6.2, I understand that support for covariant is not build-in anymore in nest client, but you rather need to reference the new NEST.JsonNetSerializer project.

I have tried to set the attribute [JsonObject(ItemTypeNameHandling = TypeNameHandling.All)] on my poco objects, but it did not work.

I also tried to specify the Default serializer in the connection settings, but this did not work either.

var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(pool, JsonNetSerializer.Default);
var client = new ElasticClient(settings);
...

I always get the exception: Could not create an instance of type Emergensys.Elastic.Core.Location.ILocation. Type is an interface or abstract class and cannot be instantiated. Path 'id', line 1, position 6.

Any help would be appreciated since covariant documentation for version 6 is almost nonexistent:
https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/covariant-search-results.html

Regards,

Gaetan


(Gaetan Gingras) #2

Hi,

I finally found the solution, I managed to inject a custom JsonConverter that is used once Nest client got the result from ElasticSearch when using _client.Search(...)

Here my implementation :

{
...
    var nodes = new List<Uri>();
    foreach (var u in urls)
    	nodes.Add(new Uri(u));

var pool = new StaticConnectionPool(nodes);
var settings = new ConnectionSettings(pool, (builtin, s) => new JsonNetSerializer(builtin, s,
	contractJsonConverters: new JsonConverter[] { new LocationConverter() }));

...
}

public class LocationConverter : JsonCreationConverter<ILocation>
{
		/// <inheritdoc />
		protected override ILocation Create(Type objectType, JObject jObject)
		{
			var type = jObject["$type"]?.ToString();
			switch (type)
			{
				case Address.Type:
					return new Address();
				case Street.Type:
					return new Street();
				case StreetSegment.Type:
					return new StreetSegment();
				case BusinessPlace.Type:
					return new BusinessPlace();
				case MileageMarker.Type:
					return new MileageMarker();
				case Site.Type:
					return new Site();
				case Intersection.Type:
					return new Intersection();
				default:
					return new EmptyLocation();
			}
		}

		/// <inheritdoc />
		public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
		{
			throw new NotImplementedException("Will never been access, since CanWrite always return false");
		}
}

public abstract class JsonCreationConverter<T> : JsonConverter
{
	/// <summary>
	/// Create an instance of objectType, based properties in the JSON object
	/// </summary>
	/// <param name="objectType">type of object expected</param>
	/// <param name="jObject"> contents of JSON object that will be deserialized</param>
	/// <returns>Concrete object of type T</returns>
	protected abstract T Create(Type objectType, JObject jObject);

	/// <inheritdoc />
	public override bool CanConvert(Type objectType)
	{
		return typeof(T).IsAssignableFrom(objectType);
	}

	/// <inheritdoc />
	public override bool CanWrite => false;

	/// <inheritdoc />
	public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
	{
		var jObject = JObject.Load(reader);
		var target = Create(objectType, jObject);
		serializer.Populate(jObject.CreateReader(), target);
		return target;
	}
}

(system) #3

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