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>
13 // (c)copyright 2002 Ville Palo
15 // XmlDataLoader is included within the Mono Class Library.
21 using System.Xml.XPath;
22 using System.Collections;
23 using System.Globalization;
28 internal class XmlDataLoader
32 Hashtable DiffGrRows = new Hashtable ();
34 public XmlDataLoader (DataSet set)
39 public XmlReadMode LoadData (XmlReader reader, XmlReadMode mode)
41 XmlReadMode Result = XmlReadMode.Auto;
45 case XmlReadMode.Fragment:
47 case XmlReadMode.ReadSchema:
48 case XmlReadMode.IgnoreSchema:
49 case XmlReadMode.InferSchema:
51 ReadModeSchema (reader, mode);
62 // Read information from the reader.
63 private void ReadModeSchema (XmlReader reader, XmlReadMode mode)
65 bool inferSchema = mode == XmlReadMode.InferSchema ? true : false;
66 //check if the current element is schema.
67 if (String.Compare (reader.LocalName, "schema", true) == 0) {
69 if (mode == XmlReadMode.InferSchema || mode == XmlReadMode.IgnoreSchema)
70 reader.Skip(); // skip the schema node.
72 DSet.ReadXmlSchema(reader);
74 reader.MoveToContent();
76 // load an XmlDocument from the reader.
77 XmlDocument doc = BuildXmlDocument(reader);
79 // treatment for .net compliancy :
80 // if xml representing dataset has exactly depth of 2 elements,
81 // than the root element actually represents datatable and not dataset
82 // so we add new root element to doc
83 // in order to create an element representing dataset.
84 int rootNodeDepth = XmlNodeElementsDepth(doc.DocumentElement);
85 if (rootNodeDepth == 2) {
87 String newDataSetName = "NewDataSet";
88 // create new document
89 XmlDocument newDoc = new XmlDocument();
90 // create element for dataset
91 XmlElement datasetElement = newDoc.CreateElement(newDataSetName);
92 // make the new created element to be the new doc root
93 newDoc.AppendChild(datasetElement);
94 // import all the elements from doc and insert them into new doc
95 XmlNode root = newDoc.ImportNode(doc.DocumentElement,true);
96 datasetElement.AppendChild(root);
98 // update dataset name
99 DSet.DataSetName = newDataSetName;
102 // set EnforceConstraint to false - we do not want any validation during
104 bool origEnforceConstraint = DSet.EnforceConstraints;
105 DSet.EnforceConstraints = false;
107 // The childs are tables.
108 XmlNodeList nList = doc.DocumentElement.ChildNodes;
110 for (int i = 0; i < nList.Count; i++) {
111 XmlNode node = nList[i];
112 // node represents a table onky if it is of type XmlNodeType.Element
113 if (node.NodeType == XmlNodeType.Element) {
114 AddRowToTable(node, null, inferSchema);
118 // set the EnforceConstraints to original value;
119 DSet.EnforceConstraints = origEnforceConstraint;
122 #endregion // reading
124 #region Private helper methods
126 private void ReadColumns (XmlReader reader, DataRow row, DataTable table, string TableName)
129 if (reader.NodeType == XmlNodeType.Element) {
130 DataColumn col = table.Columns [reader.LocalName];
132 row [col] = StringToObject (col.DataType, reader.Value);
140 } while (table.TableName != reader.LocalName
141 || reader.NodeType != XmlNodeType.EndElement);
144 internal static object StringToObject (Type type, string value)
146 if (type == null) return value;
148 switch (Type.GetTypeCode (type)) {
149 case TypeCode.Boolean: return XmlConvert.ToBoolean (value);
150 case TypeCode.Byte: return XmlConvert.ToByte (value);
151 case TypeCode.Char: return (char)XmlConvert.ToInt32 (value);
152 case TypeCode.DateTime: return XmlConvert.ToDateTime (value);
153 case TypeCode.Decimal: return XmlConvert.ToDecimal (value);
154 case TypeCode.Double: return XmlConvert.ToDouble (value);
155 case TypeCode.Int16: return XmlConvert.ToInt16 (value);
156 case TypeCode.Int32: return XmlConvert.ToInt32 (value);
157 case TypeCode.Int64: return XmlConvert.ToInt64 (value);
158 case TypeCode.SByte: return XmlConvert.ToSByte (value);
159 case TypeCode.Single: return XmlConvert.ToSingle (value);
160 case TypeCode.UInt16: return XmlConvert.ToUInt16 (value);
161 case TypeCode.UInt32: return XmlConvert.ToUInt32 (value);
162 case TypeCode.UInt64: return XmlConvert.ToUInt64 (value);
165 if (type == typeof (TimeSpan)) return XmlConvert.ToTimeSpan (value);
166 if (type == typeof (Guid)) return XmlConvert.ToGuid (value);
167 if (type == typeof (byte[])) return Convert.FromBase64String (value);
169 return Convert.ChangeType (value, type);
172 private void AddRowToTable(XmlNode tableNode, DataColumn relationColumn, bool inferSchema)
174 Hashtable rowValue = new Hashtable();
177 // Check if the table exists in the DataSet. If not create one.
178 if (DSet.Tables.Contains(tableNode.LocalName))
179 table = DSet.Tables[tableNode.LocalName];
180 else if (inferSchema) {
181 table = new DataTable(tableNode.LocalName);
182 DSet.Tables.Add(table);
187 // For elements that are inferred as tables and that contain text
188 // but have no child elements, a new column named "TableName_Text"
189 // is created for the text of each of the elements.
190 // If an element is inferred as a table and has text, but also has child elements,
191 // the text is ignored.
192 // Note : if an element is inferred as a table and has text
193 // and has no child elements,
194 // but the repeated ements of this table have child elements,
195 // then the text is ignored.
196 if(!HaveChildElements(tableNode) && HaveText(tableNode) &&
197 !IsRepeatedHaveChildNodes(tableNode)) {
198 string columnName = tableNode.Name + "_Text";
199 if (!table.Columns.Contains(columnName)) {
200 table.Columns.Add(columnName);
202 rowValue.Add(columnName, tableNode.InnerText);
205 // Get the child nodes of the table. Any child can be one of the following tow:
206 // 1. DataTable - if there was a relation with another table..
207 // 2. DataColumn - column of the current table.
208 XmlNodeList childList = tableNode.ChildNodes;
209 for (int i = 0; i < childList.Count; i++) {
210 XmlNode childNode = childList[i];
212 // we are looping through elements only
213 // Note : if an element is inferred as a table and has text, but also has child elements,
214 // the text is ignored.
215 if (childNode.NodeType != XmlNodeType.Element)
218 // Elements that have attributes are inferred as tables.
219 // Elements that have child elements are inferred as tables.
220 // Elements that repeat are inferred as a single table.
221 if (IsInferedAsTable(childNode)) {
222 // child node infered as table
224 // We need to create new column for the relation between the current
225 // table and the new table we found (the child table).
226 string newRelationColumnName = table.TableName + "_Id";
227 if (!table.Columns.Contains(newRelationColumnName)) {
228 DataColumn newRelationColumn = new DataColumn(newRelationColumnName, typeof(int));
229 newRelationColumn.AutoIncrement = true;
230 // we do not want to serialize this column so MappingType is Hidden.
231 newRelationColumn.ColumnMapping = MappingType.Hidden;
232 table.Columns.Add(newRelationColumn);
234 // Add a row to the new table we found.
235 AddRowToTable(childNode, table.Columns[newRelationColumnName], inferSchema);
238 AddRowToTable(childNode, null, inferSchema);
242 // Elements that have no attributes or child elements, and do not repeat,
243 // are inferred as columns.
245 if (childNode.FirstChild != null)
246 val = childNode.FirstChild.Value;
249 if (table.Columns.Contains(childNode.LocalName))
250 rowValue.Add(childNode.LocalName, val);
251 else if (inferSchema) {
252 table.Columns.Add(childNode.LocalName);
253 rowValue.Add(childNode.LocalName, val);
259 // Column can be attribute of the table element.
260 XmlAttributeCollection aCollection = tableNode.Attributes;
261 for (int i = 0; i < aCollection.Count; i++) {
262 XmlAttribute attr = aCollection[i];
263 //the atrribute can be the namespace.
264 if (attr.Prefix.Equals("xmlns"))
265 table.Namespace = attr.Value;
266 else { // the attribute is a column.
267 if (!table.Columns.Contains(attr.LocalName)) {
268 DataColumn col = table.Columns.Add(attr.LocalName);
269 col.ColumnMapping = MappingType.Attribute;
271 table.Columns[attr.LocalName].Namespace = table.Namespace;
273 rowValue.Add(attr.LocalName, attr.Value);
277 // If the current table is a child table we need to add a new column for the relation
278 // and add a new relation to the DataSet.
279 if (relationColumn != null) {
280 if (!table.Columns.Contains(relationColumn.ColumnName)) {
281 DataColumn dc = new DataColumn(relationColumn.ColumnName, typeof(int));
282 // we do not want to serialize this column so MappingType is Hidden.
283 dc.ColumnMapping = MappingType.Hidden;
284 table.Columns.Add(dc);
285 // Convention of relation name is: ParentTableName_ChildTableName
286 DataRelation dr = new DataRelation(relationColumn.Table.TableName + "_" + dc.Table.TableName, relationColumn, dc);
288 DSet.Relations.Add(dr);
290 rowValue.Add (relationColumn.ColumnName, relationColumn.GetAutoIncrementValue());
293 // Create new row and add all values to the row.
294 // then add it to the table.
295 DataRow row = table.NewRow ();
297 IDictionaryEnumerator enumerator = rowValue.GetEnumerator ();
298 while (enumerator.MoveNext ()) {
299 row [enumerator.Key.ToString ()] = StringToObject (table.Columns[enumerator.Key.ToString ()].DataType, enumerator.Value.ToString ());
302 table.Rows.Add (row);
306 // bulid the document from the reader.
307 private XmlDocument BuildXmlDocument(XmlReader reader)
309 XmlDocument doc = new XmlDocument();
310 // Create the root element. This is the DataSet element.
311 XmlElement dataSetElement = doc.CreateElement(DSet.DataSetName);
314 XmlNode n = doc.ReadNode (reader);
316 // Add the table nodes to the DataSet node.
317 dataSetElement.AppendChild (n);
318 } while (reader.IsStartElement());
320 // Add the DataSet element to the document.
321 doc.AppendChild(dataSetElement);
325 // this method calculates the depth of child nodes tree
326 // and it counts nodes of type XmlNodeType.Element only
327 private static int XmlNodeElementsDepth(XmlNode node)
330 if ((node != null)) {
331 if ((node.HasChildNodes) && (node.FirstChild.NodeType == XmlNodeType.Element)) {
332 for (int i=0; i<node.ChildNodes.Count; i++) {
333 if (node.ChildNodes[i].NodeType == XmlNodeType.Element) {
334 int childDepth = XmlNodeElementsDepth(node.ChildNodes[i]);
335 maxDepth = (maxDepth < childDepth) ? childDepth : maxDepth;
347 return (maxDepth + 1);
350 private bool HaveChildElements(XmlNode node)
352 bool haveChildElements = true;
353 if(node.ChildNodes.Count > 0) {
354 foreach(XmlNode childNode in node.ChildNodes) {
355 if (childNode.NodeType != XmlNodeType.Element) {
356 haveChildElements = false;
362 haveChildElements = false;
364 return haveChildElements;
367 private bool HaveText(XmlNode node)
369 bool haveText = true;
370 if(node.ChildNodes.Count > 0) {
371 foreach(XmlNode childNode in node.ChildNodes) {
372 if (childNode.NodeType != XmlNodeType.Text) {
384 private bool IsRepeat(XmlNode node)
386 bool isRepeat = false;
387 if(node.ParentNode != null) {
388 foreach(XmlNode childNode in node.ParentNode.ChildNodes) {
389 if(childNode != node && childNode.Name == node.Name) {
398 private bool HaveAttributes(XmlNode node)
400 return (node.Attributes != null && node.Attributes.Count > 0);
403 private bool IsInferedAsTable(XmlNode node)
405 // Elements that have attributes are inferred as tables.
406 // Elements that have child elements are inferred as tables.
407 // Elements that repeat are inferred as a single table.
408 return (HaveChildElements(node) || HaveAttributes(node) ||
413 /// Returns true is any node that is repeated node for the node supplied
414 /// (i.e. is child node of node's parent, have the same name and is not the node itself)
415 /// have child elements
417 private bool IsRepeatedHaveChildNodes(XmlNode node)
419 bool isRepeatedHaveChildElements = false;
420 if(node.ParentNode != null) {
421 foreach(XmlNode childNode in node.ParentNode.ChildNodes) {
422 if(childNode != node && childNode.Name == node.Name) {
423 if (HaveChildElements(childNode)) {
424 isRepeatedHaveChildElements = true;
430 return isRepeatedHaveChildElements;
433 #endregion // Private helper methods