Skip to content

Documents previously indexed with TimeSpan as string cause UnexpectedElasticsearchClientException. #1809

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jonyadamit opened this issue Feb 8, 2016 · 3 comments

Comments

@jonyadamit
Copy link
Contributor

As a consequence of #1705 , types indexed prior to 2c3abf6 that have TimeSpan/TimeSpan? properties throw the following exception upon retrieval:

Exception thrown: 'Elasticsearch.Net.UnexpectedElasticsearchClientException' in mscorlib.dll

Additional information: Cannot convert token of type String to System.Nullable`1[System.TimeSpan].

   at Nest.TimeSpanConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) in C:\users\russ\source\elasticsearch-net\src\Nest\CommonAbstractions\SerializationBehavior\GenericJsonConverters\TimeSpanConverter.cs:line 30
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
   at Nest.JsonNetSerializer.Deserialize[T](Stream stream) in C:\users\russ\source\elasticsearch-net\src\Nest\CommonAbstractions\SerializationBehavior\JsonNetSerializer.cs:line 90
   at Nest.JsonNetSerializer.DeserializeAsync[T](Stream stream, CancellationToken cancellationToken) in C:\users\russ\source\elasticsearch-net\src\Nest\CommonAbstractions\SerializationBehavior\JsonNetSerializer.cs:line 98
   at Elasticsearch.Net.ResponseBuilder`1.<SetBodyAsync>d__19.MoveNext() in C:\users\russ\source\elasticsearch-net\src\Elasticsearch.Net\Transport\Pipeline\ResponseBuilder.cs:line 97
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Elasticsearch.Net.ResponseBuilder`1.<ToResponseAsync>d__16.MoveNext() in C:\users\russ\source\elasticsearch-net\src\Elasticsearch.Net\Transport\Pipeline\ResponseBuilder.cs:line 39
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Elasticsearch.Net.HttpConnection.<RequestAsync>d__7`1.MoveNext() in C:\users\russ\source\elasticsearch-net\src\Elasticsearch.Net\Connection\HttpConnection.cs:line 183
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Elasticsearch.Net.RequestPipeline.<CallElasticsearchAsync>d__66`1.MoveNext() in C:\users\russ\source\elasticsearch-net\src\Elasticsearch.Net\Transport\Pipeline\RequestPipeline.cs:line 409
   at Elasticsearch.Net.ExceptionExtensions.RethrowKeepingStackTrace(Exception exception) in C:\users\russ\source\elasticsearch-net\src\Elasticsearch.Net\Extensions\ExceptionExtensions.cs:line 18
   at Elasticsearch.Net.RequestPipeline.<CallElasticsearchAsync>d__66`1.MoveNext() in C:\users\russ\source\elasticsearch-net\src\Elasticsearch.Net\Transport\Pipeline\RequestPipeline.cs:line 420
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Elasticsearch.Net.Transport`1.<RequestAsync>d__15`1.MoveNext() in C:\users\russ\source\elasticsearch-net\src\Elasticsearch.Net\Transport\Transport.cs:line 128
russcam added a commit that referenced this issue Feb 9, 2016
See #1809
In order to override the built in convention of serializing to long, a user would need to either register a converter for TimeSpan on ConnectionSettings or attribute TimeSpan properties
@russcam
Copy link
Contributor

russcam commented Feb 9, 2016

Hey @jonyadamit,

This is a result of inferring TimeSpan to be long that went in as part of Nest 2.x.

I've made a change in ba0ea0a to be able to deserialize existing TimeSpan properties indexed as string correctly, however in order to index new documents with the TimeSpan property as a string would require a converter, either by deriving from JsonNetSerializer and overriding ContractConverters or by attributing the property with [JsonConverter(typeof(ConverterTypeName))]. A example of a converter for serializing/deserializing string values for TimeSpan

public class StringTimeSpanConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
            writer.WriteNull();
        else
        {
            var timeSpan = (TimeSpan)value;
            writer.WriteValue(timeSpan.ToString());
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            if (!objectType.IsGeneric() || objectType.GetGenericTypeDefinition() != typeof(Nullable<>))
                throw new JsonSerializationException($"Cannot convert null value to {objectType}.");

            return null;
        }
        if (reader.TokenType == JsonToken.String)
        {
            return TimeSpan.Parse((string)reader.Value);
        }

        throw new JsonSerializationException($"Cannot convert token of type {reader.TokenType} to {objectType}.");
    }

    public override bool CanConvert(Type objectType) => objectType == typeof(TimeSpan) || objectType == typeof(TimeSpan?);
}

@jonyadamit
Copy link
Contributor Author

Hey @russcam, maybe this should be noted on the release breaking changes?
So that people would be better prepared for migration.

Closing this as it is addressed by ba0ea0a.

@russcam
Copy link
Contributor

russcam commented Feb 10, 2016

@jonyadamit yes to adding to breaking changes; I'll add it now 👍

russcam added a commit that referenced this issue Feb 10, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants