I am reading data from a Firestore database and converting it to JSON using Newtonsoft in a C# Web API. However, the timestamp fields always convert to an empty object.
ds is a DocumentSnapshot
Dictionary<string, object> d = ds.ToDictionary();
JToken jt = JToken.FromObject(d);
The above returns an empty object for the "date" field, which is a Firestore Timestamp:
date: {}
I have done the following as a workaround:
var dt = d.GetValueOrDefault("date");
if (dt != null)
{
var dts = dt.ToString().Remove(0, 11);
d["date"] = dts;
}
I have tried the various date options for the conversion but none of them do anything.
The type Google.Cloud.Firestore.Timestamp
has no public properties, which is why it is serialized as an empty object {}
. To serialize it as a RFC 3339 UTC Timestamp string you will need to create a custom JsonConverter<Timestamp>
:
public class TimestampConverter : JsonConverter<Google.Cloud.Firestore.Timestamp> // namespace added for clarity
{
//https://cloud.google.com/dotnet/docs/reference/Google.Cloud.Firestore/latest/Google.Cloud.Firestore.Timestamp
public override Timestamp ReadJson(JsonReader reader, Type objectType, Timestamp existingValue, bool hasExistingValue, JsonSerializer serializer) =>
Timestamp.FromDateTime(serializer.Deserialize<DateTime>(reader).ToUniversalTime());
public override void WriteJson(JsonWriter writer, Timestamp value, JsonSerializer serializer) =>
writer.WriteRawValue(JsonConvert.ToString(value.ToDateTime(), DateFormatHandling.IsoDateFormat, DateTimeZoneHandling.Utc));
}
Alternatively, if you need the generated JSON Timestamp values to adhere precisely to Google's expected RFC 3339 format, you may use Google.Apis.Json.RFC3339DateTimeConverter
to format the values:
public class TimestampConverter : JsonConverter<Google.Cloud.Firestore.Timestamp> // namespace added for clarity
{
//https://cloud.google.com/dotnet/docs/reference/Google.Apis/latest/Google.Apis.Json.RFC3339DateTimeConverter
//A JSON converter which honers RFC 3339 and the serialized date is accepted by Google services.
static readonly Google.Apis.Json.RFC3339DateTimeConverter googleDateTimeConverter = new();
//https://cloud.google.com/dotnet/docs/reference/Google.Cloud.Firestore/latest/Google.Cloud.Firestore.Timestamp
public override Timestamp ReadJson(JsonReader reader, Type objectType, Timestamp existingValue, bool hasExistingValue, JsonSerializer serializer) =>
Timestamp.FromDateTime(serializer.Deserialize<DateTime>(reader).ToUniversalTime());
public override void WriteJson(JsonWriter writer, Timestamp value, JsonSerializer serializer) =>
googleDateTimeConverter.WriteJson(writer, value.ToDateTime(), serializer);
}
Either way, to serialize to JToken
using the following settings:
var settings = new JsonSerializerSettings
{
Converters = { new TimestampConverter() },
};
var jt = JToken.FromObject(d, JsonSerializer.Create(settings));
Or to a JSON string as follows:
var json = JsonConvert.SerializeObject(d, settings);
The resulting JSON for your dictionary d
will look like:
{"date":"2023-04-11T21:01:01.110101Z"}
For the first converter, or
{"date":"2023-04-11T21:08:01.110Z"}
For the second. (The difference seems only to be in the precision.)