Bagaimana cara membatalkan serialisasi cap waktu unix (μs) ke DateTime dari JSON?

JSON

{
   "title":"Mozilla Firefox",
   "id":24,
   "parent":2,
   "dateAdded":1356753810000000,
   "lastModified":1356753810000000,
   "type":"text/x-moz-place-container",
   "children":[]
}

C#

class Bookmark
{
    public string title;
    public string id;
    [JsonProperty(ItemConverterType = typeof(JavaScriptDateTimeConverter))]
    public DateTime dateAdded;
    [JsonProperty(ItemConverterType = typeof(JavaScriptDateTimeConverter))]
    public DateTime lastModified;
    public string type;
    public string root;
    public long parent;
    public List<Bookmark> children;
}

private static void Main(string[] args)
{
    var json = File.ReadAllText(@"T:/bookmarks-2013-11-13.json");
    var bookmarks = JsonConvert.DeserializeObject<Bookmark>(json);
}

Saya mendapatkan pengecualian ketika saya mencoba menjalankan ini,

Informasi tambahan: Kesalahan membaca tanggal. Token tak terduga: Integer. Jalur 'tanggal Ditambahkan'

Saya pikir dengan menggunakan JavaScriptDateTimeConverter, JSON.NET dapat mengetahui cara membatalkan serialisasi cap waktu unix tersebut (ms μs sejak zaman). Apa cara termudah untuk melakukan ini?

Mengalami kesulitan menemukan dokumentasi tentang konverter... mungkin tidak akan terlalu sulit untuk menulisnya sendiri jika perlu.

Sunting: Itu sebenarnya mikrodetik, bukan milidetik.


person mpen    schedule 14.11.2013    source sumber
comment
Ini adalah pertanyaan yang sangat berharga karena waktu unix adalah satu-satunya representasi yang sepenuhnya tahan terhadap kekurangan JS sehubungan dengan zona waktu.   -  person Peter Wone    schedule 14.11.2013


Jawaban (6)


Saya sedikit membersihkan solusi Cris dan menerapkan WriteJson:

class Bookmark
{
    public string title;
    public long id;
    [JsonConverter(typeof(MicrosecondEpochConverter))]
    public DateTime dateAdded;
    [JsonConverter(typeof(MicrosecondEpochConverter))]
    public DateTime lastModified;
    public string type;
    public string root;
    public long parent;
    public List<Bookmark> children;
    public string uri;

    public override string ToString()
    {
        return string.Format("{0} - {1}", title, uri);
    }
}

public class MicrosecondEpochConverter : 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)
    {
        writer.WriteRawValue(((DateTime)value - _epoch).TotalMilliseconds + "000");
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value == null) { return null; }
        return _epoch.AddMilliseconds((long)reader.Value / 1000d);
    }
}

internal class Program
{

    private static void Main(string[] args)
    {
        var jsonString = File.ReadAllText(@"T:/bookmarks-2013-11-13.json");
        var rootMark = JsonConvert.DeserializeObject<Bookmark>(jsonString);
        var ret = JsonConvert.SerializeObject(rootMark);
    }
}
person mpen    schedule 14.11.2013
comment
Pembersihan yang bagus. Anda juga dapat menempatkan bidang _epoch statis untuk meningkatkan sedikit konsumsi memori - person Charles HETIER; 18.11.2014
comment
Kode yang diedit untuk menangani DateTime? AKA Nullable DateTime. DateTimeConverterBase Bisakah Mengonversi DateTime dan DateTime?. - person Jess; 23.06.2015
comment
Terima kasih maaf! Sama seperti catatan lebih lanjut untuk siapa saja yang ketahuan - OP meminta konverter dari unix MICRO detik ke C# Datetime. Saya dengan bodohnya berasumsi bahwa waktu zaman selalu dinyatakan dalam mikrodetik sejak tahun 1970 tetapi biasanya dinyatakan dalam hitungan detik (en.wikipedia .org/wiki/Unix_time) jadi ketika saya menggunakan konverter ini semuanya salah yang membuat saya bingung sejenak ^_^ - person Ryan; 27.01.2016
comment
@Ryan Memang. PHP dan sebagian besar bahasa lainnya menggunakan detik sejak zaman, JavaScript menggunakan milidetik untuk zaman, tetapi karena alasan tertentu Firefox memutuskan untuk menyimpan bookmarknya sebagai mikrodetik sejak zaman. - person mpen; 28.01.2016
comment
@mpen - Data saya masuk sebagai string yang mewakili panjang (dari Instagram). Apakah Convert.ToInt64 lebih baik daripada reader.Value (panjang)? Jika tidak, berfungsi dengan sempurna. - person alex; 15.01.2017
comment
@alex Uhh... C# saya agak berkarat sekarang. Saya pikir Anda harus mengonversinya daripada melemparkannya jika itu berasal dari string. - person mpen; 15.01.2017
comment
Berhasil, saya mengubah nilai pengembalian pada ReadJson menjadi _epoch.AddMilliseconds((long)reader.Value); untuk membuat serial nilai panjang yang berasal dari Java Timestamp - person Juan; 10.12.2018
comment
@Juan Jika Anda menginginkan milidetik, bukan mikrodetik, Anda harus menghapus + "000" di WriteJson juga - person mpen; 11.12.2018
comment
Jika ada orang lain yang bertanya-tanya mengapa ini tidak berhasil, pastikan Anda menggunakan namespace Newtonsoft.Json yang sama untuk anotasi [JsonConverter] di kelas data. - person Zoltán Barics; 27.07.2020
comment
@ZoltánBarics Cukup yakin saya tidak menggunakan NewtonSoft untuk ini; seharusnya System.Text.Json.Serialization.JsonConverter - person mpen; 28.07.2020

Anda dapat membuat konverter DateTime khusus

  var bookmarks = JsonConvert.DeserializeObject<Bookmark>(json,
                                                      new MyDateTimeConverter());

public class MyDateTimeConverter : Newtonsoft.Json.JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(DateTime);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var t = long.Parse((string)reader.Value);
        return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(t);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

pendekatan lain adalah dengan memberi anotasi ganda pada anggota kelas,

[JsonProperty(PropertyName="dateAdded")] 
[JsonConverter(typeof(JavaScriptDateTimeConverter))]
public DateTime dateAdded;
person Cris    schedule 14.11.2013
comment
Sepertinya ini berhasil. Lalu atribut apa yang digunakan? - person mpen; 14.11.2013
comment
ItemConverterType adalah untuk mengonversi item dalam array, coba anotasi ganda properti dengan JsonProperty dan JsonConverter seperti [JsonProperty(PropertyName=dateAdded)] [JsonConverter(typeof(JavaScriptDateTimeConverter))] - person Cris; 14.11.2013
comment
Anotasi ganda tidak diperlukan; [JsonConverter] adalah atribut yang saya cari. Terima kasih! (Tetapi [JsonProperty] juga bagus, karena sekarang saya dapat mengganti nama properti saya) - person mpen; 14.11.2013
comment
Menggunakan Convert.ToString(reader.Value) alih-alih pemeran objek asli (string)reader.Value agar ini berfungsi untuk saya. - person Darth Jon; 22.08.2014
comment
Saya mencoba jawaban Anda tetapi saya juga ingin menggunakan pengaturan untuk mengabaikan nilai nol, bagaimana saya bisa menggunakan keduanya? seperti ketika saya mencoba seperti di bawah ini saya mendapat beberapa argumen yang tidak valid, apakah ada JsonConvert.DeserializeObject(restResponse.Content, typeof(MyClass), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }, new MyDateTimeConverter()); - person Neo; 18.09.2015
comment
Kesalahan apa yang Anda dapatkan di sini? Apakah ini run-time? - person Cris; 18.09.2015
comment
@DarthJon: var t = Convert.ToInt64(reader.Value); menghindari semua jalan memutar string - person TomB; 16.09.2020

Ada cara bawaan untuk mengonversi dari stempel waktu unix ke DateTime tanpa harus menulis kelas Anda sendiri:

[JsonConverter(typeof(UnixDateTimeConverter))]
public DateTime lastModified;

https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Converters_UnixDateTimeConverter.htm

Anotasi dalam Jawaban Cris tidak benar, karena JavaScriptDateTimeConverter ditujukan untuk format Date(52231943) dan bukan stempel waktu Unix dari OP.

Saya menyadari pertanyaan ini sudah berumur beberapa tahun sekarang jadi kemungkinan besar kelas ini telah ditambahkan sejak pertanyaan ini diajukan, tetapi ini dapat membantu siapa saja yang mengalami masalah yang sama.

person Aoki Metal    schedule 26.02.2018
comment
Pertanyaan saya adalah tentang mikrodetik sejak zaman, bukan detik biasanya. Menurut Anda UnixDateTimeConverter tidak berhasil untuk itu, bukan? - person mpen; 26.02.2018
comment
Perhatikan bahwa UnixDateTimeConverter adalah JSONConvert v.11+. Jika Anda terjebak dengan versi 10.x atau lebih rendah, Anda harus menggunakan beberapa solusi lain yang diusulkan :) - person Frederik Struck-Schøning; 24.04.2018

Saya ingin menambahkan solusi untuk .Net core dan System.Text.Json berdasarkan jawaban yang diterima. Diverifikasi dalam versi 3.1 pada saat penulisan ini.

public class UnixDateTimeConverter : JsonConverter<DateTime>
{
   public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
   {
       return DateTime.UnixEpoch.AddSeconds(reader.GetInt64());
   }

   public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
   {
       writer.WriteStringValue((value - DateTime.UnixEpoch).TotalMilliseconds + "000");
   }
}

Anda kemudian dapat menerapkannya menggunakan anotasi data

[JsonConverter(typeof(UnixDateTimeConverter))]
public DateTime MyDateTime {get;set;}
person Bryan Halterman    schedule 14.09.2020
comment
Apakah DateTime.UnixEpoch baru? Ini terlihat sedikit lebih bersih. Terima kasih! - person mpen; 16.09.2020
comment
@mpen, Tersedia dalam .net standard 2.1, .net core 2.1 dan yang lebih baru docs.microsoft.com/en-us/dotnet/api/ - person Bryan Halterman; 17.09.2020

Hmmm ...yang rumit adalah konstruktor DateTime tidak menerima stempel waktu sebagai argumen.

Berikut solusi dari artikel ini: Cara mengonversi Unix stempel waktu ke DateTime dan sebaliknya?

public static DateTime UnixTimeStampToDateTime( double unixTimeStamp )
{
    // Unix timestamp is seconds past epoch
    System.DateTime dtDateTime = new DateTime(1970,1,1,0,0,0,0);
    dtDateTime = dtDateTime.AddSeconds( unixTimeStamp ).ToLocalTime();
    return dtDateTime;
}

Anda harus memanggil ini, mungkin yang terbaik adalah menggunakan konstruktor default di kelas Anda, atau memiliki properti lain yang akan mengembalikan ini dalam metode get-nya.

person Noctis    schedule 14.11.2013

Begitulah cara saya melakukannya dan berhasil

Di ViewModel saya, saya memiliki properti Publik Tipe DateTime

Public DateTime TimeToBeShown {get; set;}

Dalam Model Saya, saya memiliki properti publik bertipe Long yang mendapatkan tanggal dari API sebagai Format JSON.

Public long DateThatIsComingAsJsonFormat {get; set;}
     var dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
     TimeToBeShown=dateTime.AddSeconds(somethingfromloop.CreatedTime).ToLocalTime();
Bind to TimeToBeShown in Xaml



person redar ismail    schedule 27.06.2019