Elasticsearch Nest - JsonParsingException expected:'{',

I am in the process of updating my application to use Elasticsearch 7.3.
I updated my mappings

{
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_point"
      }
    }
  }
}

Here's part of a query result:

"_source": {
  "location": {
    "lon": -81.9438063,
    "lat": 40.8507256
  }
}

I'm using Elasticsearch NEST 7.3 to try and cast a IHit<ILazyDocument> to my model

hit.Source.As(typeof(MyModel))

Here are my models:

public class MyModel
{
    public GeoCoordinate Location { get; set; }
}

public class GeoCoordinate
{
    [Text(Name = "lat")]
    public Latitude Latitude { get; set; }

    [Text(Name = "lon")]
    public Longitude Longitude { get; set; }
}

Here's the error message I'm receiving:

expected:'{', actual:'-81.9438063', at offset:965

Partial Stack Trace (if helpful):

at Elasticsearch.Net.Utf8Json.JsonReader.ReadIsBeginObjectWithVerify()
at Deserialize(Object , JsonReader& , IJsonFormatterResolver )
at Deserialize(Object , JsonReader& , IJsonFormatterResolver )
at Deserialize(Object , JsonReader& , IJsonFormatterResolver )
at Elasticsearch.Net.Utf8Json.JsonSerializer.Deserialize[T](Byte bytes, Int32 offset, IJsonFormatterResolver resolver)
at Elasticsearch.Net.Utf8Json.JsonSerializer.Deserialize[T](Stream stream, IJsonFormatterResolver resolver)
at Deserialize(Stream , IJsonFormatterResolver )
at Elasticsearch.Net.DiagnosticsSerializerProxy.Deserialize(Type type, Stream stream)
at Nest.LazyDocument.As(Type objectType)

Is my GeoCoordinate class not properly deserializing?

Would you be able to add the client API call that you're making, too?

I'm searching two separate indexes.

POST /companies,contacts/_search?typed_keys=true&search_type=dfs_query_then_fetch

Can you show the .NET client API call that you're making? You can also get the request and response bytes in the response.DebugInformation by setting .DisableDirectStreaming(). This will allow us to see what the JSON source document looks like

Here ya go, had to trim down the number of results returned due to a character limit:

Valid NEST response built from a successful (200) low level call on POST: /companies%2Ccontacts/_search?typed_keys=true&search_type=dfs_query_then_fetch

Audit trail of this API call:

Request:

{"from":0,"query":{"bool":{"must":[{"bool":{"minimum_should_match":1,"should":[{"match":{"dba_name.edge_ngram":{"operator":"and","query":"cert ang+","_name":"Company.DbaName.EdgeNgram.Match"}}},{"match":{"dba_name":{"operator":"and","query":"cert ang+","_name":"Company.DbaName.Match"}}},{"match_phrase_prefix":{"dba_name.edge_ngram":{"query":"cert ang+","slop":100,"_name":"Company.DbaName.EdgeNgram.MatchPhrase"}}},{"match":{"first_last_name.edge_ngram":{"operator":"and","query":"cert ang+","_name":"Contact.FirstLastName.EdgeNgram.Match^0.0"}}},{"match":{"preferred_last_name.edge_ngram":{"operator":"and","query":"cert ang+","_name":"Contact.PreferredLastName.EdgeNgram.Match^0.0"}}}]}}],"should":[{"nested":{"ignore_unmapped":true,"path":"children","query":{"match":{"children.dba_name.edge_ngram":{"operator":"and","query":"cert ang+","_name":"Company.Children.DbaName.EdgeNgram.Match"}}},"score_mode":"max","boost":2.0}},{"terms":{"_name":"Contact.JobFunctions.Term^8.0","boost":8.0,"job_functions":[1863,1864,1872,1873]}}]}},"sort":[{"is_currently_licensed":{"unmapped_type":"boolean","missing":0,"order":"desc"}},{"_score":{"order":"desc"}},{"first_last_name.keyword":{"unmapped_type":"keyword","missing":"","order":"asc"}},{"is_previously_licensed":{"unmapped_type":"boolean","missing":0,"order":"desc"}},{"last_activity_date":{"order":"desc"}}],"_source":{"excludes":["children"],"includes":["*"]},"track_scores":true}

Response:

{"took":15,"timed_out":false,"_shards":{"total":2,"successful":2,"skipped":0,"failed":0},"hits":{"total":{"value":20,"relation":"eq"},"max_score":933.4909,"hits":[{"_index":"companies_v3","_type":"_doc","_id":"6404","_score":933.4909,"_source":{"parent":{"dba_name":"Certified Angus Beef LLC - Wooster","key":6404},"agent":{"hostname":"BAUMANXPS15","id":"e3c44bd2-4f90-46af-a626-6c1cd7755f1e","ephemeral_id":"ebbce2ae-a5dc-444d-b2f3-d6f74bc60f87","type":"filebeat","version":"7.3.0"},"log":{"file":{"path":"C:\Elasticsearch\Artifacts\input\filebeat_companies.2019_08_15.json"},"offset":3904432},"city":"Wooster","geo_accuracy":8,"state_abbreviation":"OH","star_ranking":0,"country_abbreviation":"USA","ecs":{"version":"1.0.1"},"@version":"1","primary_license_description":"Corp. Office - Misc.","key":6404,"banner":{"dba_name":"Certified Angus Beef LLC - Wooster","key":6404},"last_activity_date":"2019-08-01T00:00:00","dba_name":"Certified Angus Beef LLC - Wooster","volume_13_week":18661,"tags":,"input":{"type":"log"},"active_status":"T","@timestamp":"2019-08-15T14:45:10.349Z","is_currently_licensed":true,"open_issue_count":0,"is_previously_licensed":false,"menu_needed_status":0,"location":{"lon":-81.9438063,"lat":40.8507256},"primary_license_key":43,"primary_license_status":"T"},"sort":[1,933.4909,"",0,1564617600000],"matched_queries":["Company.DbaName.EdgeNgram.Match","Company.DbaName.EdgeNgram.MatchPhrase"]},{"_index":"companies_v3","_type":"_doc","_id":"216875","_score":933.4909,"_source":{"parent":null,"agent":{"hostname":"BAUMANXPS15","id":"e3c44bd2-4f90-46af-a626-6c1cd7755f1e","ephemeral_id":"ebbce2ae-a5dc-444d-b2f3-d6f74bc60f87","type":"filebeat","version":"7.3.0"},"log":{"file":{"path":"C:\Elasticsearch\Artifacts\input\filebeat_companies.2019_08_15.json"},"offset":130279478},"city":"Makati City","geo_accuracy":6,"state_abbreviation":null,"star_ranking":0,"country_abbreviation":"PHILIP","ecs":{"version":"1.0.1"},"@version":"1","primary_license_description":"Restaurant - International","key":216875,"banner":null,"last_activity_date":null,"dba_name":"Melo's Home of Certified Angus Beef","volume_13_week":0,"tags":,"input":{"type":"log"},"active_status":"T","@timestamp":"2019-08-15T14:45:27.369Z","is_currently_licensed":false,"open_issue_count":0,"is_previously_licensed":false,"menu_needed_status":0,"location":{"lon":121.0330876,"lat":14.5494421},"primary_license_key":42,"primary_license_status":"F"},"sort":[0,933.4909,"",0,-9223372036854775808],"matched_queries":["Company.DbaName.EdgeNgram.Match","Company.DbaName.EdgeNgram.MatchPhrase"]}

Do you also have the API call and what you're doing in .NET code with the response? I'd like to see this to try to reproduce the issue. Without these, I'm just guessing :smiley:

The serializer is expecting to deserialize a JSON object into each of the Latitude and Longitude types, but the response data has numeric values for these. I think these should be modelled as double e.g.

[PropertyName(Name = "lat")] 
public double Latitude { get; set; } 

[PropertyName(Name = "lon")] 
public double Longitude { get; set; }

An alternative to defining your own GeoCoordinate type is to use the Nest.GeoCoordinate type, which the JSON {"lon":-81.9438063,"lat":40.8507256} will deserialize into.

My Latitude and Longitude classes have implicit casting to double built in, but nonetheless I tried changing to use the PropertyName attribute and the type of double with no luck.

Quick Question: I am running Elasticsearch 7.3, but the Nuget package is for Elasticsearch 7.2, is this part of the problem perhaps?

I would expect this to work. Do you have a full example to demonstrate?

Nope, NEST 7.2.0 will work with Elasticsearch 7.3.0.

Here is my GeoCoordinate class, which I created since I didn't want the dependency.

public class GeoCoordinate : IEquatable<GeoCoordinate>
{
	public static readonly GeoCoordinate Unknown = new GeoCoordinate();

	[PropertyName("lat")]
	public double Latitude { get; set; }

	[PropertyName("lon")]
	public double Longitude { get; set; }

	private GeoCoordinate() { }

	public GeoCoordinate(double latitude, double longitude)
	{
		if (double.IsNaN(latitude)) { throw new ArgumentNullException(nameof(latitude)); }
		if (double.IsNaN(longitude)) { throw new ArgumentNullException(nameof(longitude)); }

		Latitude = latitude;
		Longitude = longitude;
	}

	public GeoCoordinate(Latitude latitude, Longitude longitude)
	{
		Latitude = latitude;
		Longitude = longitude;
	}

	public bool IsUnknown => Equals(Unknown);

	public override string ToString()
	{
		return $"{Latitude},{Longitude}";
	}

	public override bool Equals(object other)
	{
		if (other == null) { throw new ArgumentNullException(nameof(other)); }
		return other is GeoCoordinate gc && Equals(gc);
	}

	public bool Equals(GeoCoordinate other)
	{
		if (other == null) { return false; }

		if (Latitude == null)
		{
			if (other.Latitude != null) { return false; }
		}
		else if (!Latitude.Equals(other.Latitude)) { return false; }

		if (Longitude == null)
		{
			if (other.Longitude != null) { return false; }
		}
		else if (!Longitude.Equals(other.Longitude)) { return false; }

		return true;
	}

	public override int GetHashCode()
	{
		return Latitude.GetHashCode() ^ Longitude.GetHashCode();
	}

	public static GeoCoordinate Parse(string coordinates)
	{
		if (coordinates.IsNullOrEmpty()) { return null; }
		var s = coordinates.Split(',');
		return new GeoCoordinate(s.First().ToDouble(), s.Last().ToDouble());
	}
}

public class Latitude : IEquatable<Latitude>
{
	private readonly double _value;

	public Latitude(double latitude)
	{
		if (latitude > 90.0 || latitude < -90.0)
		{
			throw new ArgumentOutOfRangeException(nameof(Latitude), latitude, "Latitude must be between 90 & -90.");
		}
		_value = latitude;
	}

	public static implicit operator Latitude(double latitude) => new Latitude(latitude);
	public static implicit operator Latitude(string latitude) => new Latitude(Convert.ToDouble(latitude));
	public static implicit operator double(Latitude latitude) => latitude._value;

	public override bool Equals(object other)
	{
		if (other == null) { throw new ArgumentNullException(nameof(other)); }
		var l = new Latitude(Convert.ToDouble(other));
		return Equals(l);
	}

	public bool Equals(Latitude other)
	{
		return other != null && _value.Equals(other._value);
	}

	public override string ToString()
	{
		return _value.ToString(CultureInfo.InvariantCulture);
	}

	public override int GetHashCode()
	{
		return _value.GetHashCode();
	}


}

public class Longitude : IEquatable<Longitude>
{
	private readonly double _value;

	public Longitude(double longitude)
	{
		if (longitude > 180.0 || longitude < -180.0)
		{
			throw new ArgumentOutOfRangeException(nameof(Latitude), longitude, "Longitude must be between 180 & -180.");
		}
		_value = longitude;
	}

	public static implicit operator Longitude(double longitude) => new Longitude(longitude);
	public static implicit operator Longitude(string longitude) => new Longitude(Convert.ToDouble(longitude));
	public static implicit operator double(Longitude longitude) => longitude._value;

	public override bool Equals(object other)
	{
		if (other == null) { throw new ArgumentNullException(nameof(other)); }
		var l = new Longitude(Convert.ToDouble(other));
		return Equals(l);
	}

	public bool Equals(Longitude other)
	{
		return other != null && _value.Equals(other._value);
	}

	public override string ToString()
	{
		return _value.ToString(CultureInfo.InvariantCulture);
	}

	public override int GetHashCode()
	{
		return _value.GetHashCode();
	}
}

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