Customize Newtonsoft.Json Serializer/Deserializer to convert Javascript Datetime and Number to .NET datatype.
Newtonsoft.Json Serializer/Deserializer is very useful and expandable library, I have a number of articles to processing: JSON
- Parse Json by Newtonsoft.Json package.
- How to parse JSON by Newtonsoft.Json (on example of FireFox Tab Session Manager and decrypted JwtSecurityToken)
- Parse Yotube response by Newtonsoft.Json
In this page I describe how to expand Newtonsoft.Json converter by own code. If you try to serialize .NET Type to JSON, you receive the similar representation of double (variabre with R suffix in VB.NET like Dim Price = 0.00000001R).
Usually this double-precision floating-point numbers will be not right handle in any server code, there for before deserialization this digit need to own deserializator:
109: Friend NotInheritable Class FormatNumbersAsTextConverter
110: Inherits JsonConverter
111:
112: Public Overrides ReadOnly Property CanRead As Boolean = False
113: Public Overrides ReadOnly Property CanWrite As Boolean = True
114:
115: ' <NullableAttribute(2)>
116: Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
117: writer.WriteValue(String.Format("{0:#######0.########}", value))
118: End Sub
119:
120: ' <NullableAttribute(2)>
121: Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
122: Throw New NotImplementedException()
123: End Function
124:
125: Public Overrides Function CanConvert(objectType As Type) As Boolean
126: Return objectType Is GetType(Double)
127: End Function
128: End Class
To use this this serializer as
32: Public Shared Property DoubleNumericFormaterSerializerSettings = New JsonSerializerSettings With {.Converters = {New FormatNumbersAsTextConverter()}}
...
100: Dim JsonString As String = JsonConvert.SerializeObject(InputJsonObject, DoubleNumericFormaterSerializerSettings)
you receive:
The second big problem to build interaction between Javascript and .NET is DateTime. In Javascript after serialization DateTime usually look as:
19: "lastAccessed": 1574205139566,
You can receive this data to ordinary model by one string of code, if you will specify special converter of this fields as Attribute:
121: Public Class TradeModel
122: Property id As Long
123: Property price As Double
124: Property qty As Double
125: Property quoteQty As Double
126: <Newtonsoft.Json.JsonConverter(GetType(JavascriptTimestampMicrosecondConverter))>
127: Property lastAccessed As DateTime
128: Property isBuyerMaker As Boolean
129: Property isBestMatch As Boolean
130: End Class
Then receiving JSON to model is possible by the ordinary way in one line of code:
399: Dim TradesArr As JArray = JArray.Parse(Trades.Result)
400: TradeList = JsonConvert.DeserializeObject(Of List(Of TradeModel))(TradesArr.ToString)
Also is use receive one converted variable (without attribute and model):
33: Public Shared Property JavascriptTimestampMicrosecondSerializerSettings = New JsonSerializerSettings With {.Converters = {New JavascriptTimestampMicrosecondConverter()}}
360: Dim ServerTime As DateTime = JsonConvert.DeserializeObject(Of DateTime)(ExchangeInfoJson("serverTime").ToString, JavascriptTimestampMicrosecondSerializerSettings)
This is my DateTime converter.
131: Public Class JavascriptTimestampMicrosecondConverter
132: Inherits JsonConverter
133:
134: Public Overrides ReadOnly Property CanRead As Boolean = True
135: Public Overrides ReadOnly Property CanWrite As Boolean = True
136:
137: Private Shared ReadOnly _epoch As DateTime = New DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
138:
139: Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
140: writer.WriteRawValue((CType(value, DateTime) - _epoch).TotalMilliseconds)
141: End Sub
142:
143: Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
144: If reader.Value Is Nothing Then
145: Return Nothing
146: Else
147: Return _epoch.AddMilliseconds(CLng(reader.Value)) 'double precizion float
148: End If
149: End Function
150:
151: Public Overrides Function CanConvert(objectType As Type) As Boolean
152: Return objectType Is GetType(DateTime)
153: End Function
Pay attention to variable CanRead/CanWrite. Unlike the previous converter, this converter is possible to use in opposite way too - rrom VB.NET DateTime to Javascript DateTime.
And this is another cool example to parse IPaddress in one case and custom error handling in other case. Usually I use my own ObjectDumper Reflection Object dumper., but this is not need, you always can use NewtonJson object serializer in this way.
1: Imports System.Runtime.CompilerServices
2: Imports Newtonsoft.Json
3:
4: Module Dumper
5: <Extension()>
6: Function ToPrettyString(ByVal value As Object) As String
7: Dim Setting As New JsonSerializerSettings With {
8: .ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
9: .[Error] = AddressOf SerializationErrorHanler
10: }
11: Setting.Converters.Add(New IPAddressConverter)
12: Setting.Converters.Add(New IPEndPointConverter)
13: Return JsonConvert.SerializeObject(value, Newtonsoft.Json.Formatting.Indented, Setting)
14: End Function
15:
16: Sub SerializationErrorHanler(Of T)(sender As Object, args As T)
17: Dim ErrorContext = TryCast(args, Newtonsoft.Json.Serialization.ErrorEventArgs).ErrorContext
18: Console.WriteLine(ErrorContext.Error.Message)
19: ErrorContext.Handled = True
20: End Sub
21:
22: <Extension()>
23: Function Dump(Of T)(ByVal value As T) As T
24: Console.WriteLine(value.ToPrettyString())
25: Return value
26: End Function
27: End Module
1: Imports Newtonsoft.Json
2: Imports Newtonsoft.Json.Linq
3: Imports System.Diagnostics.Eventing
4: Imports System.Net
5:
6: Class IPAddressConverter
7: Inherits JsonConverter
8: Public Overrides Function CanConvert(objectType As Type) As Boolean
9: Return (objectType = GetType(IPAddress))
10: End Function
11: Public Overrides Sub WriteJson(writer As JsonWriter, _value As Object, serializer As JsonSerializer)
12: writer.WriteValue(_value.ToString())
13: End Sub
14: Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
15: Return IPAddress.Parse(CStr(reader.Value))
16: End Function
17: End Class
18:
19: Class IPEndPointConverter
20: Inherits JsonConverter
21: Public Overrides Function CanConvert(objectType As Type) As Boolean
22: Return (objectType = GetType(IPEndPoint))
23: End Function
24: Public Overrides Sub WriteJson(writer As JsonWriter, _value As Object, serializer As JsonSerializer)
25: Dim ep As IPEndPoint = CType(_value, IPEndPoint)
26: Dim jo As New JObject
27: jo.Add("Address", JToken.FromObject(ep.Address, serializer))
28: jo.Add("Port", ep.Port)
29: jo.WriteTo(writer)
30: End Sub
31: Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
32: Dim jo As JObject = JObject.Load(reader)
33: Dim address1 As IPAddress = jo("Address").ToObject(Of IPAddress)(serializer)
34: Dim port1 As Integer = CInt(CLng(Fix(jo("Port"))) Mod Integer.MaxValue)
35: Return New IPEndPoint(address1, port1)
36: End Function
37: End Class
|