1 #Region "Microsoft.VisualBasic::992ffb00fb8ac813b61f7e02a3637e6e, Microsoft.VisualBasic.Core\Text\Xml\Linq\Linq.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 Data
35     
36     '         FunctionGetNodeNameDefine, GetTypeName, InternalIterates, LoadUltraLargeXMLDataSet, LoadXmlDataSet
37     '                   LoadXmlDocument, NodeInstanceBuilder, PopulateXmlElementText, UltraLargeXmlNodesIterator
38     
39     
40     ' /********************************************************************************/
41
42 #End Region
43
44 Imports System.Runtime.CompilerServices
45 Imports System.Text
46 Imports System.Xml
47 Imports System.Xml.Serialization
48 Imports Microsoft.VisualBasic.Language
49
50 Namespace Text.Xml.Linq
51
52     ''' <summary>
53     ''' Using large xml file as Linq data source
54     ''' </summary>
55     Public Module Data
56
57         ''' <summary>
58         ''' Load a specific xml file from a file location <paramref name="pathOrDoc"/>/ or 
59         ''' a xml text document data into a <see cref="XmlDocument"/> object.
60         ''' </summary>
61         ''' <param name="pathOrDoc"></param>
62         ''' <returns></returns>
63         ''' <remarks>
64         ''' using internally <see cref="XDocument.Load"/> to parse whole XML at once
65         ''' </remarks>
66         <Extension>
67         Public Function LoadXmlDocument(pathOrDoc$, Optional preprocess As Func(Of StringString) = NothingAs XmlDocument
68             Dim xmlDoc As New XmlDocument()
69             Dim doc$
70
71             If pathOrDoc.FileExists Then
72                 If Not preprocess Is Nothing Then
73                     doc = preprocess(pathOrDoc.ReadAllText)
74                     xmlDoc.LoadXml(doc)
75                 Else
76                     Call xmlDoc.Load(pathOrDoc)
77                 End If
78             Else
79                 If Not preprocess Is Nothing Then
80                     doc = preprocess(pathOrDoc)
81                 Else
82                     doc = pathOrDoc
83                 End If
84
85                 Call xmlDoc.LoadXml(doc)
86             End If
87
88             Return xmlDoc
89         End Function
90
91         ''' <summary>
92         ''' Using <paramref name="default"/> string name or <see cref="Type.Name"/>
93         ''' </summary>
94         ''' <param name="type"></param>
95         ''' <param name="default$">
96         ''' If this parameter value is <see cref="StringEmpty"/>, then <see cref="Type.Name"/> will be use as the xml node name.
97         ''' </param>
98         ''' <returns></returns>
99         <Extension>
100         Public Function GetTypeName(type As Type, default$) As String
101             If [default].StringEmpty Then
102                 Return type.Name
103             Else
104                 Return [default]
105             End If
106         End Function
107
108         ''' <summary>
109         ''' 分别解析<see cref="XmlTypeAttribute"/>,<see cref="XmlRootAttribute"/>,如果这两个定义都不存在的话就返回<see cref="Type.Name"/>
110         ''' </summary>
111         ''' <param name="type"></param>
112         ''' <returns></returns>
113         <Extension>
114         Public Function GetNodeNameDefine(type As Type) As String
115             With type.GetCustomAttributes(GetType(XmlTypeAttribute), inherit:=True)
116                 If Not .IsNullOrEmpty Then
117                     With DirectCast(.First, XmlTypeAttribute).TypeName
118                         If Not .StringEmpty Then
119                             Return .ByRef
120                         End If
121                     End With
122                 End If
123             End With
124
125             With type.GetCustomAttributes(GetType(XmlRootAttribute), inherit:=True)
126                 If Not .IsNullOrEmpty Then
127                     With DirectCast(.First, XmlRootAttribute).ElementName
128                         If Not .StringEmpty Then
129                             Return .ByRef
130                         End If
131                     End With
132                 End If
133             End With
134
135             Return type.Name
136         End Function
137
138         Private Iterator Function InternalIterates(XML$, nodeName$) As IEnumerable(Of String)
139             Dim XmlNodeList As XmlNodeList = XML _
140                 .LoadXmlDocument _
141                 .GetElementsByTagName(nodeName)
142             Dim sb As New StringBuilder
143
144             For Each xmlNode As XmlNode In XmlNodeList
145                 Call sb.Clear()
146                 Call sb.Append($"<{nodeName} xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""")
147                 Call sb.Append(" ")
148
149                 For Each attr As XmlAttribute In xmlNode.Attributes
150                     Call sb.Append($"{attr.Name}=""{attr.Value}""")
151                     Call sb.Append(" ")
152                 Next
153
154                 Call sb.AppendLine(">")
155                 Call sb.AppendLine(xmlNode.InnerXml)
156                 Call sb.AppendLine($"</{nodeName}>")
157
158                 Yield sb.ToString
159             Next
160         End Function
161
162         ''' <summary>
163         ''' Only works for the xml file that contains a list or array of xml element, and then this function using this list element as linq data source.
164         ''' (这个函数只建议在读取比较小的XML文件的时候使用,并且这个XML文件仅仅是一个数组或者列表的序列化结果)
165         ''' </summary>
166         ''' <typeparam name="T"></typeparam>
167         ''' <param name="XML$">超大的XML文件的文件路径</param>
168         ''' <param name="typeName">
169         ''' 列表之中的节点在XML之中的tag标记名称,假若这个参数值为空的话,则会默认使用目标类型名称<see cref="Type.Name"/>
170         ''' </param>
171         ''' <param name="xmlns">
172         ''' Using for the namespace replacement.
173         ''' (当这个参数存在的时候,目标命名空间申明将会被替换为空字符串,数据对象才会被正确的加载)
174         ''' </param>
175         ''' <returns></returns>
176         <Extension>
177         Public Function LoadXmlDataSet(Of T As Class)(XML$, Optional typeName$ = NothingOptional xmlns$ = NothingOptional forceLargeMode As Boolean = FalseAs IEnumerable(Of T)
178             Dim nodeName$ = GetType(T).GetTypeName([default]:=typeName)
179             Dim source As IEnumerable(Of String)
180
181             If forceLargeMode OrElse XML.FileLength > 1024 * 1024 * 128 Then
182                 ' 这是一个超大的XML文档
183                 source = NodeIterator.IterateArrayNodes(XML, nodeName)
184                 xmlns = Nothing
185             Else
186                 source = InternalIterates(XML, nodeName)
187             End If
188
189             Return source.NodeInstanceBuilder(Of T)(xmlns, xmlNode:=nodeName)
190         End Function
191
192         ''' <summary>
193         ''' 从给定的文本之中利用反序列化从XML字符串构建出.NET对象
194         ''' </summary>
195         ''' <typeparam name="T"></typeparam>
196         ''' <param name="nodes"></param>
197         ''' <param name="replaceXmlns$"></param>
198         ''' <param name="xmlNode$">文件之中的节点名称</param>
199         ''' <returns></returns>
200         <Extension>
201         Private Iterator Function NodeInstanceBuilder(Of T As Class)(nodes As IEnumerable(Of String), replaceXmlns$, xmlNode$) As IEnumerable(Of T)
202             Dim handle As New DeserializeHandler(Of T)(xmlNode) With {
203                 .ReplaceXmlns = replaceXmlns
204             }
205
206             For Each xml As String In nodes
207                 Yield handle.LoadXml(xml)
208             Next
209         End Function
210
211         ''' <summary>
212         ''' Apply on a ultra large size XML database, which its data size is greater than 1GB to 100GB or even more.
213         ''' </summary>
214         ''' <typeparam name="T"></typeparam>
215         ''' <param name="path$"></param>
216         ''' <param name="typeName$"></param>
217         ''' <param name="xmlns$"></param>
218         ''' <returns></returns>
219         <Extension>
220         Public Function LoadUltraLargeXMLDataSet(Of T As Class)(path$, Optional typeName$ = NothingOptional xmlns$ = NothingAs IEnumerable(Of T)
221             Dim nodeName$ = GetType(T).GetTypeName([default]:=typeName)
222             Return nodeName _
223                 .UltraLargeXmlNodesIterator(path) _
224                 .NodeInstanceBuilder(Of T)(xmlns, xmlNode:=nodeName)
225         End Function
226
227         <Extension>
228         Private Iterator Function UltraLargeXmlNodesIterator(nodeName$, path$) As IEnumerable(Of String)
229             Dim el As New Value(Of XElement)
230             Dim XML$
231
232             Using reader As XmlReader = XmlReader.Create(path)
233
234                 Call reader.MoveToContent()
235
236                 Do While (reader.Read())
237                     ' Parse the file And return each of the child_node
238                     If (reader.NodeType = XmlNodeType.Element AndAlso reader.Name = nodeName) Then
239                         If (Not (el = XNode.ReadFrom(reader)) Is NothingThen
240                             XML = el.Value.ToString
241                             Yield XML
242                         End If
243                     End If
244                 Loop
245             End Using
246         End Function
247
248         <MethodImpl(MethodImplOptions.AggressiveInlining)>
249         <Extension>
250         Public Function PopulateXmlElementText(Of T As Class)(path$, Optional typeName$ = NothingAs IEnumerable(Of String)
251             Return GetType(T) _
252                 .GetTypeName([default]:=typeName) _
253                 .UltraLargeXmlNodesIterator(path)
254         End Function
255     End Module
256 End Namespace