2016-08-26 7 views
0

Я использую java.sql.Timestamp 2014-12-27 00:00:00Java.util.Timestamp => ElasticSearch долго => C# NEST DateTime

и ElasticSearch сохраняет его как долго1419634800000 с отображением:

"EventDateLocal" : { 
    "type" : "long" 
} 

, и я хочу прочитать его на C# (через NEST) в System.DateTime

Я попытался

[Date(NumericResolution = NumericResolutionUnit.Milliseconds)] 
public DateTime? EventDateLocal { get; set; } 

, но я получил:

Unhandled Exception: Elasticsearch.Net.UnexpectedElasticsearchClientException: U 
    nexpected token parsing date. Expected String, got Integer. 

....

Path 'hits.hits[0]._ 
     source.EventDateLocal', line 1, position 350. ---> Newtonsoft.Json.JsonSerializa 
     tionException: Unexpected token parsing date. Expected String, got Integer. Path 
     'hits.hits[0]._source.EventDateLocal', line 1, position 350. 
      at Newtonsoft.Json.Converters.IsoDateTimeConverter.ReadJson(JsonReader reader 
     , Type objectType, Object existingValue, JsonSerializer serializer) 
      at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConv 
     ertable(JsonConverter converter, JsonReader reader, Type objectType, Object exis 
     tingValue)... 

Что я должен поставить в аннотации, чтобы получить этот эффект автоматически (длинный + Unix => EVENTDATE):

foreach (var e in request.Documents) 
{ 
    var start = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 
    var date = start.AddMilliseconds(e.EventDateLocal.Value).ToLocalTime(); 
} 

My NEST c на которых:

var client = new ElasticClient(new ConnectionSettings(new Uri(url)) 
     .DefaultIndex(index) 
     .DefaultTypeNameInferrer(t => type) 
); 

Если не с аннотацией, можно как-то сказать NEST о длинных?

client.Map<Event>(m => m 
    .AutoMap() 
    .Properties(ps => ps 
     .Date(e => e 
      .Name("EventDateLocal") 
      .Format(????) 
    ) 
) 
); 

ответ

2

по умолчанию сериализатор Используемый по Nest, Json.NET, не обрабатывает миллисекунд с начала эпохи как сериализованном виде даты (System.DateTime/System.DateTimeOffset). Однако мы можем применить наш собственный конвертер для обработки этого, либо только для этих свойств, либо во всем мире.

Во-первых, определить преобразователь

public class EpochDateTimeConverter : DateTimeConverterBase 
{ 
    private static readonly DateTime Epoch = new DateTime(1970,1,1,0,0,0,DateTimeKind.Utc); 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     if (value == null) 
     { 
      writer.WriteNull(); 
      return; 
     } 

     long millisecondsSinceEpoch; 
     if (value is DateTime) 
     { 
      millisecondsSinceEpoch = Convert.ToInt64((((DateTime)value).ToUniversalTime() - Epoch).TotalMilliseconds); 
     } 
     else 
     { 
      if (!(value is DateTimeOffset)) 
       throw new JsonSerializationException("Expected date object value."); 
      millisecondsSinceEpoch = Convert.ToInt64((((DateTimeOffset)value).ToUniversalTime().UtcDateTime - Epoch).TotalMilliseconds); 
     } 
     writer.WriteValue(millisecondsSinceEpoch); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) 
     { 
      if (objectType != typeof(DateTime?) && objectType != typeof(DateTimeOffset?)) 
       throw new JsonSerializationException($"Cannot convert null value to {objectType}"); 

      return null; 
     } 
     if (reader.TokenType == JsonToken.Integer || reader.TokenType == JsonToken.Float) 
     { 
      var millisecondsSinceEpoch = (long)reader.Value; 
      var dateTime = Epoch.AddMilliseconds(millisecondsSinceEpoch); 
      if (objectType == typeof(DateTime) || objectType == typeof(DateTime?)) 
      { 
       return dateTime; 
      } 
      else 
      { 
       return new DateTimeOffset(dateTime); 
      } 
     } 

     throw new JsonSerializationException($"Cannot convert to DateTime or DateTimeOffset from token type {reader.TokenType}"); 
    } 
} 

Теперь применить его к вам Poco свойства

public class MyDocument 
{ 
    [JsonConverter(typeof(EpochDateTimeConverter))] 
    public DateTime DateTime { get; set;} 

    [JsonConverter(typeof(EpochDateTimeConverter))] 
    public DateTimeOffset DateTimeOffset { get; set; } 

    [JsonConverter(typeof(EpochDateTimeConverter))] 
    public DateTime? NullableDateTime { get; set; } 

    [JsonConverter(typeof(EpochDateTimeConverter))] 
    public DateTimeOffset? NullableDateTimeOffset { get; set; } 
} 

и проверить его

var client = new ElasticClient(); 

var document = new MyDocument 
{ 
    DateTime = new DateTime(2016, 8, 29, 9, 46, 0), 
    NullableDateTime = null, 
    DateTimeOffset = new DateTimeOffset(2016, 8, 29, 9, 46, 0, TimeSpan.Zero), 
    NullableDateTimeOffset = new DateTimeOffset(2016, 8, 29, 9, 46, 0, TimeSpan.Zero), 
}; 

client.Index(document); 

сериализует

{ 
    "dateTime": 1472427960000, 
    "dateTimeOffset": 1472463960000, 
    "nullableDateTimeOffset": 1472463960000 
} 

NEST не отправляет null значения по умолчанию, но это может быть изменено путем добавления [JsonProperty(NullValueHandling = NullValueHandling.Include)] к соответствующему имуществу POCO, если вы действительно хотите отправить null.

Чтобы проверить десериализации

var json = @"{ 
    ""dateTime"": 1472427960000, 
    ""dateTimeOffset"": 1472463960000, 
    ""nullableDateTimeOffset"": 1472463960000 
}"; 

MyDocument deserializedDocument = null; 
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) 
    deserializedDocument = client.Serializer.Deserialize<MyDocument>(stream); 

к сведению, что все DateTime и DateTimeOffset экземпляры в UTC поэтому может потребоваться изменить для преобразования в местное время.