3 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 #if STANDALONE_DRIVER_TEST
36 public static void Main (string [] args
)
38 if (args
.Length
== 0) {
39 Console
.WriteLine ("usage: mono xmldatareader.exe filename");
43 Console
.WriteLine ("Target file: " + args
[0]);
45 DataSet ds
= new DataSet ();
46 // ds.InferXmlSchema (args [0], null);
49 ds
.ReadXml (args
[0]);
50 } catch (Exception ex
) {
51 Console
.WriteLine ("ReadXml() borked: " + ex
.Message
);
54 Console
.WriteLine ("---- DataSet ----------------");
55 StringWriter sw
= new StringWriter ();
56 PrintDataSet (ds
, sw
);
57 PrintDataSet (ds
, Console
.Out
);
60 ds
.InferXmlSchema (args
[0], null);
61 XmlDataReader
.ReadXml (ds
, new XmlTextReader (args
[0]));
62 Console
.WriteLine ("---- XmlDataReader ----------------");
63 StringWriter sw2
= new StringWriter ();
64 PrintDataSet (ds
, sw2
);
66 if (sw
.ToString () == sw2
.ToString ())
67 Console
.WriteLine ("Successful.");
69 Console
.WriteLine ("Different *************************************************\n" + sw2
);
72 private static void PrintDataSet (DataSet ds
, TextWriter tw
)
74 tw
.WriteLine ("DS::" + ds
.DataSetName
+ ", " + ds
.Tables
.Count
+ ", " + ds
.Relations
.Count
);
75 foreach (DataTable dt
in ds
.Tables
)
76 tw
.WriteLine ("DT:" + dt
.TableName
+ ", " + dt
.Columns
.Count
+ ", " + dt
.Rows
.Count
);
84 internal class XmlDataReader
86 const string xmlnsNS
= "http://www.w3.org/2000/xmlns/";
88 public static void ReadXml (
89 DataSet dataset
, XmlReader reader
, XmlReadMode mode
)
91 new XmlDataReader (dataset
, reader
, mode
).Process ();
98 public XmlDataReader (DataSet ds
, XmlReader xr
, XmlReadMode m
)
105 private void Process ()
107 // set EnforceConstraint to false during load time.
108 bool savedEnforceConstraints
=
109 dataset
.EnforceConstraints
;
110 dataset
.EnforceConstraints
= false;
112 reader
.MoveToContent ();
114 if (mode
== XmlReadMode
.Fragment
) {
116 if (XmlConvert
.DecodeName (reader
.LocalName
) == dataset
.DataSetName
&& reader
.NamespaceURI
== dataset
.Namespace
)
117 ReadTopLevelElement ();
120 } while (!reader
.EOF
);
122 // Top level element can be ignored, being regarded
123 // just as a wrapper (even it is not dataset element).
124 DataTable tab
= dataset
.Tables
[XmlConvert
.DecodeName (reader
.LocalName
)];
125 if (tab
!= null && tab
.Namespace
== reader
.NamespaceURI
)
126 ReadDataSetContent ();
128 ReadTopLevelElement ();
129 reader
.MoveToContent ();
132 dataset
.EnforceConstraints
= savedEnforceConstraints
;
135 private void ReadTopLevelElement ()
137 int depth
= reader
.Depth
;
139 reader
.MoveToContent ();
141 ReadDataSetContent ();
142 } while (reader
.Depth
> depth
&& !reader
.EOF
);
144 if (reader
.IsEmptyElement
)
146 if (reader
.NodeType
== XmlNodeType
.EndElement
)
147 reader
.ReadEndElement ();
148 reader
.MoveToContent ();
151 private void ReadDataSetContent ()
153 DataTable table
= dataset
.Tables
[XmlConvert
.DecodeName (reader
.LocalName
)];
154 if (table
== null || table
.Namespace
!= reader
.NamespaceURI
) {
156 reader
.MoveToContent ();
157 return; // skip if there is no matching table
160 // skip if namespace does not match.
161 // TODO: This part is suspicious for MS compatibility
163 if (table
.Namespace
!= reader
.NamespaceURI
) {
165 reader
.MoveToContent ();
166 return; // skip if there is no matching table
169 DataRow row
= table
.NewRow ();
171 table
.Rows
.Add (row
);
174 private void ReadElement (DataRow row
)
176 // Consume attributes
177 if (reader
.MoveToFirstAttribute ()) {
179 if (reader
.NamespaceURI
== xmlnsNS
)
181 ReadElementAttribute (row
);
182 } while (reader
.MoveToNextAttribute ());
183 reader
.MoveToElement ();
186 // If not empty element, read content.
187 if (reader
.IsEmptyElement
) {
189 reader
.MoveToContent ();
191 int depth
= reader
.Depth
;
193 reader
.MoveToContent ();
195 ReadElementContent (row
);
196 } while (reader
.Depth
> depth
&& !reader
.EOF
);
197 if (reader
.IsEmptyElement
)
199 if (reader
.NodeType
== XmlNodeType
.EndElement
)
200 reader
.ReadEndElement ();
201 reader
.MoveToContent ();
205 private void ReadElementAttribute (DataRow row
)
207 DataColumn col
= row
.Table
.Columns
[XmlConvert
.DecodeName (reader
.LocalName
)];
208 if (col
== null || col
.Namespace
!= reader
.NamespaceURI
)
210 row
[col
] = StringToObject (col
.DataType
, reader
.Value
);
213 private void ReadElementContent (DataRow row
)
215 switch (reader
.NodeType
) {
217 case XmlNodeType
.EndElement
:
218 // This happens when the content was only whitespace (and skipped by MoveToContent()).
221 case XmlNodeType
.Element
:
222 ReadElementElement (row
);
225 case XmlNodeType
.Text
:
226 case XmlNodeType
.CDATA
:
227 case XmlNodeType
.SignificantWhitespace
:
228 DataColumn simple
= null;
229 DataColumnCollection cols
= row
.Table
.Columns
;
230 for (int i
= 0; i
< cols
.Count
; i
++) {
231 DataColumn col
= cols
[i
];
232 if (col
.ColumnMapping
==
233 MappingType
.SimpleContent
) {
238 string s
= reader
.ReadString ();
239 reader
.MoveToContent ();
240 #if SILLY_MS_COMPATIBLE
241 // As to MS, "test string" and "test <!-- comment -->string" are different :P
242 if (simple
!= null && row
.IsNull (simple
))
243 row
[simple
] = StringToObject (simple
.DataType
, s
);
245 // But it does not mean we support "123<!-- comment -->456". just allowed for string
250 case XmlNodeType
.Whitespace
:
251 reader
.ReadString ();
256 private void ReadElementElement (DataRow row
)
258 // This child element (for row) might be either simple
259 // content element, or child element
261 // MS.NET crashes here... but it seems just a bug.
262 // DataColumn col = row.Table.Columns [XmlConvert.DecodeName (reader.LocalName)];
263 DataColumn col
= null;
264 DataColumnCollection cols
= row
.Table
.Columns
;
265 for (int i
= 0; i
< cols
.Count
; i
++) {
266 if (cols
[i
].ColumnName
== XmlConvert
.DecodeName (reader
.LocalName
) && cols
[i
].Namespace
== reader
.NamespaceURI
) {
273 // if col exists, then it should be MappingType.Element
275 && col
.ColumnMapping
== MappingType
.Element
) {
277 // TODO: This part is suspicious for
278 // MS compatibility (test required)
279 if (col
.Namespace
!= reader
.NamespaceURI
) {
284 bool wasEmpty
= reader
.IsEmptyElement
;
285 int depth
= reader
.Depth
;
286 row
[col
] = StringToObject (col
.DataType
, reader
.ReadElementString ());
287 if (!wasEmpty
&& reader
.Depth
> depth
) {
288 // This means, instance does not match with
289 // the schema (because the instance element
290 // contains complex content, while specified as
291 // simple), so just skip to the end of the
293 while (reader
.Depth
> depth
)
297 reader
.MoveToContent ();
299 } else if (col
!= null) {
300 // Mismatch column type. Just skip
302 reader
.MoveToContent ();
306 // Otherwise, it might be child table element
307 DataRelationCollection rels
= row
.Table
.ChildRelations
;
308 for (int i
= 0; i
< rels
.Count
; i
++) {
309 DataRelation rel
= rels
[i
];
312 DataTable ct
= rel
.ChildTable
;
313 if (ct
.TableName
!= XmlConvert
.DecodeName (reader
.LocalName
) || ct
.Namespace
!= reader
.NamespaceURI
)
316 DataRow childRow
= rel
.ChildTable
.NewRow ();
317 ReadElement (childRow
);
319 for (int c
= 0; c
< rel
.ChildColumns
.Length
; c
++) {
320 childRow
[rel
.ChildColumns
[c
]]
321 = row
[rel
.ParentColumns
[c
]];
323 rel
.ChildTable
.Rows
.Add (childRow
);
327 // Matched neither of the above: just skip
329 reader
.MoveToContent ();
332 internal static object StringToObject (Type type
, string value)
334 if (type
== null) return value;
336 switch (Type
.GetTypeCode (type
)) {
337 case TypeCode
.Boolean
: return XmlConvert
.ToBoolean (value);
338 case TypeCode
.Byte
: return XmlConvert
.ToByte (value);
339 case TypeCode
.Char
: return (char)XmlConvert
.ToInt32 (value);
340 case TypeCode
.DateTime
: return XmlConvert
.ToDateTime (value);
341 case TypeCode
.Decimal
: return XmlConvert
.ToDecimal (value);
342 case TypeCode
.Double
: return XmlConvert
.ToDouble (value);
343 case TypeCode
.Int16
: return XmlConvert
.ToInt16 (value);
344 case TypeCode
.Int32
: return XmlConvert
.ToInt32 (value);
345 case TypeCode
.Int64
: return XmlConvert
.ToInt64 (value);
346 case TypeCode
.SByte
: return XmlConvert
.ToSByte (value);
347 case TypeCode
.Single
: return XmlConvert
.ToSingle (value);
348 case TypeCode
.UInt16
: return XmlConvert
.ToUInt16 (value);
349 case TypeCode
.UInt32
: return XmlConvert
.ToUInt32 (value);
350 case TypeCode
.UInt64
: return XmlConvert
.ToUInt64 (value);
353 if (type
== typeof (TimeSpan
)) return XmlConvert
.ToTimeSpan (value);
354 if (type
== typeof (Guid
)) return XmlConvert
.ToGuid (value);
355 if (type
== typeof (byte[])) return Convert
.FromBase64String (value);
357 return Convert
.ChangeType (value, type
);