3 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
\r
5 // Permission is hereby granted, free of charge, to any person obtaining
\r
6 // a copy of this software and associated documentation files (the
\r
7 // "Software"), to deal in the Software without restriction, including
\r
8 // without limitation the rights to use, copy, modify, merge, publish,
\r
9 // distribute, sublicense, and/or sell copies of the Software, and to
\r
10 // permit persons to whom the Software is furnished to do so, subject to
\r
11 // the following conditions:
\r
13 // The above copyright notice and this permission notice shall be
\r
14 // included in all copies or substantial portions of the Software.
\r
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
31 namespace System.Data
\r
33 #if STANDALONE_DRIVER_TEST
\r
36 public static void Main (string [] args)
\r
38 if (args.Length == 0) {
\r
39 Console.WriteLine ("usage: mono xmldatareader.exe filename");
\r
43 Console.WriteLine ("Target file: " + args [0]);
\r
45 DataSet ds = new DataSet ();
\r
46 // ds.InferXmlSchema (args [0], null);
\r
49 ds.ReadXml (args [0]);
\r
50 } catch (Exception ex) {
\r
51 Console.WriteLine ("ReadXml() borked: " + ex.Message);
\r
54 Console.WriteLine ("---- DataSet ----------------");
\r
55 StringWriter sw = new StringWriter ();
\r
56 PrintDataSet (ds, sw);
\r
57 PrintDataSet (ds, Console.Out);
\r
59 ds = new DataSet ();
\r
60 ds.InferXmlSchema (args [0], null);
\r
61 XmlDataReader.ReadXml (ds, new XmlTextReader (args [0]));
\r
62 Console.WriteLine ("---- XmlDataReader ----------------");
\r
63 StringWriter sw2 = new StringWriter ();
\r
64 PrintDataSet (ds, sw2);
\r
66 if (sw.ToString () == sw2.ToString ())
\r
67 Console.WriteLine ("Successful.");
\r
69 Console.WriteLine ("Different *************************************************\n" + sw2);
\r
72 private static void PrintDataSet (DataSet ds, TextWriter tw)
\r
74 tw.WriteLine ("DS::" + ds.DataSetName + ", " + ds.Tables.Count + ", " + ds.Relations.Count);
\r
75 foreach (DataTable dt in ds.Tables)
\r
76 tw.WriteLine ("DT:" + dt.TableName + ", " + dt.Columns.Count + ", " + dt.Rows.Count);
\r
84 internal class XmlDataReader
\r
86 const string xmlnsNS = "http://www.w3.org/2000/xmlns/";
\r
88 public static void ReadXml (
\r
89 DataSet dataset, XmlReader reader, XmlReadMode mode)
\r
91 new XmlDataReader (dataset, reader, mode).Process ();
\r
98 public XmlDataReader (DataSet ds, XmlReader xr, XmlReadMode m)
\r
105 private void Process ()
\r
107 bool savedEnforceConstraints =
\r
108 dataset.EnforceConstraints;
\r
110 dataset.EnforceConstraints = false;
\r
111 reader.MoveToContent ();
\r
113 if (mode == XmlReadMode.Fragment) {
\r
114 while (reader.NodeType == XmlNodeType.Element && !reader.EOF) {
\r
115 ReadTopLevelElement ();
\r
119 ReadTopLevelElement ();
\r
121 dataset.EnforceConstraints =
\r
122 savedEnforceConstraints;
\r
126 private bool IsTopLevelDataSet ()
\r
128 string local = XmlConvert.DecodeName (reader.LocalName);
\r
130 // No need to check DataSetName. In fact, it is ignored.
\r
132 bool ambiguous = false;
\r
133 DataTable dt = dataset.Tables [local];
\r
137 XmlDocument doc = new XmlDocument ();
\r
138 XmlElement el = (XmlElement) doc.ReadNode (reader);
\r
139 doc.AppendChild (el);
\r
140 reader = new XmlNodeReader (el);
\r
141 reader.MoveToContent ();
\r
143 return !XmlDataInferenceLoader.IsDocumentElementTable (
\r
147 private void ReadTopLevelElement ()
\r
149 if (mode == XmlReadMode.Fragment &&
\r
150 (XmlConvert.DecodeName (reader.LocalName) !=
\r
151 dataset.DataSetName ||
\r
152 reader.NamespaceURI != dataset.Namespace))
\r
154 else if (mode == XmlReadMode.Fragment ||
\r
155 IsTopLevelDataSet ()) {
\r
156 int depth = reader.Depth;
\r
158 reader.MoveToContent ();
\r
160 ReadDataSetContent ();
\r
161 } while (reader.Depth > depth && !reader.EOF);
\r
162 if (reader.NodeType == XmlNodeType.EndElement)
\r
163 reader.ReadEndElement ();
\r
164 reader.MoveToContent ();
\r
167 ReadDataSetContent ();
\r
170 private void ReadDataSetContent ()
\r
172 DataTable table = dataset.Tables [XmlConvert.DecodeName (reader.LocalName)];
\r
173 if (table == null || table.Namespace != reader.NamespaceURI) {
\r
175 reader.MoveToContent ();
\r
176 return; // skip if there is no matching table
\r
179 // skip if namespace does not match.
\r
180 if (table.Namespace != reader.NamespaceURI) {
\r
182 reader.MoveToContent ();
\r
183 return; // skip if there is no matching table
\r
186 DataRow row = table.NewRow ();
\r
188 table.Rows.Add (row);
\r
191 private void ReadElement (DataRow row)
\r
193 // Consume attributes
\r
194 if (reader.MoveToFirstAttribute ()) {
\r
196 if (reader.NamespaceURI == XmlConstants.XmlnsNS)
\r
198 ReadElementAttribute (row);
\r
199 } while (reader.MoveToNextAttribute ());
\r
200 reader.MoveToElement ();
\r
203 // If not empty element, read content.
\r
204 if (reader.IsEmptyElement) {
\r
206 reader.MoveToContent ();
\r
208 int depth = reader.Depth;
\r
210 reader.MoveToContent ();
\r
212 ReadElementContent (row);
\r
213 } while (reader.Depth > depth && !reader.EOF);
\r
214 if (reader.IsEmptyElement)
\r
216 if (reader.NodeType == XmlNodeType.EndElement)
\r
217 reader.ReadEndElement ();
\r
218 reader.MoveToContent ();
\r
222 private void ReadElementAttribute (DataRow row)
\r
224 DataColumn col = row.Table.Columns [XmlConvert.DecodeName (reader.LocalName)];
\r
225 if (col == null || col.Namespace != reader.NamespaceURI)
\r
227 row [col] = StringToObject (col.DataType, reader.Value);
\r
230 private void ReadElementContent (DataRow row)
\r
232 switch (reader.NodeType) {
\r
234 case XmlNodeType.EndElement:
\r
235 // This happens when the content was only whitespace (and skipped by MoveToContent()).
\r
238 case XmlNodeType.Element:
\r
239 ReadElementElement (row);
\r
242 case XmlNodeType.Text:
\r
243 case XmlNodeType.CDATA:
\r
244 case XmlNodeType.SignificantWhitespace:
\r
245 DataColumn simple = null;
\r
246 DataColumnCollection cols = row.Table.Columns;
\r
247 for (int i = 0; i < cols.Count; i++) {
\r
248 DataColumn col = cols [i];
\r
249 if (col.ColumnMapping ==
\r
250 MappingType.SimpleContent) {
\r
255 string s = reader.ReadString ();
\r
256 reader.MoveToContent ();
\r
257 #if SILLY_MS_COMPATIBLE
\r
258 // As to MS, "test string" and "test <!-- comment -->string" are different :P
\r
259 if (simple != null && row.IsNull (simple))
\r
260 row [simple] = StringToObject (simple.DataType, s);
\r
262 // But it does not mean we support "123<!-- comment -->456". just allowed for string
\r
263 if (simple != null)
\r
267 case XmlNodeType.Whitespace:
\r
268 reader.ReadString ();
\r
273 private void ReadElementElement (DataRow row)
\r
275 // This child element (for row) might be either simple
\r
276 // content element, or child element
\r
278 // MS.NET crashes here... but it seems just a bug.
\r
279 // DataColumn col = row.Table.Columns [XmlConvert.DecodeName (reader.LocalName)];
\r
280 DataColumn col = null;
\r
281 DataColumnCollection cols = row.Table.Columns;
\r
282 for (int i = 0; i < cols.Count; i++) {
\r
283 if (cols [i].ColumnName == XmlConvert.DecodeName (reader.LocalName) && cols [i].Namespace == reader.NamespaceURI) {
\r
290 // if col exists, then it should be MappingType.Element
\r
292 && col.ColumnMapping == MappingType.Element) {
\r
294 // TODO: This part is suspicious for
\r
295 // MS compatibility (test required)
\r
296 if (col.Namespace != reader.NamespaceURI) {
\r
301 bool wasEmpty = reader.IsEmptyElement;
\r
302 int depth = reader.Depth;
\r
303 row [col] = StringToObject (col.DataType, reader.ReadElementString ());
\r
304 if (!wasEmpty && reader.Depth > depth) {
\r
305 // This means, instance does not match with
\r
306 // the schema (because the instance element
\r
307 // contains complex content, while specified as
\r
308 // simple), so just skip to the end of the
\r
310 while (reader.Depth > depth)
\r
314 reader.MoveToContent ();
\r
316 } else if (col != null) {
\r
317 // Mismatch column type. Just skip
\r
319 reader.MoveToContent ();
\r
323 // Otherwise, it might be child table element
\r
324 DataRelationCollection rels = row.Table.ChildRelations;
\r
325 for (int i = 0; i < rels.Count; i++) {
\r
326 DataRelation rel = rels [i];
\r
329 DataTable ct = rel.ChildTable;
\r
330 if (ct.TableName != XmlConvert.DecodeName (reader.LocalName) || ct.Namespace != reader.NamespaceURI)
\r
333 DataRow childRow = rel.ChildTable.NewRow ();
\r
334 ReadElement (childRow);
\r
336 for (int c = 0; c < rel.ChildColumns.Length; c++) {
\r
337 childRow [rel.ChildColumns [c]]
\r
338 = row [rel.ParentColumns [c]];
\r
340 rel.ChildTable.Rows.Add (childRow);
\r
344 // Matched neither of the above: just skip
\r
346 reader.MoveToContent ();
\r
349 internal static object StringToObject (Type type, string value)
\r
351 if (type == null) return value;
\r
353 switch (Type.GetTypeCode (type)) {
\r
354 case TypeCode.Boolean: return XmlConvert.ToBoolean (value);
\r
355 case TypeCode.Byte: return XmlConvert.ToByte (value);
\r
356 case TypeCode.Char: return (char)XmlConvert.ToInt32 (value);
\r
357 case TypeCode.DateTime: return XmlConvert.ToDateTime (value);
\r
358 case TypeCode.Decimal: return XmlConvert.ToDecimal (value);
\r
359 case TypeCode.Double: return XmlConvert.ToDouble (value);
\r
360 case TypeCode.Int16: return XmlConvert.ToInt16 (value);
\r
361 case TypeCode.Int32: return XmlConvert.ToInt32 (value);
\r
362 case TypeCode.Int64: return XmlConvert.ToInt64 (value);
\r
363 case TypeCode.SByte: return XmlConvert.ToSByte (value);
\r
364 case TypeCode.Single: return XmlConvert.ToSingle (value);
\r
365 case TypeCode.UInt16: return XmlConvert.ToUInt16 (value);
\r
366 case TypeCode.UInt32: return XmlConvert.ToUInt32 (value);
\r
367 case TypeCode.UInt64: return XmlConvert.ToUInt64 (value);
\r
370 if (type == typeof (TimeSpan)) return XmlConvert.ToTimeSpan (value);
\r
371 if (type == typeof (Guid)) return XmlConvert.ToGuid (value);
\r
372 if (type == typeof (byte[])) return Convert.FromBase64String (value);
\r
374 return Convert.ChangeType (value, type);
\r