2 // mcs/class/System.Data/System.Data/XmlDataLoader.cs
4 // Purpose: Loads XmlDocument to DataSet
6 // class: XmlDataLoader
7 // assembly: System.Data.dll
8 // namespace: System.Data
11 // Ville Palo <vi64pa@koti.soon.fi>
12 // Atsushi Enomoto <atsushi@ximian.com>
14 // (c)copyright 2002 Ville Palo
15 // (C)2004 Novell Inc.
17 // XmlDataLoader is included within the Mono Class Library.
21 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
23 // Permission is hereby granted, free of charge, to any person obtaining
24 // a copy of this software and associated documentation files (the
25 // "Software"), to deal in the Software without restriction, including
26 // without limitation the rights to use, copy, modify, merge, publish,
27 // distribute, sublicense, and/or sell copies of the Software, and to
28 // permit persons to whom the Software is furnished to do so, subject to
29 // the following conditions:
31 // The above copyright notice and this permission notice shall be
32 // included in all copies or substantial portions of the Software.
34 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
35 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
36 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
37 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
38 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
39 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
40 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46 using System
.Collections
;
47 using System
.Globalization
;
52 internal class XmlDataLoader
57 public XmlDataLoader (DataSet
set)
62 public XmlReadMode
LoadData (XmlReader reader
, XmlReadMode mode
)
64 XmlReadMode Result
= mode
;
67 case XmlReadMode
.Auto
:
68 Result
= DSet
.Tables
.Count
== 0 ? XmlReadMode
.InferSchema
: XmlReadMode
.IgnoreSchema
;
69 ReadModeSchema (reader
, DSet
.Tables
.Count
== 0 ? XmlReadMode
.Auto
: XmlReadMode
.IgnoreSchema
);
71 case XmlReadMode
.InferSchema
:
72 Result
= XmlReadMode
.InferSchema
;
73 ReadModeSchema (reader
, mode
);
75 case XmlReadMode
.IgnoreSchema
:
76 Result
= XmlReadMode
.IgnoreSchema
;
77 ReadModeSchema (reader
, mode
);
89 // Read information from the reader.
90 private void ReadModeSchema (XmlReader reader
, XmlReadMode mode
)
92 bool inferSchema
= mode
== XmlReadMode
.InferSchema
|| mode
== XmlReadMode
.Auto
;
93 bool fillRows
= mode
!= XmlReadMode
.InferSchema
;
94 // This check is required for full DiffGram.
95 // It is not described in MSDN and it is impossible
96 // with WriteXml(), but when writing XML using
97 // XmlSerializer, the output is like this:
99 // <schema>...</schema>
100 // <diffgram>...</diffgram>
103 // FIXME: This, this check should (also) be done
104 // after reading the top-level element.
106 //check if the current element is schema.
107 if (reader
.LocalName
== "schema") {
108 if (mode
!= XmlReadMode
.Auto
)
109 reader
.Skip(); // skip the schema node.
111 DSet
.ReadXmlSchema(reader
);
113 reader
.MoveToContent();
116 // load an XmlDocument from the reader.
117 XmlDocument doc
= new XmlDocument ();
119 if (doc
.DocumentElement
== null)
122 // treatment for .net compliancy :
123 // if xml representing dataset has exactly depth of 2 elements,
124 // than the root element actually represents datatable and not dataset
125 // so we add new root element to doc
126 // in order to create an element representing dataset.
128 // FIXME: Consider attributes.
129 // <root a='1' b='2' /> is regarded as a valid DataTable.
130 int rootNodeDepth
= XmlNodeElementsDepth(doc
.DocumentElement
);
131 switch (rootNodeDepth
) {
134 DSet
.DataSetName
= doc
.DocumentElement
.LocalName
;
135 DSet
.Prefix
= doc
.DocumentElement
.Prefix
;
136 DSet
.Namespace
= doc
.DocumentElement
.NamespaceURI
;
140 // create new document
141 XmlDocument newDoc
= new XmlDocument();
142 // create element for dataset
143 XmlElement datasetElement
= newDoc
.CreateElement("dummy");
144 // make the new created element to be the new doc root
145 newDoc
.AppendChild(datasetElement
);
146 // import all the elements from doc and insert them into new doc
147 XmlNode root
= newDoc
.ImportNode(doc
.DocumentElement
,true);
148 datasetElement
.AppendChild(root
);
153 DSet
.DataSetName
= doc
.DocumentElement
.LocalName
;
154 DSet
.Prefix
= doc
.DocumentElement
.Prefix
;
155 DSet
.Namespace
= doc
.DocumentElement
.NamespaceURI
;
160 // set EnforceConstraint to false - we do not want any validation during
162 bool origEnforceConstraint
= DSet
.EnforceConstraints
;
163 DSet
.EnforceConstraints
= false;
165 // The childs are tables.
166 XmlNodeList nList
= doc
.DocumentElement
.ChildNodes
;
168 // FIXME: When reading DataTable (not DataSet),
169 // the nodes are column items, not rows.
170 for (int i
= 0; i
< nList
.Count
; i
++) {
171 XmlNode node
= nList
[i
];
172 // node represents a table onky if it is of type XmlNodeType.Element
173 if (node
.NodeType
== XmlNodeType
.Element
) {
174 AddRowToTable(node
, null, inferSchema
, fillRows
);
177 // set the EnforceConstraints to original value;
178 DSet
.EnforceConstraints
= origEnforceConstraint
;
181 #endregion // reading
183 #region Private helper methods
185 private void ReadColumns (XmlReader reader
, DataRow row
, DataTable table
, string TableName
)
188 if (reader
.NodeType
== XmlNodeType
.Element
) {
189 DataColumn col
= table
.Columns
[reader
.LocalName
];
191 row
[col
] = StringToObject (col
.DataType
, reader
.Value
);
199 } while (table
.TableName
!= reader
.LocalName
200 || reader
.NodeType
!= XmlNodeType
.EndElement
);
203 internal static object StringToObject (Type type
, string value)
205 if (type
== null) return value;
207 switch (Type
.GetTypeCode (type
)) {
208 case TypeCode
.Boolean
: return XmlConvert
.ToBoolean (value);
209 case TypeCode
.Byte
: return XmlConvert
.ToByte (value);
210 case TypeCode
.Char
: return (char)XmlConvert
.ToInt32 (value);
211 case TypeCode
.DateTime
: return XmlConvert
.ToDateTime (value);
212 case TypeCode
.Decimal
: return XmlConvert
.ToDecimal (value);
213 case TypeCode
.Double
: return XmlConvert
.ToDouble (value);
214 case TypeCode
.Int16
: return XmlConvert
.ToInt16 (value);
215 case TypeCode
.Int32
: return XmlConvert
.ToInt32 (value);
216 case TypeCode
.Int64
: return XmlConvert
.ToInt64 (value);
217 case TypeCode
.SByte
: return XmlConvert
.ToSByte (value);
218 case TypeCode
.Single
: return XmlConvert
.ToSingle (value);
219 case TypeCode
.UInt16
: return XmlConvert
.ToUInt16 (value);
220 case TypeCode
.UInt32
: return XmlConvert
.ToUInt32 (value);
221 case TypeCode
.UInt64
: return XmlConvert
.ToUInt64 (value);
224 if (type
== typeof (TimeSpan
)) return XmlConvert
.ToTimeSpan (value);
225 if (type
== typeof (Guid
)) return XmlConvert
.ToGuid (value);
226 if (type
== typeof (byte[])) return Convert
.FromBase64String (value);
228 return Convert
.ChangeType (value, type
);
231 private void AddRowToTable(XmlNode tableNode
, DataColumn relationColumn
, bool inferSchema
, bool fillRows
)
233 Hashtable rowValue
= new Hashtable();
236 // Check if the table exists in the DataSet. If not create one.
237 if (DSet
.Tables
.Contains(tableNode
.LocalName
))
238 table
= DSet
.Tables
[tableNode
.LocalName
];
239 else if (inferSchema
) {
240 table
= new DataTable(tableNode
.LocalName
);
241 DSet
.Tables
.Add(table
);
246 // For elements that are inferred as tables and that contain text
247 // but have no child elements, a new column named "TableName_Text"
248 // is created for the text of each of the elements.
249 // If an element is inferred as a table and has text, but also has child elements,
250 // the text is ignored.
251 // Note : if an element is inferred as a table and has text
252 // and has no child elements,
253 // but the repeated ements of this table have child elements,
254 // then the text is ignored.
255 if(!HaveChildElements(tableNode
) && HaveText(tableNode
) &&
256 !IsRepeatedHaveChildNodes(tableNode
)) {
257 string columnName
= tableNode
.Name
+ "_Text";
258 if (!table
.Columns
.Contains(columnName
)) {
259 table
.Columns
.Add(columnName
);
261 rowValue
.Add(columnName
, tableNode
.InnerText
);
264 // Get the child nodes of the table. Any child can be one of the following tow:
265 // 1. DataTable - if there was a relation with another table..
266 // 2. DataColumn - column of the current table.
267 XmlNodeList childList
= tableNode
.ChildNodes
;
268 for (int i
= 0; i
< childList
.Count
; i
++) {
269 XmlNode childNode
= childList
[i
];
271 // we are looping through elements only
272 // Note : if an element is inferred as a table and has text, but also has child elements,
273 // the text is ignored.
274 if (childNode
.NodeType
!= XmlNodeType
.Element
)
277 // Elements that have attributes are inferred as tables.
278 // Elements that have child elements are inferred as tables.
279 // Elements that repeat are inferred as a single table.
280 if (IsInferedAsTable(childNode
)) {
281 // child node infered as table
283 // We need to create new column for the relation between the current
284 // table and the new table we found (the child table).
285 string newRelationColumnName
= table
.TableName
+ "_Id";
286 if (!table
.Columns
.Contains(newRelationColumnName
)) {
287 DataColumn newRelationColumn
= new DataColumn(newRelationColumnName
, typeof(int));
288 newRelationColumn
.AllowDBNull
= false;
289 newRelationColumn
.AutoIncrement
= true;
290 // we do not want to serialize this column so MappingType is Hidden.
291 newRelationColumn
.ColumnMapping
= MappingType
.Hidden
;
292 table
.Columns
.Add(newRelationColumn
);
294 // Add a row to the new table we found.
295 AddRowToTable(childNode
, table
.Columns
[newRelationColumnName
], inferSchema
, fillRows
);
298 AddRowToTable(childNode
, null, inferSchema
, fillRows
);
302 // Elements that have no attributes or child elements, and do not repeat,
303 // are inferred as columns.
305 if (childNode
.FirstChild
!= null)
306 val
= childNode
.FirstChild
.Value
;
309 if (table
.Columns
.Contains(childNode
.LocalName
))
310 rowValue
.Add(childNode
.LocalName
, val
);
311 else if (inferSchema
) {
312 table
.Columns
.Add(childNode
.LocalName
);
313 rowValue
.Add(childNode
.LocalName
, val
);
319 // Column can be attribute of the table element.
320 XmlAttributeCollection aCollection
= tableNode
.Attributes
;
321 for (int i
= 0; i
< aCollection
.Count
; i
++) {
322 XmlAttribute attr
= aCollection
[i
];
323 //the atrribute can be the namespace.
324 if (attr
.Prefix
.Equals("xmlns"))
325 table
.Namespace
= attr
.Value
;
326 else { // the attribute is a column.
327 if (!table
.Columns
.Contains(attr
.LocalName
)) {
328 DataColumn col
= table
.Columns
.Add(attr
.LocalName
);
329 col
.ColumnMapping
= MappingType
.Attribute
;
331 table
.Columns
[attr
.LocalName
].Namespace
= table
.Namespace
;
333 rowValue
.Add(attr
.LocalName
, attr
.Value
);
337 // If the current table is a child table we need to add a new column for the relation
338 // and add a new relation to the DataSet.
339 if (relationColumn
!= null) {
340 if (!table
.Columns
.Contains(relationColumn
.ColumnName
)) {
341 DataColumn dc
= new DataColumn(relationColumn
.ColumnName
, typeof(int));
342 // we do not want to serialize this column so MappingType is Hidden.
343 dc
.ColumnMapping
= MappingType
.Hidden
;
344 table
.Columns
.Add(dc
);
345 // Convention of relation name is: ParentTableName_ChildTableName
346 DataRelation dr
= new DataRelation(relationColumn
.Table
.TableName
+ "_" + dc
.Table
.TableName
, relationColumn
, dc
);
348 DSet
.Relations
.Add(dr
);
349 UniqueConstraint
.SetAsPrimaryKey (dr
.ParentTable
.Constraints
, dr
.ParentKeyConstraint
);
351 rowValue
.Add (relationColumn
.ColumnName
, relationColumn
.GetAutoIncrementValue());
354 // Create new row and add all values to the row.
355 // then add it to the table.
356 DataRow row
= table
.NewRow ();
358 IDictionaryEnumerator enumerator
= rowValue
.GetEnumerator ();
359 while (enumerator
.MoveNext ()) {
360 row
[enumerator
.Key
.ToString ()] = StringToObject (table
.Columns
[enumerator
.Key
.ToString ()].DataType
, enumerator
.Value
.ToString ());
364 table
.Rows
.Add (row
);
368 // this method calculates the depth of child nodes tree
369 // and it counts nodes of type XmlNodeType.Element only
370 private static int XmlNodeElementsDepth(XmlNode node
)
373 if ((node
!= null)) {
374 if ((node
.HasChildNodes
) && (node
.FirstChild
.NodeType
== XmlNodeType
.Element
)) {
375 for (int i
=0; i
<node
.ChildNodes
.Count
; i
++) {
376 if (node
.ChildNodes
[i
].NodeType
== XmlNodeType
.Element
) {
377 int childDepth
= XmlNodeElementsDepth(node
.ChildNodes
[i
]);
378 maxDepth
= (maxDepth
< childDepth
) ? childDepth
: maxDepth
;
390 return (maxDepth
+ 1);
393 private bool HaveChildElements(XmlNode node
)
395 bool haveChildElements
= true;
396 if(node
.ChildNodes
.Count
> 0) {
397 foreach(XmlNode childNode
in node
.ChildNodes
) {
398 if (childNode
.NodeType
!= XmlNodeType
.Element
) {
399 haveChildElements
= false;
405 haveChildElements
= false;
407 return haveChildElements
;
410 private bool HaveText(XmlNode node
)
412 bool haveText
= true;
413 if(node
.ChildNodes
.Count
> 0) {
414 foreach(XmlNode childNode
in node
.ChildNodes
) {
415 if (childNode
.NodeType
!= XmlNodeType
.Text
) {
427 private bool IsRepeat(XmlNode node
)
429 bool isRepeat
= false;
430 if(node
.ParentNode
!= null) {
431 foreach(XmlNode childNode
in node
.ParentNode
.ChildNodes
) {
432 if(childNode
!= node
&& childNode
.Name
== node
.Name
) {
441 private bool HaveAttributes(XmlNode node
)
443 return (node
.Attributes
!= null && node
.Attributes
.Count
> 0);
446 private bool IsInferedAsTable(XmlNode node
)
448 // Elements that have attributes are inferred as tables.
449 // Elements that have child elements are inferred as tables.
450 // Elements that repeat are inferred as a single table.
451 return (HaveChildElements(node
) || HaveAttributes(node
) ||
456 /// Returns true is any node that is repeated node for the node supplied
457 /// (i.e. is child node of node's parent, have the same name and is not the node itself)
458 /// have child elements
460 private bool IsRepeatedHaveChildNodes(XmlNode node
)
462 bool isRepeatedHaveChildElements
= false;
463 if(node
.ParentNode
!= null) {
464 foreach(XmlNode childNode
in node
.ParentNode
.ChildNodes
) {
465 if(childNode
!= node
&& childNode
.Name
== node
.Name
) {
466 if (HaveChildElements(childNode
)) {
467 isRepeatedHaveChildElements
= true;
473 return isRepeatedHaveChildElements
;
476 #endregion // Private helper methods