1 #Region "Microsoft.VisualBasic::13d066718eb77c8eee2f252c495064d0, Microsoft.VisualBasic.Core\Serialization\JSON\JsonSerialization.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Module JsonContract
35     
36     '         Function: EnsureDate, GetJson, GetObjectJson, LoadJSON, LoadJsonFile
37     '                   LoadJSONObject, (+2 Overloads) LoadObject, MatrixJson, RemoveJsonNullItems, WriteLargeJson
38     
39     '         Sub: writeJsonInternal
40     
41     
42     ' /********************************************************************************/
43
44 #End Region
45
46 Imports System.IO
47 Imports System.Runtime.CompilerServices
48 Imports System.Runtime.Serialization.Json
49 Imports System.Text
50 Imports System.Text.RegularExpressions
51 Imports System.Web.Script.Serialization
52 Imports Microsoft.VisualBasic.CommandLine.Reflection
53 Imports Microsoft.VisualBasic.Language
54 Imports Microsoft.VisualBasic.Linq
55 Imports Microsoft.VisualBasic.Scripting.MetaData
56 Imports Microsoft.VisualBasic.Text
57 Imports Factory = System.Runtime.Serialization.Json.DataContractJsonSerializer
58 Imports r = System.Text.RegularExpressions.Regex
59
60 Namespace Serialization.JSON
61
62     ''' <summary>
63     ''' Only works on the Public visible type.
64     ''' (使用.NET系统环境之中自带的框架进行JSON序列化和反序列化)
65     ''' </summary>
66     <Package("Json.Contract")> Public Module JsonContract
67
68         <Extension>
69         Public Function MatrixJson(matrix As Double()()) As String
70             Dim rows = matrix.Select(Function(row) $"[ {row.JoinBy("")} ]")
71             Dim json = $"[ {rows.JoinBy("," & ASCII.LF)} ]"
72             Return json
73         End Function
74
75         ''' <summary>
76         ''' 使用<see cref="ScriptIgnoreAttribute"/>来屏蔽掉不想序列化的属性
77         ''' </summary>
78         ''' <param name="obj"></param>
79         ''' <param name="type"></param>
80         ''' <returns></returns>
81         <ExportAPI("Get.Json")>
82         <Extension>
83         Public Function GetObjectJson(type As Type, obj As Object,
84                                       Optional indent As Boolean = True,
85                                       Optional simpleDict As Boolean = True,
86                                       Optional knownTypes As IEnumerable(Of Type) = NothingAs String
87
88             Using ms As New MemoryStream()
89                 Call ms.writeJsonInternal(
90                     obj:=obj,
91                     type:=type,
92                     simpleDict:=simpleDict,
93                     knownTypes:=knownTypes
94                 )
95
96                 If indent Then
97                     Return Formatter.Format(Encoding.UTF8.GetString(ms.ToArray()))
98                 Else
99                     Return Encoding.UTF8.GetString(ms.ToArray())
100                 End If
101             End Using
102         End Function
103
104         <MethodImpl(MethodImplOptions.AggressiveInlining)>
105         <Extension>
106         Private Sub writeJsonInternal(output As Stream, obj As Object, type As Type, simpleDict As Boolean, knownTypes As IEnumerable(Of Type))
107             If simpleDict Then
108                 Dim settings As New DataContractJsonSerializerSettings With {
109                     .UseSimpleDictionaryFormat = True,
110                     .SerializeReadOnlyTypes = True,
111                     .KnownTypes = knownTypes _
112                         .SafeQuery _
113                         .ToArray
114                 }
115                 Call New Factory(type, settings).WriteObject(output, obj)
116             Else
117                 Call New Factory(type).WriteObject(output, obj)
118             End If
119         End Sub
120
121         ''' <summary>
122         ''' 将目标对象保存为json文件
123         ''' </summary>
124         ''' <typeparam name="T"></typeparam>
125         ''' <param name="obj"></param>
126         ''' <param name="path"></param>
127         ''' <returns></returns>
128         <Extension>
129         Public Function WriteLargeJson(Of T)(obj As T, path$, Optional simpleDict As Boolean = TrueAs Boolean
130             Using ms As FileStream = path.Open(, doClear:=True)
131                 Call ms.writeJsonInternal(obj, GetType(T), simpleDict, Nothing)
132             End Using
133
134             Return True
135         End Function
136
137         ''' <summary>
138         ''' 有些javascript程序(例如highcharts.js)要求json里面不可以出现null的属性,可以使用这个方法进行移除
139         ''' </summary>
140         ''' <param name="json"></param>
141         ''' <returns></returns>
142         <MethodImpl(MethodImplOptions.AggressiveInlining)>
143         <Extension>
144         Public Function RemoveJsonNullItems(json As StringAs String
145             Return r.Replace(json, """[^""]+""[:]\s*null\s*,?", "", RegexICSng)
146         End Function
147
148         ''' <summary>
149         ''' Gets the json text value of the target object, the attribute <see cref="ScriptIgnoreAttribute"/> 
150         ''' can be used for block the property which is will not serialize to the text.
151         ''' (使用<see cref="ScriptIgnoreAttribute"/>来屏蔽掉不想序列化的属性)
152         ''' </summary>
153         ''' <typeparam name="T"></typeparam>
154         ''' <param name="obj"></param>
155         ''' <returns></returns>
156         ''' <remarks>
157         ''' 2016-11-9 对字典进行序列化的时候,假若对象类型是从字典类型继承而来的,则新的附加属性并不会被序列化,只会序列化字典本身
158         ''' 2018-10-5 不可以序列化匿名类型
159         ''' </remarks>
160         ''' 
161         <MethodImpl(MethodImplOptions.AggressiveInlining)>
162         <Extension> Public Function GetJson(Of T)(obj As T,
163                                                   Optional indent As Boolean = False,
164                                                   Optional simpleDict As Boolean = True,
165                                                   Optional knownTypes As IEnumerable(Of Type) = NothingAs String
166             Return GetType(T).GetObjectJson(obj, indent, simpleDict, knownTypes)
167         End Function
168
169         ''' <summary>
170         ''' 
171         ''' </summary>
172         ''' <param name="json">null -> Nothing</param>
173         ''' <param name="type"></param>
174         ''' <returns></returns>
175         <ExportAPI("LoadObject")>
176         <Extension>
177         Public Function LoadObject(json$,
178                                    type As Type,
179                                    Optional simpleDict As Boolean = True,
180                                    Optional throwEx As Boolean = True,
181                                    Optional ByRef exception As Exception = NothingAs Object
182
183             If String.Equals(json, "null"StringComparison.OrdinalIgnoreCase) Then
184                 Return Nothing
185             End If
186
187             Using MS As New MemoryStream(Encoding.UTF8.GetBytes(json))
188                 Dim settings As New DataContractJsonSerializerSettings With {
189                     .UseSimpleDictionaryFormat = simpleDict,
190                     .SerializeReadOnlyTypes = True
191                 }
192                 Dim ser As New DataContractJsonSerializer(type, settings)
193                 Dim de As Func(Of Object) = Function() ser.ReadObject(MS)
194                 Dim obj = TryCatch(de, $"Incorrect JSON string format => >>>{json}<<<", throwEx, exception)
195                 Return obj
196             End Using
197         End Function
198
199         <Extension>
200         Public Function LoadJSONObject(jsonStream As Stream, type As Type, Optional simpleDict As Boolean = TrueAs Object
201             If jsonStream Is Nothing Then
202                 Return Nothing
203             Else
204                 Dim settings As New DataContractJsonSerializerSettings With {
205                     .UseSimpleDictionaryFormat = simpleDict,
206                     .SerializeReadOnlyTypes = True
207                 }
208                 Return New DataContractJsonSerializer(type, settings) _
209                     .ReadObject(jsonStream)
210             End If
211         End Function
212
213         ''' <summary>
214         ''' 从文本文件或者文本内容之中进行JSON反序列化
215         ''' </summary>
216         ''' <param name="json">This string value can be json text or json file path.</param>
217         <Extension> Public Function LoadJSON(Of T)(json$,
218                                                    Optional simpleDict As Boolean = True,
219                                                    Optional throwEx As Boolean = True,
220                                                    Optional ByRef exception As Exception = NothingAs T
221             Dim text$ = json.SolveStream(Encodings.UTF8)
222             Dim value As Object = text.LoadObject(GetType(T), simpleDict, throwEx, exception)
223             Dim obj As T = DirectCast(value, T)
224             Return obj
225         End Function
226
227         ''' <summary>
228         ''' XML CDATA to json
229         ''' </summary>
230         ''' <typeparam name="T"></typeparam>
231         ''' <param name="json"></param>
232         ''' <param name="simpleDict"></param>
233         ''' <returns></returns>
234         ''' 
235         <MethodImpl(MethodImplOptions.AggressiveInlining)>
236         <Extension>
237         Public Function LoadObject(Of T As New)(json As XElement, Optional simpleDict As Boolean = TrueAs T
238             Return json.Value.LoadJSON(Of T)(simpleDict:=simpleDict)
239         End Function
240
241         <MethodImpl(MethodImplOptions.AggressiveInlining)>
242         <Extension>
243         Public Function LoadJsonFile(Of T)(file$, Optional encoding As Encoding = NothingOptional simpleDict As Boolean = TrueAs T
244             Return (file.ReadAllText(encoding Or UTF8, throwEx:=False, suppress:=True) Or "null".AsDefault) _
245                 .LoadJSON(Of T)(simpleDict)
246         End Function
247
248         Const JsonLongTime$ = "\d+-\d+-\d+T\d+:\d+:\d+\.\d+"
249
250         Public Function EnsureDate(json$, Optional propertyName$ = NothingAs String
251             Dim pattern$ = $"""{JsonLongTime}"""
252
253             If Not propertyName.StringEmpty Then
254                 pattern = $"""{propertyName}""\s*:\s*" & pattern
255             End If
256
257             Dim dates = r.Matches(json, pattern, RegexICSng)
258             Dim sb As New StringBuilder(json)
259             Dim [date] As Date
260
261             For Each m As Match In dates
262                 Dim s$ = m.Value
263
264                 If Not propertyName.StringEmpty Then
265                     With r.Replace(s, $"""{propertyName}""\s*:", "", RegexICSng) _
266                         .Trim _
267                         .Trim(ASCII.Quot)
268
269                         [date] = Date.Parse(.ByRef)
270                     End With
271                     sb.Replace(s, $"""{propertyName}"":" & [date].GetJson)
272                 Else
273                     [date] = Date.Parse(s.Trim(ASCII.Quot))
274                     sb.Replace(s, [date].GetJson)
275                 End If
276             Next
277
278             Return sb.ToString
279         End Function
280     End Module
281 End Namespace