Newtonsoft.Json反序列化时如何传入ExchangeClass参数初始化对象?
问题
我通常通过以下方式创建BasicSimpleOrder对象:
Public Shared Function createBasicSimpleOrder(ammountOfQuoteCurrencyBeingOrdered As Decimal, priceInBasecoin As Decimal, Base As String, Quote As String, exchangeObject As ExchangesClass, isSellingInsteadOfBuying As Boolean, customIdentifier As String) As BasicSimpleOrder Dim exchangeString = exchangeObject.ExchangeStringName Dim a = New BasicSimpleOrder(ammountOfQuoteCurrencyBeingOrdered, priceInBasecoin, Base, Quote, exchangeString, isSellingInsteadOfBuying, customIdentifier) a.initializeComplexObjects(exchangeObject) Return a End Function
其中exchangeObject As ExchangesClass是BasicSimpleOrder中唯一的非基元类型成员,我会在设置基元只读属性后调用initializeComplexObjects方法完成复杂对象初始化:
Public Sub initializeComplexObjects(exchangeObject As ExchangesClass) _exchangeObject = exchangeObject If exchangeObject Is Nothing Then Exit Sub Dim mark = CoinClass.MarketBaseQuoted(exchangeObject.ExchangeStringName, Base, Quote) If mark Is Nothing Then Exit Sub setMarket(mark) End Sub
现在需要反序列化包含BasicSimpleOrder派生类的futureStateForGrid对象,使用代码:
gridOrderOldState = Newtonsoft.Json.JsonConvert.DeserializeObject(Of futureStateForGrid)(settingsFromBefore)
BasicSimpleOrder的构造函数如下:
Protected Sub New(ammountOfQuoteCurrencyBeingOrdered As Decimal, priceInBasecoin As Decimal, Base As String, Quote As String, exchangeString As String, isSellingInsteadOfBuying As Boolean, customIdentifier As String) _ammountOfQuoteCurrencyBeingOrdered = ammountOfQuoteCurrencyBeingOrdered _Base = Base _Quote = Quote _priceInBasecoin = priceInBasecoin _exchangeString = exchangeString _isSellingInsteadOfBuying = isSellingInsteadOfBuying _customIdentifier = customIdentifier End Sub
核心问题:反序列化时无法自动调用initializeComplexObjects(exchangeObject As ExchangesClass)——构造函数不能接收ExchangesClass类型参数(因为该类型未被序列化,Json.NET无法生成对应实例),但反序列化完成后需要传入exchangeObject来完成BasicSimpleOrder的复杂成员初始化。
解决方案
方案1:反序列化后手动遍历调用初始化方法
这是最直接的方案,反序列化完成后,遍历futureStateForGrid中所有BasicSimpleOrder(或其派生类)对象,逐个调用initializeComplexObjects方法,传入对应的exchangeObject。
假设futureStateForGrid包含一个Orders属性(存储所有订单对象),代码示例:
' 先完成反序列化 gridOrderOldState = Newtonsoft.Json.JsonConvert.DeserializeObject(Of futureStateForGrid)(settingsFromBefore) ' 遍历所有订单,调用初始化方法 For Each order In gridOrderOldState.Orders If TypeOf order Is BasicSimpleOrder Then DirectCast(order, BasicSimpleOrder).initializeComplexObjects(yourExchangeObject) End If Next
如果订单嵌套在更复杂的结构里,只需要调整遍历逻辑,找到所有BasicSimpleOrder派生类实例即可。
方案2:利用Json.NET的OnDeserialized特性延迟初始化
如果exchangeObject可以通过已序列化的exchangeString属性获取(比如全局维护一个ExchangesClass的字典,用exchangeString做键),可以在BasicSimpleOrder中添加OnDeserialized标记的方法,在反序列化完成后自动触发初始化:
Imports Newtonsoft.Json Public Class BasicSimpleOrder ' 原有代码... <OnDeserialized()> Private Sub OnDeserializedMethod(context As StreamingContext) ' 假设你有全局的Exchange管理类,能通过exchangeString获取对应的ExchangesClass实例 Dim exchangeObject = ExchangeManager.GetExchangeByString(_exchangeString) initializeComplexObjects(exchangeObject) End Sub End Class
这种方案无需手动遍历,反序列化后会自动执行初始化逻辑,但前提是你能通过exchangeString拿到对应的ExchangesClass实例。
方案3:调整类设计为懒加载模式
把_exchangeObject和相关的Market对象改为懒加载,在第一次使用时再初始化,这样反序列化后不用立刻调用初始化方法,用到的时候自动完成:
Public Class BasicSimpleOrder Private _exchangeObject As ExchangesClass Private _lazyExchange As Lazy(Of ExchangesClass) Private _lazyMarket As Lazy(Of MarketClass) ' 假设MarketClass是setMarket的参数类型 ' 原有构造函数不变,添加懒加载初始化 Protected Sub New(...) ' 原有代码... _lazyExchange = New Lazy(Of ExchangesClass)(Function() ExchangeManager.GetExchangeByString(_exchangeString)) _lazyMarket = New Lazy(Of MarketClass)(Function() Dim exch = _lazyExchange.Value If exch Is Nothing Then Return Nothing Return CoinClass.MarketBaseQuoted(exch.ExchangeStringName, Base, Quote) End Function) End Sub ' 修改原有属性,用懒加载的实例 Public ReadOnly Property ExchangeObject As ExchangesClass Get Return _lazyExchange.Value End Get End Property Public ReadOnly Property Market As MarketClass Get Return _lazyMarket.Value End Get End Property ' 可以保留原有的initializeComplexObjects方法,作为手动触发的选项 Public Sub initializeComplexObjects(exchangeObject As ExchangesClass) If _exchangeObject Is Nothing Then _exchangeObject = exchangeObject ' 手动初始化Market Dim mark = CoinClass.MarketBaseQuoted(exchangeObject.ExchangeStringName, Base, Quote) If mark IsNot Nothing Then setMarket(mark) End If End If End Sub End Class
这种方案彻底解决了反序列化后的初始化问题,对象在第一次被使用时才会加载依赖的复杂类型,无需额外手动操作。
内容的提问来源于stack exchange,提问作者user4951

