2 // System.Xml.XmlConvert
5 // Dwivedi, Ajay kumar (Adwiv@Yahoo.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Alan Tam Siu Lung (Tam@SiuLung.com)
8 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
10 // (C) 2002 Ximian, Inc (http://www.ximian.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Globalization;
37 using System.Xml.Schema;
39 namespace System.Xml {
41 public class XmlConvert {
43 const string encodedColon = "_x003A_";
44 const NumberStyles floatStyle = NumberStyles.AllowCurrencySymbol |
45 NumberStyles.AllowExponent |
46 NumberStyles.AllowDecimalPoint |
47 NumberStyles.AllowLeadingSign;
49 static readonly string [] datetimeFormats = {
51 "yyyy-MM-ddTHH:mm:ss",
52 "yyyy-MM-ddTHH:mm:ss.f",
53 "yyyy-MM-ddTHH:mm:ss.ff",
54 "yyyy-MM-ddTHH:mm:ss.fff",
55 "yyyy-MM-ddTHH:mm:ss.ffff",
56 "yyyy-MM-ddTHH:mm:ss.fffff",
57 "yyyy-MM-ddTHH:mm:ss.ffffff",
58 "yyyy-MM-ddTHH:mm:ss.fffffff",
59 "yyyy-MM-ddTHH:mm:sszzz",
60 "yyyy-MM-ddTHH:mm:ss.fzzz",
61 "yyyy-MM-ddTHH:mm:ss.ffzzz",
62 "yyyy-MM-ddTHH:mm:ss.fffzzz",
63 "yyyy-MM-ddTHH:mm:ss.ffffzzz",
64 "yyyy-MM-ddTHH:mm:ss.fffffzzz",
65 "yyyy-MM-ddTHH:mm:ss.ffffffzzz",
66 "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
67 "yyyy-MM-ddTHH:mm:ssZ",
68 "yyyy-MM-ddTHH:mm:ss.fZ",
69 "yyyy-MM-ddTHH:mm:ss.ffZ",
70 "yyyy-MM-ddTHH:mm:ss.fffZ",
71 "yyyy-MM-ddTHH:mm:ss.ffffZ",
72 "yyyy-MM-ddTHH:mm:ss.fffffZ",
73 "yyyy-MM-ddTHH:mm:ss.ffffffZ",
74 "yyyy-MM-ddTHH:mm:ss.fffffffZ",
91 "HH:mm:ss.fffffffzzz",
123 static readonly string [] defaultDateTimeFormats = new string [] {
124 "yyyy-MM-ddTHH:mm:ss", // dateTime(1)
125 "yyyy-MM-ddTHH:mm:ss.FFFFFFF", // dateTime(2)
126 "yyyy-MM-dd", // date
128 "yyyy-MM", // gYearMonth
130 "--MM-dd", // gMonthDay
134 static readonly string [] roundtripDateTimeFormats;
135 static readonly string [] localDateTimeFormats;
136 static readonly string [] utcDateTimeFormats;
138 static readonly string [] unspecifiedDateTimeFormats;
143 int l = defaultDateTimeFormats.Length;
144 roundtripDateTimeFormats = new string [l];
145 localDateTimeFormats = new string [l];
146 utcDateTimeFormats = new string [l];
148 unspecifiedDateTimeFormats = new string [l * 4];
150 for (int i = 0; i < l; i++) {
151 string s = defaultDateTimeFormats [i];
152 localDateTimeFormats [i] = s + "zzz";
153 roundtripDateTimeFormats [i] = s + 'K';
154 utcDateTimeFormats [i] = s + 'Z';
156 unspecifiedDateTimeFormats [i * 4] = s;
157 unspecifiedDateTimeFormats [i * 4 + 1] = localDateTimeFormats [i];
158 unspecifiedDateTimeFormats [i * 4 + 2] = roundtripDateTimeFormats [i];
159 unspecifiedDateTimeFormats [i * 4 + 3] = utcDateTimeFormats [i];
164 static DateTimeStyles _defaultStyle = DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite;
169 private static string TryDecoding (string s)
171 if (s == null || s.Length < 6)
176 c = (char) Int32.Parse (s.Substring (1, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
178 return s [0] + DecodeName (s.Substring (1));
182 return c.ToString ();
183 return c + DecodeName (s.Substring (6));
186 public static string DecodeName (string name)
188 if (name == null || name.Length == 0)
191 int pos = name.IndexOf ('_');
192 if (pos == -1 || pos + 6 >= name.Length)
195 if ((name [pos + 1] != 'X' && name [pos + 1] != 'x') || name [pos + 6] != '_')
196 return name [0] + DecodeName (name.Substring (1));
198 return name.Substring (0, pos) + TryDecoding (name.Substring (pos + 1));
201 public static string EncodeLocalName (string name)
206 string encoded = EncodeName (name);
207 int pos = encoded.IndexOf (':');
210 return encoded.Replace (":", encodedColon);
213 internal static bool IsInvalid (char c, bool firstOnlyLetter)
215 if (c == ':') // Special case. allowed in EncodeName, but encoded in EncodeLocalName
219 return !XmlChar.IsFirstNameChar (c);
221 return !XmlChar.IsNameChar (c);
224 private static string EncodeName (string name, bool nmtoken)
226 if (name == null || name.Length == 0)
229 StringBuilder sb = new StringBuilder ();
230 int length = name.Length;
231 for (int i = 0; i < length; i++) {
233 if (IsInvalid (c, i == 0 && !nmtoken))
234 sb.AppendFormat ("_x{0:X4}_", (int) c);
235 else if (c == '_' && i + 6 < length && name [i+1] == 'x' && name [i + 6] == '_')
236 sb.Append ("_x005F_");
240 return sb.ToString ();
243 public static string EncodeName (string name)
245 return EncodeName (name, false);
248 public static string EncodeNmToken (string name)
250 if (name == String.Empty)
251 throw new XmlException ("Invalid NmToken: ''");
252 return EncodeName (name, true);
255 // {true, false, 1, 0}
256 public static bool ToBoolean(string s)
258 s = s.Trim (XmlChar.WhitespaceChars);
270 throw new FormatException(s + " is not a valid boolean value");
274 // LAMESPEC: It has been documented as public, but is marked as internal.
275 internal static string ToBinHexString (byte [] buffer)
277 StringWriter w = new StringWriter ();
278 WriteBinHex (buffer, 0, buffer.Length, w);
279 return w.ToString ();
282 internal static void WriteBinHex (byte [] buffer, int index, int count, TextWriter w)
285 throw new ArgumentNullException ("buffer");
287 throw new ArgumentOutOfRangeException (
291 "index must be non negative integer.");
294 throw new ArgumentOutOfRangeException (
298 "count must be non negative integer.");
300 if (buffer.Length < index + count)
301 throw new ArgumentOutOfRangeException ("index and count must be smaller than the length of the buffer.");
303 // Copied from XmlTextWriter.WriteBinHex ()
304 int end = index + count;
305 for (int i = index; i < end; i++) {
306 int val = buffer [i];
310 w.Write ((char) (high + 55));
312 w.Write ((char) (high + 0x30));
314 w.Write ((char) (low + 55));
316 w.Write ((char) (low + 0x30));
320 public static byte ToByte(string s)
322 return Byte.Parse(s, CultureInfo.InvariantCulture);
325 public static char ToChar(string s)
327 return Char.Parse(s);
333 public static DateTime ToDateTime (string s)
335 return ToDateTime (s, datetimeFormats);
339 public static DateTime ToDateTime (string value, XmlDateTimeSerializationMode mode)
341 string modestr = null;
344 case XmlDateTimeSerializationMode.Local:
345 dt = ToDateTime (value, localDateTimeFormats);
346 return dt == DateTime.MinValue || dt == DateTime.MaxValue ? dt : dt.ToLocalTime ();
347 case XmlDateTimeSerializationMode.RoundtripKind:
348 return ToDateTime (value, roundtripDateTimeFormats, _defaultStyle | DateTimeStyles.RoundtripKind);
349 case XmlDateTimeSerializationMode.Utc:
350 dt = ToDateTime (value, utcDateTimeFormats);
351 return dt == DateTime.MinValue || dt == DateTime.MaxValue ? dt : dt.ToUniversalTime ();
352 case XmlDateTimeSerializationMode.Unspecified:
354 return ToDateTime (value, unspecifiedDateTimeFormats);
357 return ToDateTime (value, defaultDateTimeFormats);
361 public static DateTime ToDateTime(string s, string format)
363 //DateTimeFormatInfo d = new DateTimeFormatInfo();
364 //d.FullDateTimePattern = format;
365 //return DateTime.Parse(s, d);
366 DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
367 DateTimeStyles.AllowTrailingWhite;
368 return DateTime.ParseExact (s, format, DateTimeFormatInfo.InvariantInfo, style);
371 public static DateTime ToDateTime(string s, string[] formats)
373 return ToDateTime (s, formats, _defaultStyle);
376 private static DateTime ToDateTime (string s, string [] formats, DateTimeStyles style)
378 return DateTime.ParseExact (s, formats, DateTimeFormatInfo.InvariantInfo, style);
381 public static Decimal ToDecimal(string s)
383 return Decimal.Parse(s, CultureInfo.InvariantCulture);
386 public static double ToDouble(string s)
389 throw new ArgumentNullException();
391 return Double.PositiveInfinity;
393 return Double.NegativeInfinity;
396 return Double.Parse (s, floatStyle, CultureInfo.InvariantCulture);
399 public static Guid ToGuid(string s)
404 public static short ToInt16(string s)
406 return Int16.Parse (s, NumberStyles.Integer, CultureInfo.InvariantCulture);
409 public static int ToInt32(string s)
411 return Int32.Parse (s, NumberStyles.Integer, CultureInfo.InvariantCulture);
414 public static long ToInt64(string s)
416 return Int64.Parse (s, NumberStyles.Integer, CultureInfo.InvariantCulture);
419 [CLSCompliant (false)]
420 public static SByte ToSByte(string s)
422 return SByte.Parse(s, CultureInfo.InvariantCulture);
425 public static float ToSingle(string s)
428 throw new ArgumentNullException();
430 return Single.PositiveInfinity;
432 return Single.NegativeInfinity;
435 return Single.Parse(s, floatStyle, CultureInfo.InvariantCulture);
438 public static string ToString(Guid value)
440 return value.ToString("D", CultureInfo.InvariantCulture);
443 public static string ToString(int value)
445 return value.ToString(CultureInfo.InvariantCulture);
448 public static string ToString(short value)
450 return value.ToString(CultureInfo.InvariantCulture);
453 public static string ToString(byte value)
455 return value.ToString(CultureInfo.InvariantCulture);
458 public static string ToString(long value)
460 return value.ToString(CultureInfo.InvariantCulture);
463 public static string ToString(char value)
465 return value.ToString(CultureInfo.InvariantCulture);
468 public static string ToString(bool value)
470 if (value) return "true";
474 [CLSCompliant (false)]
475 public static string ToString(SByte value)
477 return value.ToString(CultureInfo.InvariantCulture);
480 public static string ToString(Decimal value)
482 return value.ToString (CultureInfo.InvariantCulture);
485 [CLSCompliant (false)]
486 public static string ToString(UInt64 value)
488 return value.ToString(CultureInfo.InvariantCulture);
491 public static string ToString (TimeSpan value)
493 if (value == TimeSpan.Zero)
496 StringBuilder builder = new StringBuilder ();
497 if (value.Ticks < 0) {
498 builder.Append ('-');
499 value = value.Negate ();
501 builder.Append ('P');
503 builder.Append (value.Days).Append ('D');
504 if (value.Days > 0 || value.Hours > 0 || value.Minutes > 0 || value.Seconds > 0 || value.Milliseconds > 0) {
507 builder.Append (value.Hours).Append ('H');
508 if (value.Minutes > 0)
509 builder.Append (value.Minutes).Append ('M');
510 if (value.Seconds > 0 || value.Milliseconds > 0) {
511 builder.Append (value.Seconds);
512 long ticks = value.Ticks % TimeSpan.TicksPerMillisecond;
514 builder.Append ('.').AppendFormat ("{0:0000000}", value.Ticks % TimeSpan.TicksPerSecond);
515 else if (value.Milliseconds > 0)
516 builder.Append ('.').AppendFormat ("{0:000}", value.Milliseconds);
518 builder.Append ('S');
521 return builder.ToString ();
524 public static string ToString(double value)
526 if (Double.IsNegativeInfinity(value)) return "-INF";
527 if (Double.IsPositiveInfinity(value)) return "INF";
528 if (Double.IsNaN(value)) return "NaN";
529 return value.ToString("R", CultureInfo.InvariantCulture);
532 public static string ToString(float value)
534 if (Single.IsNegativeInfinity(value)) return "-INF";
535 if (Single.IsPositiveInfinity(value)) return "INF";
536 if (Single.IsNaN(value)) return "NaN";
537 return value.ToString("R", CultureInfo.InvariantCulture);
540 [CLSCompliant (false)]
541 public static string ToString(UInt32 value)
543 return value.ToString(CultureInfo.InvariantCulture);
546 [CLSCompliant (false)]
547 public static string ToString(UInt16 value)
549 return value.ToString(CultureInfo.InvariantCulture);
555 public static string ToString (DateTime value)
557 return value.ToString ("yyyy-MM-ddTHH:mm:ss.fffffffzzz", CultureInfo.InvariantCulture);
561 public static string ToString (DateTime value, XmlDateTimeSerializationMode mode)
563 // Unlike usual DateTime formatting, it preserves
564 // MaxValue/MinValue as is.
565 string modestr = null;
567 case XmlDateTimeSerializationMode.Local:
568 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToLocalTime ()).ToString (
569 "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
570 CultureInfo.InvariantCulture);
572 case XmlDateTimeSerializationMode.RoundtripKind:
573 return value.ToString (
574 "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
575 CultureInfo.InvariantCulture);
578 return value.ToString (
579 "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
580 CultureInfo.InvariantCulture);
582 case XmlDateTimeSerializationMode.Utc:
583 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToUniversalTime ()).ToString (
584 "yyyy-MM-ddTHH:mm:ss.FFFFFFFZ",
585 CultureInfo.InvariantCulture);
587 case XmlDateTimeSerializationMode.Unspecified:
588 return value.ToString (
589 "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
590 CultureInfo.InvariantCulture);
596 public static string ToString(DateTime value, string format)
598 return value.ToString(format, CultureInfo.InvariantCulture);
601 public static TimeSpan ToTimeSpan(string s)
604 throw new ArgumentException ("Invalid format string for duration schema datatype.");
609 bool minusValue = (start == 1);
611 if (s [start] != 'P')
612 throw new ArgumentException ("Invalid format string for duration schema datatype.");
622 int parsedDigits = 0;
627 while (i < s.Length) {
635 for (; i < s.Length; i++)
636 if (s [i] < '0' || '9' < s [i])
639 parsedDigits = i - start;
640 int value = int.Parse (s.Substring (start, i - start), CultureInfo.InvariantCulture);
641 if (parseStep == 7) {
642 // adjust to 7 digits so that it makes sense as millisecond digits
643 for (; parsedDigits > 7; parsedDigits--)
645 for (; parsedDigits < 7; parsedDigits++)
658 days += 365 * (value / 12) + 30 * (value % 12);
660 } else if (isTime && parseStep < 6) {
676 if (!isTime || parseStep > 4)
686 if (!isTime || parseStep > 7)
707 throw new ArgumentException ("Invalid format string for duration schema datatype.");
708 TimeSpan ts = new TimeSpan (days, hours, minutes, seconds);
710 return TimeSpan.FromTicks (- (ts.Ticks + ticks));
712 return TimeSpan.FromTicks (ts.Ticks + ticks);
715 [CLSCompliant (false)]
716 public static UInt16 ToUInt16(string s)
718 return UInt16.Parse(s, CultureInfo.InvariantCulture);
721 [CLSCompliant (false)]
722 public static UInt32 ToUInt32(string s)
724 return UInt32.Parse(s, CultureInfo.InvariantCulture);
727 [CLSCompliant (false)]
728 public static UInt64 ToUInt64(string s)
730 return UInt64.Parse(s, CultureInfo.InvariantCulture);
733 public static string VerifyName (string name)
735 if (name == null || name.Length == 0)
736 throw new ArgumentNullException("name");
738 if (!XmlChar.IsName (name))
739 throw new XmlException("'" + name + "' is not a valid XML Name");
744 public static string VerifyNCName (string ncname)
746 if (ncname == null || ncname.Length == 0)
747 throw new ArgumentNullException("ncname");
749 if (!XmlChar.IsNCName (ncname))
750 throw new XmlException ("'" + ncname + "' is not a valid XML NCName");
755 public static string VerifyTOKEN (string name)
757 internal static string VerifyTOKEN (string name)
761 throw new ArgumentNullException("name");
763 if (name.Length == 0)
766 if (XmlChar.IsWhitespace (name [0]) ||
767 XmlChar.IsWhitespace (name [name.Length - 1]))
768 throw new XmlException ("Whitespace characters (#xA, #xD, #x9, #x20) are not allowed as leading or trailing whitespaces of xs:token.");
770 for (int i = 0; i < name.Length; i++)
771 if (XmlChar.IsWhitespace (name [i]) && name [i] != ' ')
772 throw new XmlException ("Either #xA, #xD or #x9 are not allowed inside xs:token.");
778 public static string VerifyNMTOKEN (string name)
780 internal static string VerifyNMTOKEN (string name)
784 throw new ArgumentNullException("name");
786 if (!XmlChar.IsNmToken (name))
787 throw new XmlException("'" + name + "' is not a valid XML NMTOKEN");
792 // It is documented as public method, but in fact it is not.
793 internal static byte [] FromBinHexString (string s)
795 char [] chars = s.ToCharArray ();
796 byte [] bytes = new byte [chars.Length / 2 + chars.Length % 2];
797 FromBinHexString (chars, 0, chars.Length, bytes);
801 internal static int FromBinHexString (char [] chars, int offset, int charLength, byte [] buffer)
803 int bufIndex = offset;
804 for (int i = 0; i < charLength - 1; i += 2) {
805 buffer [bufIndex] = (chars [i] > '9' ?
806 (byte) (chars [i] - 'A' + 10) :
807 (byte) (chars [i] - '0'));
808 buffer [bufIndex] <<= 4;
809 buffer [bufIndex] += chars [i + 1] > '9' ?
810 (byte) (chars [i + 1] - 'A' + 10) :
811 (byte) (chars [i + 1] - '0');
814 if (charLength %2 != 0)
815 buffer [bufIndex++] = (byte)
816 ((chars [charLength - 1] > '9' ?
817 (byte) (chars [charLength - 1] - 'A' + 10) :
818 (byte) (chars [charLength - 1] - '0'))
821 return bufIndex - offset;
824 #if NET_2_0 // actually NET_3_5
827 public static DateTimeOffset ToDateTimeOffset (string s)
829 return ToDateTimeOffset (s, datetimeFormats);
832 public static DateTimeOffset ToDateTimeOffset (string s, string format)
834 return ToDateTimeOffset (s, format);
837 public static DateTimeOffset ToDateTimeOffset (string s, string [] formats)
839 return ToDateTimeOffset (s, formats);
842 public static string ToString (DateTimeOffset value)
844 return ToString (value, "yyyy-MM-ddTHH:mm:ss.fffffffzzz");
847 public static string ToString (DateTimeOffset value, string format)
849 return value.ToString (format, CultureInfo.InvariantCulture);