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
29 using System.Xml.Serialization;
\r
32 namespace System.Data
\r
34 #if STANDALONE_DRIVER_TEST
\r
37 public static void Main (string [] args)
\r
39 if (args.Length == 0) {
\r
40 Console.WriteLine ("usage: mono xmldatareader.exe filename");
\r
44 Console.WriteLine ("Target file: " + args [0]);
\r
46 DataSet ds = new DataSet ();
\r
47 // ds.InferXmlSchema (args [0], null);
\r
50 ds.ReadXml (args [0]);
\r
51 } catch (Exception ex) {
\r
52 Console.WriteLine ("ReadXml() borked: " + ex.Message);
\r
55 Console.WriteLine ("---- DataSet ----------------");
\r
56 StringWriter sw = new StringWriter ();
\r
57 PrintDataSet (ds, sw);
\r
58 PrintDataSet (ds, Console.Out);
\r
60 ds = new DataSet ();
\r
61 ds.InferXmlSchema (args [0], null);
\r
62 XmlDataReader.ReadXml (ds, new XmlTextReader (args [0]));
\r
63 Console.WriteLine ("---- XmlDataReader ----------------");
\r
64 StringWriter sw2 = new StringWriter ();
\r
65 PrintDataSet (ds, sw2);
\r
67 if (sw.ToString () == sw2.ToString ())
\r
68 Console.WriteLine ("Successful.");
\r
70 Console.WriteLine ("Different *************************************************\n" + sw2);
\r
73 private static void PrintDataSet (DataSet ds, TextWriter tw)
\r
75 tw.WriteLine ("DS::" + ds.DataSetName + ", " + ds.Tables.Count + ", " + ds.Relations.Count);
\r
76 foreach (DataTable dt in ds.Tables)
\r
77 tw.WriteLine ("DT:" + dt.TableName + ", " + dt.Columns.Count + ", " + dt.Rows.Count);
\r
85 internal class XmlDataReader
\r
87 const string xmlnsNS = "http://www.w3.org/2000/xmlns/";
\r
89 public static void ReadXml (
\r
90 DataSet dataset, XmlReader reader, XmlReadMode mode)
\r
92 new XmlDataReader (dataset, reader, mode).Process ();
\r
99 public XmlDataReader (DataSet ds, XmlReader xr, XmlReadMode m)
\r
106 private void Process ()
\r
108 bool savedEnforceConstraints =
\r
109 dataset.EnforceConstraints;
\r
111 dataset.EnforceConstraints = false;
\r
112 reader.MoveToContent ();
\r
114 if (mode == XmlReadMode.Fragment) {
\r
115 while (reader.NodeType == XmlNodeType.Element && !reader.EOF) {
\r
116 ReadTopLevelElement ();
\r
120 ReadTopLevelElement ();
\r
122 dataset.EnforceConstraints =
\r
123 savedEnforceConstraints;
\r
127 private bool IsTopLevelDataSet ()
\r
129 string local = XmlHelper.Decode (reader.LocalName);
\r
131 // No need to check DataSetName. In fact, it is ignored.
\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 (XmlHelper.Decode (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 [XmlHelper.Decode (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 || reader.NamespaceURI == XmlConstants.XmlNS
\r
202 ReadElementAttribute (row);
\r
203 } while (reader.MoveToNextAttribute ());
\r
204 reader.MoveToElement ();
\r
207 // If not empty element, read content.
\r
208 if (reader.IsEmptyElement) {
\r
210 reader.MoveToContent ();
\r
212 int depth = reader.Depth;
\r
214 reader.MoveToContent ();
\r
216 ReadElementContent (row);
\r
217 } while (reader.Depth > depth && !reader.EOF);
\r
218 if (reader.IsEmptyElement)
\r
220 if (reader.NodeType == XmlNodeType.EndElement)
\r
221 reader.ReadEndElement ();
\r
222 reader.MoveToContent ();
\r
226 private void ReadElementAttribute (DataRow row)
\r
228 DataColumn col = row.Table.Columns [XmlHelper.Decode (reader.LocalName)];
\r
229 if (col == null || col.Namespace != reader.NamespaceURI)
\r
231 row [col] = StringToObject (col.DataType, reader.Value);
\r
234 private void ReadElementContent (DataRow row)
\r
236 switch (reader.NodeType) {
\r
238 case XmlNodeType.EndElement:
\r
239 // This happens when the content was only whitespace (and skipped by MoveToContent()).
\r
242 case XmlNodeType.Element:
\r
243 ReadElementElement (row);
\r
246 case XmlNodeType.Text:
\r
247 case XmlNodeType.CDATA:
\r
248 case XmlNodeType.SignificantWhitespace:
\r
249 DataColumn simple = null;
\r
250 DataColumnCollection cols = row.Table.Columns;
\r
251 for (int i = 0; i < cols.Count; i++) {
\r
252 DataColumn col = cols [i];
\r
253 if (col.ColumnMapping ==
\r
254 MappingType.SimpleContent) {
\r
259 string s = reader.ReadString ();
\r
260 reader.MoveToContent ();
\r
261 #if SILLY_MS_COMPATIBLE
\r
262 // As to MS, "test string" and "test <!-- comment -->string" are different :P
\r
263 if (simple != null && row.IsNull (simple))
\r
264 row [simple] = StringToObject (simple.DataType, s);
\r
266 // But it does not mean we support "123<!-- comment -->456". just allowed for string
\r
267 if (simple != null)
\r
271 case XmlNodeType.Whitespace:
\r
272 reader.ReadString ();
\r
277 private void ReadElementElement (DataRow row)
\r
279 // This child element (for row) might be either simple
\r
280 // content element, or child element
\r
282 // MS.NET crashes here... but it seems just a bug.
\r
283 DataColumn col = row.Table.Columns [XmlHelper.Decode (reader.LocalName)];
\r
284 if (col == null || col.Namespace != reader.NamespaceURI)
\r
287 // if col exists, then it should be MappingType.Element
\r
289 && col.ColumnMapping == MappingType.Element) {
\r
291 // TODO: This part is suspicious for
\r
292 // MS compatibility (test required)
\r
293 if (col.Namespace != reader.NamespaceURI) {
\r
298 bool wasEmpty = reader.IsEmptyElement;
\r
299 int depth = reader.Depth;
\r
301 if (typeof (IXmlSerializable).IsAssignableFrom (col.DataType)) {
\r
304 // NOTE: ReadElementString works fine with proper XML with CDATA etc,
\r
305 // however doesn't behave well with XMLs like the one in
\r
306 // https://bugzilla.novell.com/show_bug.cgi?id=377146 which is
\r
307 // apparently supported by MS.NET - to maintain compatibility,
\r
308 // If the obj implements IXmlSerializable, let obj's ReadXml do the reading
\r
309 IXmlSerializable obj = (IXmlSerializable) Activator.CreateInstance (col.DataType, new object [0]);
\r
310 if (!reader.IsEmptyElement) {
\r
311 obj.ReadXml (reader);
\r
312 reader.ReadEndElement ();
\r
317 } catch (XmlException) {
\r
319 // XML is not in accordance to expected standards, try reading the content as an xml doc
\r
320 row [col] = reader.ReadInnerXml ();
\r
322 } catch (InvalidOperationException) {
\r
324 row [col] = reader.ReadInnerXml ();
\r
328 row [col] = StringToObject (col.DataType, reader.ReadElementString ());
\r
331 if (!wasEmpty && reader.Depth > depth) {
\r
332 // This means, instance does not match with
\r
333 // the schema (because the instance element
\r
334 // contains complex content, while specified as
\r
335 // simple), so just skip to the end of the
\r
337 while (reader.Depth > depth)
\r
341 reader.MoveToContent ();
\r
343 } else if (col != null) {
\r
344 // Mismatch column type. Just skip
\r
346 reader.MoveToContent ();
\r
350 // Otherwise, it might be child table element
\r
351 DataRelationCollection rels = row.Table.ChildRelations;
\r
352 for (int i = 0; i < rels.Count; i++) {
\r
353 DataRelation rel = rels [i];
\r
356 DataTable ct = rel.ChildTable;
\r
357 if (ct.TableName != XmlHelper.Decode (reader.LocalName) || ct.Namespace != reader.NamespaceURI)
\r
360 DataRow childRow = rel.ChildTable.NewRow ();
\r
361 ReadElement (childRow);
\r
363 for (int c = 0; c < rel.ChildColumns.Length; c++) {
\r
364 childRow [rel.ChildColumns [c]]
\r
365 = row [rel.ParentColumns [c]];
\r
367 rel.ChildTable.Rows.Add (childRow);
\r
371 // Matched neither of the above: just skip
\r
373 reader.MoveToContent ();
\r
376 internal static object StringToObject (Type type, string value)
\r
378 if (type == null) return value;
\r
380 switch (Type.GetTypeCode (type)) {
\r
381 case TypeCode.Boolean: return XmlConvert.ToBoolean (value);
\r
382 case TypeCode.Byte: return XmlConvert.ToByte (value);
\r
383 case TypeCode.Char: return (char)XmlConvert.ToInt32 (value);
\r
385 case TypeCode.DateTime: return XmlConvert.ToDateTime (value, XmlDateTimeSerializationMode.Unspecified);
\r
387 case TypeCode.DateTime: return XmlConvert.ToDateTime (value);
\r
389 case TypeCode.Decimal: return XmlConvert.ToDecimal (value);
\r
390 case TypeCode.Double: return XmlConvert.ToDouble (value);
\r
391 case TypeCode.Int16: return XmlConvert.ToInt16 (value);
\r
392 case TypeCode.Int32: return XmlConvert.ToInt32 (value);
\r
393 case TypeCode.Int64: return XmlConvert.ToInt64 (value);
\r
394 case TypeCode.SByte: return XmlConvert.ToSByte (value);
\r
395 case TypeCode.Single: return XmlConvert.ToSingle (value);
\r
396 case TypeCode.UInt16: return XmlConvert.ToUInt16 (value);
\r
397 case TypeCode.UInt32: return XmlConvert.ToUInt32 (value);
\r
398 case TypeCode.UInt64: return XmlConvert.ToUInt64 (value);
\r
401 if (type == typeof (TimeSpan)) return XmlConvert.ToTimeSpan (value);
\r
402 if (type == typeof (Guid)) return XmlConvert.ToGuid (value);
\r
403 if (type == typeof (byte[])) return Convert.FromBase64String (value);
\r
404 if (type == typeof (System.Type)) return System.Type.GetType (value);
\r
406 return Convert.ChangeType (value, type);
\r