Java.util.Timestamp => ElasticSearch long => C# NEST DateTime

Java.util.Timestamp => ElasticSearch long => C# NEST DateTime

我用java.sql.Timestamp 2014-12-27 00:00:00

ElasticSearch 将其保存为 long 1419634800000 并映射:

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

我想在 System.DateTime

中用 C#(通过 NEST)阅读它

我试过了

[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)...

我应该在注释中输入什么才能自动获得此效果(long + 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();
}

我的 NEST 配置是:

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(????)
      )
  )
);

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 值,但如果您真的想发送 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);

请注意,所有 DateTimeDateTimeOffset 实例均采用 UTC,因此可能需要修改以转换为本地时间。