2 // XmlDataInferenceLoader.cs
\r
6 // Atsushi Enomoto <atsushi@ximian.com>
\r
8 // (C)2004 Novell Inc.
\r
10 // Design notes are the bottom of the source.
\r
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections;
\r
38 using System.IO; // for Driver
\r
39 using System.Text; // for Driver
\r
41 using System.Xml.Schema;
\r
42 using System.Xml.Serialization;
\r
44 namespace System.Data
\r
46 internal enum ElementMappingType {
\r
52 internal class TableMappingCollection : CollectionBase
\r
54 public void Add (TableMapping map)
\r
56 this.List.Add (map);
\r
59 public TableMapping this [string name] {
\r
61 foreach (TableMapping map in List)
\r
62 if (map.Table.TableName == name)
\r
69 internal class TableMapping
\r
71 private bool existsInDataSet;
\r
73 public DataTable Table;
\r
74 public ArrayList Elements = new ArrayList ();
\r
75 public ArrayList Attributes = new ArrayList ();
\r
76 public DataColumn SimpleContent;
\r
77 public DataColumn PrimaryKey;
\r
78 public DataColumn ReferenceKey;
\r
80 // Parent TableMapping
\r
81 public TableMapping ParentTable;
\r
83 // decoded LocalName -> TableMapping
\r
84 public TableMappingCollection ChildTables = new TableMappingCollection ();
\r
86 public TableMapping (string name, string ns)
\r
88 Table = new DataTable (name);
\r
89 Table.Namespace = ns;
\r
92 public TableMapping (DataTable dt)
\r
94 existsInDataSet = true;
\r
96 foreach (DataColumn col in dt.Columns) {
\r
97 switch (col.ColumnMapping) {
\r
98 case MappingType.Element:
\r
101 case MappingType.Attribute:
\r
102 Attributes.Add (col);
\r
104 case MappingType.SimpleContent:
\r
105 SimpleContent = col;
\r
109 PrimaryKey = dt.PrimaryKey.Length > 0 ? dt.PrimaryKey [0] : null;
\r
112 public bool ExistsInDataSet {
\r
113 get { return existsInDataSet; }
\r
116 public bool ContainsColumn (string name)
\r
118 return GetColumn (name) != null;
\r
121 public DataColumn GetColumn (string name)
\r
123 foreach (DataColumn col in Elements)
\r
124 if (col.ColumnName == name)
\r
126 foreach (DataColumn col in Attributes)
\r
127 if (col.ColumnName == name)
\r
129 if (SimpleContent != null && name == SimpleContent.ColumnName)
\r
130 return SimpleContent;
\r
131 if (PrimaryKey != null && name == PrimaryKey.ColumnName)
\r
136 public void RemoveElementColumn (string name)
\r
138 foreach (DataColumn col in Elements) {
\r
139 if (col.ColumnName == name) {
\r
140 Elements.Remove (col);
\r
147 internal class XmlDataInferenceLoader
\r
149 public static void Infer (DataSet dataset, XmlDocument document, XmlReadMode mode, string [] ignoredNamespaces)
\r
151 new XmlDataInferenceLoader (dataset, document, mode, ignoredNamespaces).ReadXml ();
\r
154 private XmlDataInferenceLoader (DataSet ds, XmlDocument doc, XmlReadMode mode, string [] ignoredNamespaces)
\r
159 this.ignoredNamespaces = ignoredNamespaces != null ? new ArrayList (ignoredNamespaces) : new ArrayList ();
\r
161 // Fill existing table info
\r
162 foreach (DataTable dt in dataset.Tables)
\r
163 tables.Add (new TableMapping (dt));
\r
167 XmlDocument document;
\r
169 ArrayList ignoredNamespaces;
\r
170 TableMappingCollection tables = new TableMappingCollection ();
\r
171 RelationStructureCollection relations = new RelationStructureCollection ();
\r
173 private void ReadXml ()
\r
175 if (document.DocumentElement == null)
\r
178 // If the root element is not a data table, treat
\r
179 // this element as DataSet.
\r
180 // Read one element. It might be DataSet element.
\r
181 XmlElement el = document.DocumentElement;
\r
183 if (el.NamespaceURI == XmlSchema.Namespace)
\r
184 throw new InvalidOperationException ("DataSet is not designed to handle XML Schema as data content. Please use ReadXmlSchema method instead of InferXmlSchema method.");
\r
186 if (IsDocumentElementTable ())
\r
187 InferTopLevelTable (el);
\r
189 string localName = XmlConvert.DecodeName (el.LocalName);
\r
190 dataset.DataSetName = localName;
\r
191 dataset.Namespace = el.NamespaceURI;
\r
192 dataset.Prefix = el.Prefix;
\r
193 foreach (XmlNode n in el.ChildNodes) {
\r
194 if (n.NamespaceURI == XmlSchema.Namespace)
\r
196 if (n.NodeType == XmlNodeType.Element)
\r
197 InferTopLevelTable (n as XmlElement);
\r
201 foreach (TableMapping map in tables) {
\r
202 foreach (TableMapping ct in map.ChildTables) {
\r
203 ct.ReferenceKey = GetMappedColumn (ct, map.Table.TableName + "_Id", map.Table.Prefix, map.Table.Namespace, MappingType.Hidden);
\r
207 foreach (TableMapping map in tables) {
\r
208 if (map.ExistsInDataSet)
\r
210 if (map.PrimaryKey != null)
\r
211 map.Table.Columns.Add (map.PrimaryKey);
\r
212 foreach (DataColumn col in map.Elements)
\r
213 map.Table.Columns.Add (col);
\r
214 foreach (DataColumn col in map.Attributes)
\r
215 map.Table.Columns.Add (col);
\r
216 if (map.SimpleContent != null)
\r
217 map.Table.Columns.Add (map.SimpleContent);
\r
218 if (map.ReferenceKey != null)
\r
219 map.Table.Columns.Add (map.ReferenceKey);
\r
220 dataset.Tables.Add (map.Table);
\r
223 foreach (RelationStructure rs in relations) {
\r
224 string relName = rs.ExplicitName != null ? rs.ExplicitName : rs.ParentTableName + "_" + rs.ChildTableName;
\r
225 DataTable pt = dataset.Tables [rs.ParentTableName];
\r
226 DataTable ct = dataset.Tables [rs.ChildTableName];
\r
227 DataColumn pc = pt.Columns [rs.ParentColumnName];
\r
228 DataColumn cc = ct.Columns [rs.ChildColumnName];
\r
230 throw new DataException ("Parent table was not found : " + rs.ParentTableName);
\r
231 else if (ct == null)
\r
232 throw new DataException ("Child table was not found : " + rs.ChildTableName);
\r
233 else if (pc == null)
\r
234 throw new DataException ("Parent column was not found :" + rs.ParentColumnName);
\r
235 else if (cc == null)
\r
236 throw new DataException ("Child column was not found :" + rs.ChildColumnName);
\r
237 DataRelation rel = new DataRelation (relName, pc, cc, rs.CreateConstraint);
\r
240 rel.ParentTable.PrimaryKey = rel.ParentColumns;
\r
242 dataset.Relations.Add (rel);
\r
246 private void InferTopLevelTable (XmlElement el)
\r
248 InferTableElement (null, el);
\r
251 private void InferColumnElement (TableMapping table, XmlElement el)
\r
253 string localName = XmlConvert.DecodeName (el.LocalName);
\r
254 DataColumn col = table.GetColumn (localName);
\r
256 if (col.ColumnMapping != MappingType.Element)
\r
257 throw new DataException (String.Format ("Column {0} is already mapped to {1}.", localName, col.ColumnMapping));
\r
260 if (table.ChildTables [localName] != null)
\r
261 // Child is already mapped, or infered as a table
\r
262 // (in that case, that takes precedence than
\r
263 // this simple column inference.)
\r
266 col = new DataColumn (localName, typeof (string));
\r
267 col.Namespace = el.NamespaceURI;
\r
268 col.Prefix = el.Prefix;
\r
269 table.Elements.Add (col);
\r
272 private void CheckExtraneousElementColumn (TableMapping parentTable, XmlElement el)
\r
274 if (parentTable == null)
\r
276 string localName = XmlConvert.DecodeName (el.LocalName);
\r
277 DataColumn elc = parentTable.GetColumn (localName);
\r
279 parentTable.RemoveElementColumn (localName);
\r
282 private void PopulatePrimaryKey (TableMapping table)
\r
284 if (table.PrimaryKey != null) {
\r
285 if (table.PrimaryKey.ColumnName != table.Table.TableName + "_Id")
\r
286 throw new DataException ("There is already a primary key column.");
\r
289 DataColumn col = new DataColumn (table.Table.TableName + "_Id");
\r
290 col.ColumnMapping = MappingType.Hidden;
\r
291 col.DataType = typeof (int);
\r
292 col.AllowDBNull = false;
\r
293 col.AutoIncrement = true;
\r
294 col.Namespace = table.Table.Namespace;
\r
295 col.Prefix = table.Table.Prefix;
\r
296 table.PrimaryKey = col;
\r
299 private void PopulateRelationStructure (string parent, string child)
\r
301 if (relations [parent, child] != null)
\r
304 RelationStructure rs = new RelationStructure ();
\r
305 rs.ParentTableName = parent;
\r
306 rs.ChildTableName = child;
\r
307 rs.ParentColumnName = parent + "_Id";
\r
308 rs.ChildColumnName = parent + "_Id";
\r
309 rs.CreateConstraint = true;
\r
310 rs.IsNested = true;
\r
311 relations.Add (rs);
\r
314 private void InferRepeatedElement (TableMapping parentTable, XmlElement el)
\r
316 string localName = XmlConvert.DecodeName (el.LocalName);
\r
317 // FIXME: can be checked later
\r
318 CheckExtraneousElementColumn (parentTable, el);
\r
319 TableMapping table = GetMappedTable (parentTable, localName, el.NamespaceURI);
\r
321 // If the mapping is actually complex type (not simple
\r
322 // repeatable), then ignore it.
\r
323 if (table.Elements.Count > 0)
\r
326 // If simple column already exists, do nothing
\r
327 if (table.SimpleContent != null)
\r
330 GetMappedColumn (table, localName + "_Column", el.Prefix, el.NamespaceURI, MappingType.SimpleContent);
\r
333 private void InferTableElement (TableMapping parentTable, XmlElement el)
\r
335 // If parent table already has the same name column but
\r
336 // mapped as Element, that must be removed.
\r
337 // FIXME: This can be done later (doing it here is
\r
338 // loss of performance.
\r
339 CheckExtraneousElementColumn (parentTable, el);
\r
341 string localName = XmlConvert.DecodeName (el.LocalName);
\r
342 TableMapping table = GetMappedTable (parentTable, localName, el.NamespaceURI);
\r
344 bool hasChildElements = false;
\r
345 bool hasAttributes = false;
\r
346 bool hasText = false;
\r
348 foreach (XmlAttribute attr in el.Attributes) {
\r
349 if (attr.NamespaceURI == XmlConstants.XmlnsNS)
\r
351 if (ignoredNamespaces != null &&
\r
352 ignoredNamespaces.Contains (attr.NamespaceURI))
\r
355 hasAttributes = true;
\r
356 DataColumn col = GetMappedColumn (table,
\r
357 XmlConvert.DecodeName (attr.LocalName),
\r
360 MappingType.Attribute);
\r
363 foreach (XmlNode n in el.ChildNodes) {
\r
364 switch (n.NodeType) {
\r
365 case XmlNodeType.Comment:
\r
366 case XmlNodeType.ProcessingInstruction: // ignore
\r
368 default: // text content
\r
371 case XmlNodeType.Element: // child
\r
372 hasChildElements = true;
\r
373 XmlElement cel = n as XmlElement;
\r
374 string childLocalName = XmlConvert.DecodeName (cel.LocalName);
\r
376 switch (GetElementMappingType (cel, ignoredNamespaces)) {
\r
377 case ElementMappingType.Simple:
\r
378 InferColumnElement (table, cel);
\r
380 case ElementMappingType.Repeated:
\r
381 PopulatePrimaryKey (table);
\r
382 PopulateRelationStructure (table.Table.TableName, childLocalName);
\r
383 InferRepeatedElement (table, cel);
\r
385 case ElementMappingType.Complex:
\r
386 PopulatePrimaryKey (table);
\r
387 PopulateRelationStructure (table.Table.TableName, childLocalName);
\r
388 InferTableElement (table, cel);
\r
395 // Attributes + !Children + Text = SimpleContent
\r
396 if (table.SimpleContent == null // no need to create
\r
397 && !hasChildElements && hasText && hasAttributes) {
\r
398 GetMappedColumn (table, table.Table.TableName + "_Text", String.Empty, String.Empty, MappingType.SimpleContent);
\r
402 private TableMapping GetMappedTable (TableMapping parent, string tableName, string ns)
\r
404 TableMapping map = tables [tableName];
\r
406 if (parent != null && map.ParentTable != null && map.ParentTable != parent)
\r
407 throw new DataException (String.Format ("The table {0} is already allocated as another table's child table.", tableName));
\r
409 map = new TableMapping (tableName, ns);
\r
410 map.ParentTable = parent;
\r
413 if (parent != null) {
\r
414 bool shouldAdd = true;
\r
415 foreach (TableMapping child in parent.ChildTables) {
\r
416 if (child.Table.TableName == tableName) {
\r
422 parent.ChildTables.Add (map);
\r
427 private DataColumn GetMappedColumn (TableMapping table, string name, string prefix, string ns, MappingType type)
\r
429 DataColumn col = table.GetColumn (name);
\r
432 col = new DataColumn (name);
\r
433 col.Prefix = prefix;
\r
434 col.Namespace = ns;
\r
435 col.ColumnMapping = type;
\r
437 case MappingType.Element:
\r
438 table.Elements.Add (col);
\r
440 case MappingType.Attribute:
\r
441 table.Attributes.Add (col);
\r
443 case MappingType.SimpleContent:
\r
444 table.SimpleContent = col;
\r
446 case MappingType.Hidden:
\r
447 // To generate parent key
\r
448 col.DataType = typeof (int);
\r
449 table.ReferenceKey = col;
\r
453 else if (col.ColumnMapping != type) // Check mapping type
\r
454 throw new DataException (String.Format ("There are already another column that has different mapping type. Column is {0}, existing mapping type is {1}", col.ColumnName, col.ColumnMapping));
\r
459 private static ElementMappingType GetElementMappingType (
\r
460 XmlElement el, ArrayList ignoredNamespaces)
\r
462 foreach (XmlAttribute attr in el.Attributes) {
\r
463 if (attr.NamespaceURI == XmlConstants.XmlnsNS)
\r
465 if (ignoredNamespaces != null && ignoredNamespaces.Contains (attr.NamespaceURI))
\r
467 return ElementMappingType.Complex;
\r
469 foreach (XmlNode n in el.ChildNodes)
\r
470 if (n.NodeType == XmlNodeType.Element)
\r
471 return ElementMappingType.Complex;
\r
473 for (XmlNode n = el.NextSibling; n != null; n = n.NextSibling)
\r
474 if (n.NodeType == XmlNodeType.Element && n.LocalName == el.LocalName)
\r
475 return GetElementMappingType (
\r
478 == ElementMappingType.Complex ?
\r
479 ElementMappingType.Complex :
\r
480 ElementMappingType.Repeated;
\r
482 return ElementMappingType.Simple;
\r
485 private bool IsDocumentElementTable ()
\r
487 return IsDocumentElementTable (
\r
488 document.DocumentElement,
\r
489 ignoredNamespaces);
\r
492 internal static bool IsDocumentElementTable (XmlElement top,
\r
493 ArrayList ignoredNamespaces)
\r
495 foreach (XmlAttribute attr in top.Attributes) {
\r
496 if (attr.NamespaceURI == XmlConstants.XmlnsNS)
\r
498 if (ignoredNamespaces != null &&
\r
499 ignoredNamespaces.Contains (attr.NamespaceURI))
\r
501 // document element has attributes other than xmlns
\r
504 foreach (XmlNode n in top.ChildNodes) {
\r
505 XmlElement el = n as XmlElement;
\r
508 if (GetElementMappingType (el, ignoredNamespaces)
\r
509 == ElementMappingType.Simple)
\r
515 // Returns if it "might" be a column element (this method is
\r
516 // called per child element, thus it might still consist of
\r
517 // table, since it might be repeated).
\r
518 private bool IsPossibleColumnElement (XmlElement el)
\r
520 foreach (XmlAttribute attr in el.Attributes) {
\r
521 if (attr.NamespaceURI == XmlConstants.XmlnsNS)
\r
525 foreach (XmlNode n in el.ChildNodes)
\r
526 if (n.NodeType == XmlNodeType.Element)
\r
534 #if TEST_STANDALONE_INFERENCE
\r
535 internal class Driver
\r
537 private static void DumpDataTable (DataTable dt)
\r
539 Console.WriteLine ("<Table>");
\r
540 Console.WriteLine (dt.TableName);
\r
541 Console.WriteLine ("ChildRelationCount: " + dt.ChildRelations.Count);
\r
542 Console.WriteLine ("ConstraintsCount: " + dt.Constraints.Count);
\r
543 Console.WriteLine ("ParentRelationCount: " + dt.ParentRelations.Count);
\r
544 Console.WriteLine ("Prefix: " + dt.Prefix);
\r
545 Console.WriteLine ("Namespace: " + dt.Namespace);
\r
546 Console.WriteLine ("Site: " + dt.Site);
\r
547 Console.WriteLine ("RowCount: " + dt.Rows.Count);
\r
548 Console.WriteLine ("<Columns count='" + dt.Columns.Count + "'>");
\r
549 foreach (DataColumn col in dt.Columns)
\r
550 DumpDataColumn (col);
\r
551 Console.WriteLine ("</Columns>");
\r
552 Console.WriteLine ("</Table>");
\r
555 private static void DumpDataRelation (DataRelation rel)
\r
557 Console.WriteLine ("<Relation>");
\r
558 Console.WriteLine (rel.RelationName);
\r
559 Console.WriteLine (rel.Nested);
\r
560 Console.Write (" <ParentColumns>");
\r
561 foreach (DataColumn col in rel.ParentColumns)
\r
562 Console.Write (col.ColumnName + " ");
\r
563 Console.WriteLine ("</ParentColumns>");
\r
564 Console.Write (" <ChildColumns>");
\r
565 foreach (DataColumn col in rel.ChildColumns)
\r
566 Console.Write (col.ColumnName + " ");
\r
567 Console.WriteLine ("</ChildColumns>");
\r
568 if (rel.ParentKeyConstraint != null) {
\r
569 Console.WriteLine (" <ParentKeyConstraint>");
\r
570 DumpUniqueConstraint (rel.ParentKeyConstraint);
\r
571 Console.WriteLine (" </ParentKeyConstraint>");
\r
573 if (rel.ChildKeyConstraint != null) {
\r
574 Console.WriteLine (" <ChildKeyConstraint>");
\r
575 DumpForeignKeyConstraint (rel.ChildKeyConstraint);
\r
576 Console.WriteLine (" </ChildKeyConstraint>");
\r
578 Console.WriteLine ("</Relation>");
\r
581 public static void DumpUniqueConstraint (UniqueConstraint uc)
\r
583 Console.WriteLine ("Name " + uc.ConstraintName);
\r
584 Console.WriteLine ("PK? " + uc.IsPrimaryKey);
\r
585 Console.Write (" <Columns>");
\r
586 foreach (DataColumn col in uc.Columns)
\r
587 Console.Write (col.ColumnName + " ");
\r
588 Console.WriteLine ("</Columns>");
\r
591 public static void DumpForeignKeyConstraint (ForeignKeyConstraint fk)
\r
593 Console.WriteLine ("Name " + fk.ConstraintName);
\r
594 Console.WriteLine (" <Rules>" + fk.AcceptRejectRule + ", " +
\r
595 fk.DeleteRule + ", " + fk.UpdateRule + "</Rules>");
\r
596 Console.Write (" <Columns>");
\r
597 foreach (DataColumn col in fk.Columns)
\r
598 Console.Write (col.ColumnName + " ");
\r
599 Console.WriteLine ("</Columns>");
\r
600 Console.Write (" <RelatedColumns>");
\r
601 foreach (DataColumn col in fk.RelatedColumns)
\r
602 Console.Write (col.ColumnName + " ");
\r
603 Console.WriteLine ("</RelatedColumns>");
\r
606 private static void DumpDataSet (DataSet ds)
\r
608 Console.WriteLine ("-----------------------");
\r
609 Console.WriteLine ("name: " + ds.DataSetName);
\r
610 Console.WriteLine ("ns: " + ds.Namespace);
\r
611 Console.WriteLine ("prefix: " + ds.Prefix);
\r
612 Console.WriteLine ("extprop: " + ds.ExtendedProperties.Count);
\r
613 Console.WriteLine ("<Tables count='" + ds.Tables.Count + "'>");
\r
614 foreach (DataTable dt in ds.Tables)
\r
615 DumpDataTable (dt);
\r
616 Console.WriteLine ("</Tables>");
\r
617 Console.WriteLine ("<Relations count='" + ds.Relations.Count + "'>");
\r
618 foreach (DataRelation rel in ds.Relations)
\r
619 DumpDataRelation (rel);
\r
620 Console.WriteLine ("</Relations>");
\r
623 public static void DumpDataColumn (DataColumn col)
\r
625 Console.WriteLine ("<Column>");
\r
626 Console.WriteLine (" ColumnName: " + col.ColumnName);
\r
627 Console.WriteLine (" AllowDBNull? " + col.AllowDBNull);
\r
628 Console.WriteLine (" AutoIncrement? " + col.AutoIncrement);
\r
629 Console.WriteLine (" Seed: " + col.AutoIncrementSeed);
\r
630 Console.WriteLine (" Step: " + col.AutoIncrementStep);
\r
631 Console.WriteLine (" Caption " + col.Caption);
\r
632 Console.WriteLine (" Mapping: " + col.ColumnMapping);
\r
633 Console.WriteLine (" Type: " + col.DataType);
\r
634 Console.WriteLine (" DefaultValue: " + (col.DefaultValue == DBNull.Value ? "(DBNull)" : col.DefaultValue));
\r
635 Console.WriteLine (" Expression: " + (col.Expression == "" ? "(empty)" : col.Expression));
\r
636 Console.WriteLine (" MaxLength: " + col.MaxLength);
\r
637 Console.WriteLine (" Namespace: " + (col.Namespace == null ? "(null)" : col.Namespace));
\r
638 Console.WriteLine (" Ordinal: " + col.Ordinal);
\r
639 Console.WriteLine (" Prefix: " + (col.Prefix == null ? "(null)" : col.Prefix));
\r
640 Console.WriteLine (" ReadOnly: " + col.ReadOnly);
\r
641 Console.WriteLine (" Unique: " + col.Unique);
\r
642 Console.WriteLine ("</Column>");
\r
645 public static void Main (string [] args)
\r
647 if (args.Length < 1) {
\r
648 Console.WriteLine ("reader.exe xmlfilename");
\r
652 XmlSerializer ser = new XmlSerializer (typeof (DataSet));
\r
654 DataSet ds = new DataSet ();
\r
655 XmlTextReader xtr = new XmlTextReader (args [0]);
\r
656 ds.ReadXml (xtr, XmlReadMode.Auto);
\r
658 TextWriter sw = new StringWriter ();
\r
659 ser.Serialize (sw, ds);
\r
660 using (TextWriter w = new StreamWriter (Path.ChangeExtension (args [0], "ms.txt"), false, Encoding.ASCII)) {
\r
661 w.WriteLine (sw.ToString ());
\r
664 ds = new DataSet ();
\r
665 xtr = new XmlTextReader (args [0]);
\r
666 XmlDocument doc = new XmlDocument ();
\r
668 XmlDataInferenceLoader.Infer (ds, doc, XmlReadMode.Auto, null);
\r
670 sw = new StringWriter ();
\r
672 ser.Serialize (sw, ds);
\r
673 using (TextWriter w = new StreamWriter (Path.ChangeExtension (args [0], "my.txt"), false, Encoding.ASCII)) {
\r
674 w.WriteLine (sw.ToString ());
\r
677 } catch (Exception ex) {
\r
678 Console.WriteLine (ex);
\r
688 // This class is used to implement DataSet's ReadXml() and
\r
689 // InferXmlSchema() methods. That is, 1) to infer dataset schema
\r
690 // structure and 2) to read data rows.
\r
692 // It is instantiated from DataSet, XmlReader and XmlReadMode.
\r
695 // ** General Design
\r
699 // Data rows are not read when XmlReadMode is ReadSchema or InferSchema
\r
700 // (well, actually ReadSchema should not be passed).
\r
702 // Schema inference is done only when XmlReadMode is Auto or InferSchema.
\r
704 // *** Information Set
\r
706 // Only elements, "attributes", and "text" are considered. Here "text"
\r
707 // includes text node and CDATA section, but does not include whitespace
\r
708 // and even significant whitespace (as MS does). Also, here "attributes"
\r
709 // does not include "namespace" nodes.
\r
712 // ** Inference Design
\r
716 // There are four type of mapping in MappingType enumeration:
\r
718 // Element : Mapped to a simple type element.
\r
719 // Attribute : Mapped to an attribute
\r
720 // SimpleContent : Mapped to the text content of complex type element.
\r
721 // (i.e. xs:element/xs:complexType/xs:simpleContent)
\r
722 // Hidden : Used for both key and reference, for auto-generated columns.
\r
724 // *** Mapping strategy
\r
726 // Attributes are always (except for namespace nodes) infered as
\r
727 // DataColumn (of MappingType.Attribute).
\r
729 // When an element has attributes, it always becomes a DataTable.
\r
730 // Otherwise if there is a child element, it becomes DataTable as well.
\r
732 // When there is a text content, 1) if the container element has
\r
733 // attribute(s), the text content becomes a SimpleContent DataColumn
\r
734 // in the container "table" element (yes, it becomes a DataTable).
\r
735 // 2) if the container has no attribute, the element becomes DataColumn.
\r
737 // If there are both text content and child element(s), the text content
\r
738 // is ignored (thus, it always become DataTable).
\r
740 // *** Mapping conflicts
\r
742 // If there has been already a different MappingType of DataColumn,
\r
743 // it is DataException. For example, it is an error if there are an
\r
744 // attribute and an element child those names are the same.
\r
746 // Name remapping will never be done. It introduces complicated rules.
\r
748 // *** Upgrading from DataColumn to DataTable
\r
750 // If there has been the same Element type of mapping (that is, when
\r
751 // the same-name child elements appeared in the element), then the
\r
752 // child elements become a DataTable (so here must be a conversion
\r
753 // from DataColumn/value_DataRow to DataTable/value_DataRow in the new
\r
754 // table and reference_to_new_table in the old DataColumn.
\r
757 // ** Implementation
\r
759 // *** XmlReader based implementation
\r
761 // This class uses XmlReader to avoid having the entire XML document
\r
762 // object. The basic stategy is
\r
764 // 1) handle attributes at startElement
\r
765 // 2) store text content (if it "stores" in data rows) while
\r
767 // 3) dispose of elements at endElement
\r
768 // 4) Empty element without attributes is equal to a column
\r
771 // In XmlSchemaMapper.cs (by Ville Palo) there is an enumeration type
\r
772 // ElementType (undefined, table, column). This concept is nice to reuse.
\r
774 // *** Top level inference
\r
776 // The process starts with ReadElement() for the top-level element.
\r
777 // (considering Fragment mode, it might not be the document element.
\r
778 // However, no inference is done in that mode.)
\r
780 // If the top level element was not a DataTable and there is
\r
781 // no more content, the element is regarded as DataSet with no tables.
\r
783 // *** First child of the DataSet element
\r
785 // There are some special cases.
\r
787 // *** ReadElement()
\r
789 // The main inference process is ReadElement(). This method consumes
\r
790 // (should consume) exactly one element and interpret it as either
\r
791 // DataTable or DataColumn.
\r