MongoDB C#:如何將包含DateTime的JSON反序列化為正確的BsonDocument DateTime值
目錄
如果您的Json包含日期時間值如“2018-11-23T20:56:05.3117673Z”並且您需要將其作為正確的BsonDateTime值放在BsonDocument中,該怎麼辦?
介紹
這是我的第一篇文章,最後我發現了一個問題,我認為在這個領域(JSON,DateTime和使用BsonDocument的MongoDb)中有很多人都有值得發表的問題!我很高興與您分享解決方案!
大多數時候,人們使用Json作為序列化格式在應用程式之間傳輸資料。MongoDB有內建的序列化器,可以從Json轉換為Bson。問題是生成的Bson
背景
MongoDb中內建的序列化程式可以在滿足給定格式時處理“Json”中的datetime字串表示,即:
{
"myDate": ISODate("2018-11-23T20:56:05.311Z")
}
但這不是一個有效的Json格式,並且沒有Json序列化程式(如Json.NET)能夠正確地序列化或反序列化(無需任何修改)。大多數人都希望擁有以下
{
"myDate": "2018-11-23T20:56:05.311Z"
}
通常,您可以使用以下程式碼將Json反序列化為Bson:
var bson = BsonSerializer.Deserialize<BsonDocument>(json);
你會看到結果是BsonType.String。
當您檢查BsonSerializer原始碼時,您將找到以下程式碼:
public static TNominalType Deserialize<TNominalType> (string json, Action<BsonDeserializationContext.Builder> configurator = null) { using (var bsonReader = new JsonReader(json)) { return Deserialize<TNominalType>(bsonReader, configurator); } }
所以在幕後,MongoDb使用某種JsonReader,只是解析任何string值為BsonType.String。
這是我們將擴充套件JsonReader的地方,以便能夠正確地解析上面提到的datetime模式,從而產生一個BsonType.DateTime。
使用程式碼
讓我們建立自己的DateTimeAwareJsonReader,只是擴充套件現有的JsonReader並嘗試確定給定string值是否可以是有效的日期時間。但我們要記住,我們希望它儘可能高效。我們知道我們必須調查Json值的每個string表示,無論它是否是datetime。讓我們開始吧:
public class DateTimeAwareJsonReader : JsonReader
{
public DateTimeAwareJsonReader(string json) : base(json)
{
}
public DateTimeAwareJsonReader(TextReader textReader) : base(textReader)
{
}
public DateTimeAwareJsonReader(string json, JsonReaderSettings settings) : base(json, settings)
{
}
public DateTimeAwareJsonReader(TextReader textReader, JsonReaderSettings settings) :
base(textReader, settings)
{
}
}
序列化引擎使用IBsonReader.ReadBsonType()以確定當前正在解析哪種型別,因此我們必須掛鉤並在此處進行調查,無論我們是否想告訴引擎給定的值是 string或datetime。所以讓我們新增一個重寫方法:
private string _currentStringValue;
private BsonDateTime _currentDateTime;
public override BsonType ReadBsonType()
{
_currentDateTime = null;
var currentBsonType = base.ReadBsonType();
if (currentBsonType == BsonType.String)
{
var previousState = State;
_currentStringValue = ReadString();
State = previousState;
// date pattern is YYYY-MM-dd, if this pattern is met,
// we assume it is a DateTime and try to parse.
// kind of like duck typing..if it walks like a duck and talks like a duck,
// it must be a duck.. :D
if (_currentStringValue.Length > 9)
{
if (char.IsDigit(_currentStringValue[0]) &&
char.IsDigit(_currentStringValue[1]) &&
char.IsDigit(_currentStringValue[2]) &&
char.IsDigit(_currentStringValue[3]) &&
_currentStringValue[4].Equals('-') &&
char.IsDigit(_currentStringValue[5]) &&
char.IsDigit(_currentStringValue[6]) &&
_currentStringValue[7].Equals('-') &&
char.IsDigit(_currentStringValue[8]) &&
char.IsDigit(_currentStringValue[9]))
{
// looks like a date string
if (DateTime.TryParse(_currentStringValue, out var parsedDateTime))
{
_currentDateTime = new BsonDateTime(parsedDateTime);
CurrentBsonType = BsonType.DateTime; // we have to set also the
// current bson type and return it.
return BsonType.DateTime;
}
}
}
}
return currentBsonType;
}
基礎的JsonReader將告訴我們,我們的“ myDate”屬性是BsonType.String。在這種情況下,我們想要攔截並進一步的調查。為了提高效率,我們只檢查至少給定大小的值。我希望序列化datetime的格式至少為“ YYYY-MM-dd”(例如“ 2018-05-02”)。如果您有其他要求,可以根據需要在此處調整邏輯。所以一旦我們在這裡涉及到數字,並且所有內容看起來都像是string表示的datetime,我們將告訴序列化器這個值是BsonType.DateTime,否則我們想要回退到序列化器必須決定的情況。
一旦我們認為給定的值是DateTime,我們必須重寫另一個方法:
public override long ReadDateTime()
{
if (_currentDateTime == null)
{
return base.ReadDateTime();
}
if (Disposed) { ThrowObjectDisposedException(); }
VerifyBsonType("ReadDateTime", BsonType.DateTime);
State = BsonReaderState.Type;
return _currentDateTime.AsBsonDateTime.MillisecondsSinceEpoch;
}
如果我們的邏輯開始,該欄位_currentDateTime將具有解析的值,我們將返回它。
這就是它!您現在有一個可以正確計算datetime 字串表示的工作解決方案,並告訴MongoDb它將其作為一個DateTime處理。
要測試它,你可以做一個簡單的控制檯應用程式:
class Program
{
public class SomeModel
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("test")]
public string Test { get; set; }
[JsonProperty("today")]
public DateTime Today { get; set; }
[JsonProperty("inner")]
public SomeModel Inner { get; set; }
}
static void Main(string[] args)
{
var model = new SomeModel
{
Id = 1,
Test = "Model",
Today = DateTime.UtcNow.Date,
Inner = new SomeModel
{
Id = 2,
Test = "Inner",
Today = DateTime.UtcNow
}
};
var json = JsonConvert.SerializeObject(model);
using (var reader = new DateTimeAwareJsonReader(json))
{
var bson = BsonSerializer.Deserialize<BsonDocument>(reader);
Console.WriteLine("Models today property is: {0}", bson["today"].BsonType);
}
Console.ReadLine();
}
}
興趣點
一旦我弄清楚MongoDb序列化是如何工作的,就很容易構建一個攔截機制來告訴底層的序列化框架datetime應該是什麼。我還沒有在高效的環境中測試它,但我很快就會做到。更有趣的部分是以一種精益的方式來猜測一個string值是否可以是datetime最有效的方式,而不會過多地干擾MongoDb客戶端庫。我認為這個解決方案適用於大多數用例。期待您的意見!;-)
原文地址:https://www.codeproject.com/Tips/1268019/MongoDB-Csharp-How-to-Deserialize-a-JSON-Containin