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 |
48 NumberStyles.AllowLeadingWhite |
49 NumberStyles.AllowTrailingWhite;
51 const NumberStyles integerStyle = NumberStyles.Integer |
52 NumberStyles.AllowLeadingWhite |
53 NumberStyles.AllowTrailingWhite;
55 static readonly string [] datetimeFormats = {
57 "yyyy-MM-ddTHH:mm:sszzz",
58 "yyyy-MM-ddTHH:mm:ss.fzzz",
59 "yyyy-MM-ddTHH:mm:ss.ffzzz",
60 "yyyy-MM-ddTHH:mm:ss.fffzzz",
61 "yyyy-MM-ddTHH:mm:ss.ffffzzz",
62 "yyyy-MM-ddTHH:mm:ss.fffffzzz",
63 "yyyy-MM-ddTHH:mm:ss.ffffffzzz",
64 "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
65 "yyyy-MM-ddTHH:mm:ssZ",
66 "yyyy-MM-ddTHH:mm:ss.fZ",
67 "yyyy-MM-ddTHH:mm:ss.ffZ",
68 "yyyy-MM-ddTHH:mm:ss.fffZ",
69 "yyyy-MM-ddTHH:mm:ss.ffffZ",
70 "yyyy-MM-ddTHH:mm:ss.fffffZ",
71 "yyyy-MM-ddTHH:mm:ss.ffffffZ",
72 "yyyy-MM-ddTHH:mm:ss.fffffffZ",
73 "yyyy-MM-ddTHH:mm:ss",
74 "yyyy-MM-ddTHH:mm:ss.f",
75 "yyyy-MM-ddTHH:mm:ss.ff",
76 "yyyy-MM-ddTHH:mm:ss.fff",
77 "yyyy-MM-ddTHH:mm:ss.ffff",
78 "yyyy-MM-ddTHH:mm:ss.fffff",
79 "yyyy-MM-ddTHH:mm:ss.ffffff",
80 "yyyy-MM-ddTHH:mm:ss.fffffff",
97 "HH:mm:ss.fffffffzzz",
129 static readonly string [] defaultDateTimeFormats = new string [] {
130 "yyyy-MM-ddTHH:mm:ss", // dateTime(1)
131 "yyyy-MM-ddTHH:mm:ss.FFFFFFF", // dateTime(2)
132 "yyyy-MM-dd", // date
134 "yyyy-MM", // gYearMonth
136 "--MM-dd", // gMonthDay
140 static readonly string [] roundtripDateTimeFormats;
141 static readonly string [] localDateTimeFormats;
142 static readonly string [] utcDateTimeFormats;
143 static readonly string [] unspecifiedDateTimeFormats;
147 int l = defaultDateTimeFormats.Length;
148 roundtripDateTimeFormats = new string [l];
149 localDateTimeFormats = new string [l];
150 utcDateTimeFormats = new string [l * 3];
151 unspecifiedDateTimeFormats = new string [l * 4];
152 for (int i = 0; i < l; i++) {
153 string s = defaultDateTimeFormats [i];
154 localDateTimeFormats [i] = s + "zzz";
155 roundtripDateTimeFormats [i] = s + 'K';
156 utcDateTimeFormats [i * 3] = s;
157 utcDateTimeFormats [i * 3 + 1] = s + 'Z';
158 utcDateTimeFormats [i * 3 + 2] = s + "zzz";
159 unspecifiedDateTimeFormats [i * 4] = s;
160 unspecifiedDateTimeFormats [i * 4 + 1] = localDateTimeFormats [i];
161 unspecifiedDateTimeFormats [i * 4 + 2] = roundtripDateTimeFormats [i];
162 unspecifiedDateTimeFormats [i * 4 + 3] = utcDateTimeFormats [i];
166 static DateTimeStyles _defaultStyle = DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite;
171 private static string TryDecoding (string s)
173 if (s == null || s.Length < 6)
178 c = (char) Int32.Parse (s.Substring (1, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
180 return s [0] + DecodeName (s.Substring (1));
184 return c.ToString ();
185 return c + DecodeName (s.Substring (6));
188 public static string DecodeName (string name)
190 if (name == null || name.Length == 0)
193 int pos = name.IndexOf ('_');
194 if (pos == -1 || pos + 6 >= name.Length)
197 if ((name [pos + 1] != 'X' && name [pos + 1] != 'x') || name [pos + 6] != '_')
198 return name [0] + DecodeName (name.Substring (1));
200 return name.Substring (0, pos) + TryDecoding (name.Substring (pos + 1));
203 public static string EncodeLocalName (string name)
208 string encoded = EncodeName (name);
209 int pos = encoded.IndexOf (':');
212 return encoded.Replace (":", encodedColon);
215 internal static bool IsInvalid (char c, bool firstOnlyLetter)
217 if (c == ':') // Special case. allowed in EncodeName, but encoded in EncodeLocalName
221 return !XmlChar.IsFirstNameChar (c);
223 return !XmlChar.IsNameChar (c);
226 private static string EncodeName (string name, bool nmtoken)
228 if (name == null || name.Length == 0)
231 StringBuilder sb = new StringBuilder ();
232 int length = name.Length;
233 for (int i = 0; i < length; i++) {
235 if (IsInvalid (c, i == 0 && !nmtoken))
236 sb.AppendFormat ("_x{0:X4}_", (int) c);
237 else if (c == '_' && i + 6 < length && name [i+1] == 'x' && name [i + 6] == '_')
238 sb.Append ("_x005F_");
242 return sb.ToString ();
245 public static string EncodeName (string name)
247 return EncodeName (name, false);
250 public static string EncodeNmToken (string name)
252 if (name == String.Empty)
253 throw new XmlException ("Invalid NmToken: ''");
254 return EncodeName (name, true);
257 // {true, false, 1, 0}
258 public static bool ToBoolean(string s)
260 s = s.Trim (XmlChar.WhitespaceChars);
272 throw new FormatException(s + " is not a valid boolean value");
276 // LAMESPEC: It has been documented as public, but is marked as internal.
277 internal static string ToBinHexString (byte [] buffer)
279 StringWriter w = new StringWriter ();
280 WriteBinHex (buffer, 0, buffer.Length, w);
281 return w.ToString ();
284 internal static void WriteBinHex (byte [] buffer, int index, int count, TextWriter w)
287 throw new ArgumentNullException ("buffer");
289 throw new ArgumentOutOfRangeException (
293 "index must be non negative integer.");
296 throw new ArgumentOutOfRangeException (
300 "count must be non negative integer.");
302 if (buffer.Length < index + count)
303 throw new ArgumentOutOfRangeException ("index and count must be smaller than the length of the buffer.");
305 // Copied from XmlTextWriter.WriteBinHex ()
306 int end = index + count;
307 for (int i = index; i < end; i++) {
308 int val = buffer [i];
312 w.Write ((char) (high + 55));
314 w.Write ((char) (high + 0x30));
316 w.Write ((char) (low + 55));
318 w.Write ((char) (low + 0x30));
322 public static byte ToByte(string s)
324 return Byte.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
327 public static char ToChar(string s)
330 return Char.Parse(s);
333 throw new ArgumentNullException ("s");
336 throw new FormatException ("String contain more than one char");
345 public static DateTime ToDateTime (string s)
347 return ToDateTime (s, datetimeFormats);
351 public static DateTime ToDateTime (string value, XmlDateTimeSerializationMode mode)
355 case XmlDateTimeSerializationMode.Local:
356 dt = ToDateTime (value, localDateTimeFormats);
357 return dt == DateTime.MinValue || dt == DateTime.MaxValue ? dt : dt.ToLocalTime ();
358 case XmlDateTimeSerializationMode.RoundtripKind:
359 return ToDateTime (value, roundtripDateTimeFormats, _defaultStyle | DateTimeStyles.RoundtripKind);
360 case XmlDateTimeSerializationMode.Utc:
361 dt = ToDateTime (value, utcDateTimeFormats);
362 return dt == DateTime.MinValue || dt == DateTime.MaxValue ? dt : dt.ToUniversalTime ();
363 case XmlDateTimeSerializationMode.Unspecified:
364 return ToDateTime (value, unspecifiedDateTimeFormats);
366 return ToDateTime (value, defaultDateTimeFormats);
370 public static DateTime ToDateTime(string s, string format)
372 //DateTimeFormatInfo d = new DateTimeFormatInfo();
373 //d.FullDateTimePattern = format;
374 //return DateTime.Parse(s, d);
375 DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
376 DateTimeStyles.AllowTrailingWhite;
377 return DateTime.ParseExact (s, format, DateTimeFormatInfo.InvariantInfo, style);
380 public static DateTime ToDateTime(string s, string[] formats)
382 return ToDateTime (s, formats, _defaultStyle);
385 private static DateTime ToDateTime (string s, string [] formats, DateTimeStyles style)
387 return DateTime.ParseExact (s, formats, DateTimeFormatInfo.InvariantInfo, style);
390 public static Decimal ToDecimal(string s)
392 return Decimal.Parse(s, CultureInfo.InvariantCulture);
395 public static double ToDouble(string s)
398 throw new ArgumentNullException();
400 float f = TryParseStringFloatConstants (s);
404 return Double.Parse (s, floatStyle, CultureInfo.InvariantCulture);
407 static float TryParseStringFloatConstants (string s)
410 while (sidx < s.Length && Char.IsWhiteSpace (s [sidx]))
412 if (sidx == s.Length)
413 throw new FormatException ();
414 int sEndPos = s.Length - 1;
415 while (Char.IsWhiteSpace (s [sEndPos]))
418 if (TryParseStringConstant ("NaN", s, sidx, sEndPos))
420 if (TryParseStringConstant ("INF", s, sidx, sEndPos))
421 return Single.PositiveInfinity;
422 if (TryParseStringConstant ("-INF", s, sidx, sEndPos))
423 return Single.NegativeInfinity;
424 // Handle these here because Single.Parse("Infinity") is invalid while XmlConvert.ToSingle("Infinity") is valid.
425 if (TryParseStringConstant ("Infinity", s, sidx, sEndPos))
426 return Single.PositiveInfinity;
427 if (TryParseStringConstant ("-Infinity", s, sidx, sEndPos))
428 return Single.NegativeInfinity;
432 static bool TryParseStringConstant (string format, string s, int start, int end)
434 return end - start + 1 == format.Length && String.CompareOrdinal (format, 0, s, start, format.Length) == 0;
437 public static Guid ToGuid (string s)
441 } catch (FormatException ex) {
442 throw new FormatException (String.Format ("Invalid Guid input '{0}'", ex.InnerException));
446 public static short ToInt16(string s)
448 return Int16.Parse (s, integerStyle, CultureInfo.InvariantCulture);
451 public static int ToInt32(string s)
453 return Int32.Parse (s, integerStyle, CultureInfo.InvariantCulture);
456 public static long ToInt64(string s)
458 return Int64.Parse (s, integerStyle, CultureInfo.InvariantCulture);
461 [CLSCompliant (false)]
462 public static SByte ToSByte(string s)
464 return SByte.Parse(s, integerStyle, CultureInfo.InvariantCulture);
467 public static float ToSingle(string s)
470 throw new ArgumentNullException();
472 float f = TryParseStringFloatConstants (s);
476 return Single.Parse(s, floatStyle, CultureInfo.InvariantCulture);
479 public static string ToString(Guid value)
481 return value.ToString("D", CultureInfo.InvariantCulture);
484 public static string ToString(int value)
486 return value.ToString(CultureInfo.InvariantCulture);
489 public static string ToString(short value)
491 return value.ToString(CultureInfo.InvariantCulture);
494 public static string ToString(byte value)
496 return value.ToString(CultureInfo.InvariantCulture);
499 public static string ToString(long value)
501 return value.ToString(CultureInfo.InvariantCulture);
504 public static string ToString(char value)
506 return value.ToString(CultureInfo.InvariantCulture);
509 public static string ToString(bool value)
511 if (value) return "true";
515 [CLSCompliant (false)]
516 public static string ToString(SByte value)
518 return value.ToString(CultureInfo.InvariantCulture);
521 public static string ToString(Decimal value)
523 return value.ToString (CultureInfo.InvariantCulture);
526 [CLSCompliant (false)]
527 public static string ToString(UInt64 value)
529 return value.ToString(CultureInfo.InvariantCulture);
532 public static string ToString (TimeSpan value)
534 if (value == TimeSpan.Zero)
537 StringBuilder builder = new StringBuilder ();
538 if (value.Ticks < 0) {
539 builder.Append ('-');
540 value = value.Negate ();
542 builder.Append ('P');
544 builder.Append (value.Days).Append ('D');
545 if (value.Days > 0 || value.Hours > 0 || value.Minutes > 0 || value.Seconds > 0 || value.Milliseconds > 0) {
548 builder.Append (value.Hours).Append ('H');
549 if (value.Minutes > 0)
550 builder.Append (value.Minutes).Append ('M');
551 if (value.Seconds > 0 || value.Milliseconds > 0) {
552 builder.Append (value.Seconds);
553 long ticks = value.Ticks % TimeSpan.TicksPerMillisecond;
555 builder.Append ('.').AppendFormat ("{0:0000000}", value.Ticks % TimeSpan.TicksPerSecond);
556 else if (value.Milliseconds > 0)
557 builder.Append ('.').AppendFormat ("{0:000}", value.Milliseconds);
559 builder.Append ('S');
562 return builder.ToString ();
565 public static string ToString(double value)
567 if (Double.IsNegativeInfinity(value)) return "-INF";
568 if (Double.IsPositiveInfinity(value)) return "INF";
569 if (Double.IsNaN(value)) return "NaN";
570 return value.ToString("R", CultureInfo.InvariantCulture);
573 public static string ToString(float value)
575 if (Single.IsNegativeInfinity(value)) return "-INF";
576 if (Single.IsPositiveInfinity(value)) return "INF";
577 if (Single.IsNaN(value)) return "NaN";
578 return value.ToString("R", CultureInfo.InvariantCulture);
581 [CLSCompliant (false)]
582 public static string ToString(UInt32 value)
584 return value.ToString(CultureInfo.InvariantCulture);
587 [CLSCompliant (false)]
588 public static string ToString(UInt16 value)
590 return value.ToString(CultureInfo.InvariantCulture);
596 public static string ToString (DateTime value)
598 return value.ToString ("yyyy-MM-ddTHH:mm:ss.fffffffzzz", CultureInfo.InvariantCulture);
602 public static string ToString (DateTime value, XmlDateTimeSerializationMode mode)
604 // Unlike usual DateTime formatting, it preserves
605 // MaxValue/MinValue as is.
607 case XmlDateTimeSerializationMode.Local:
608 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToLocalTime ()).ToString (
609 "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
610 CultureInfo.InvariantCulture);
611 case XmlDateTimeSerializationMode.RoundtripKind:
612 return value.ToString (
613 "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
614 CultureInfo.InvariantCulture);
616 return value.ToString (
617 "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
618 CultureInfo.InvariantCulture);
619 case XmlDateTimeSerializationMode.Utc:
620 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToUniversalTime ()).ToString (
621 "yyyy-MM-ddTHH:mm:ss.FFFFFFFZ",
622 CultureInfo.InvariantCulture);
623 case XmlDateTimeSerializationMode.Unspecified:
624 return value.ToString (
625 "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
626 CultureInfo.InvariantCulture);
631 public static string ToString(DateTime value, string format)
633 return value.ToString(format, CultureInfo.InvariantCulture);
636 public static TimeSpan ToTimeSpan(string s)
639 throw new FormatException ("Invalid format string for duration schema datatype.");
644 bool minusValue = (start == 1);
646 if (s [start] != 'P')
647 throw new FormatException ("Invalid format string for duration schema datatype.");
657 int parsedDigits = 0;
662 while (i < s.Length) {
670 for (; i < s.Length; i++)
671 if (s [i] < '0' || '9' < s [i])
674 parsedDigits = i - start;
675 int value = int.Parse (s.Substring (start, i - start), CultureInfo.InvariantCulture);
676 if (parseStep == 7) {
677 // adjust to 7 digits so that it makes sense as millisecond digits
678 for (; parsedDigits > 7; parsedDigits--)
680 for (; parsedDigits < 7; parsedDigits++)
693 days += 365 * (value / 12) + 30 * (value % 12);
695 } else if (isTime && parseStep < 6) {
711 if (!isTime || parseStep > 4)
721 if (!isTime || parseStep > 7)
742 throw new FormatException ("Invalid format string for duration schema datatype.");
743 TimeSpan ts = new TimeSpan (days, hours, minutes, seconds);
745 return TimeSpan.FromTicks (- (ts.Ticks + ticks));
747 return TimeSpan.FromTicks (ts.Ticks + ticks);
750 [CLSCompliant (false)]
751 public static UInt16 ToUInt16(string s)
753 return UInt16.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
756 [CLSCompliant (false)]
757 public static UInt32 ToUInt32(string s)
759 return UInt32.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
762 [CLSCompliant (false)]
763 public static UInt64 ToUInt64(string s)
765 return UInt64.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
768 public static string VerifyName (string name)
770 if (name == null || name.Length == 0)
771 throw new ArgumentNullException("name");
773 if (!XmlChar.IsName (name))
774 throw new XmlException("'" + name + "' is not a valid XML Name");
779 public static string VerifyNCName (string ncname)
781 if (ncname == null || ncname.Length == 0)
782 throw new ArgumentNullException("ncname");
784 if (!XmlChar.IsNCName (ncname))
785 throw new XmlException ("'" + ncname + "' is not a valid XML NCName");
790 public static string VerifyTOKEN (string name)
792 internal static string VerifyTOKEN (string name)
796 throw new ArgumentNullException("name");
798 if (name.Length == 0)
801 if (XmlChar.IsWhitespace (name [0]) ||
802 XmlChar.IsWhitespace (name [name.Length - 1]))
803 throw new XmlException ("Whitespace characters (#xA, #xD, #x9, #x20) are not allowed as leading or trailing whitespaces of xs:token.");
805 for (int i = 0; i < name.Length; i++)
806 if (XmlChar.IsWhitespace (name [i]) && name [i] != ' ')
807 throw new XmlException ("Either #xA, #xD or #x9 are not allowed inside xs:token.");
813 public static string VerifyNMTOKEN (string name)
815 internal static string VerifyNMTOKEN (string name)
819 throw new ArgumentNullException("name");
821 if (!XmlChar.IsNmToken (name))
822 throw new XmlException("'" + name + "' is not a valid XML NMTOKEN");
827 // It is documented as public method, but in fact it is not.
828 internal static byte [] FromBinHexString (string s)
830 char [] chars = s.ToCharArray ();
831 byte [] bytes = new byte [chars.Length / 2 + chars.Length % 2];
832 FromBinHexString (chars, 0, chars.Length, bytes);
836 internal static int FromBinHexString (char [] chars, int offset, int charLength, byte [] buffer)
838 int bufIndex = offset;
839 for (int i = 0; i < charLength - 1; i += 2) {
840 buffer [bufIndex] = (chars [i] > '9' ?
841 (byte) (chars [i] - 'A' + 10) :
842 (byte) (chars [i] - '0'));
843 buffer [bufIndex] <<= 4;
844 buffer [bufIndex] += chars [i + 1] > '9' ?
845 (byte) (chars [i + 1] - 'A' + 10) :
846 (byte) (chars [i + 1] - '0');
849 if (charLength %2 != 0)
850 buffer [bufIndex++] = (byte)
851 ((chars [charLength - 1] > '9' ?
852 (byte) (chars [charLength - 1] - 'A' + 10) :
853 (byte) (chars [charLength - 1] - '0'))
856 return bufIndex - offset;
859 #if NET_2_0 // actually NET_3_5
862 public static DateTimeOffset ToDateTimeOffset (string s)
864 return ToDateTimeOffset (s, datetimeFormats);
867 public static DateTimeOffset ToDateTimeOffset (string s, string format)
869 return DateTimeOffset.ParseExact (s, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
872 public static DateTimeOffset ToDateTimeOffset (string s, string [] formats)
874 DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
875 DateTimeStyles.AllowTrailingWhite |
876 DateTimeStyles.AssumeUniversal;
877 return DateTimeOffset.ParseExact (s, formats, CultureInfo.InvariantCulture, style);
880 public static string ToString (DateTimeOffset value)
882 return ToString (value, "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz");
885 public static string ToString (DateTimeOffset value, string format)
887 return value.ToString (format, CultureInfo.InvariantCulture);
891 // it is used only from 2.1 System.Xml.Serialization.dll from
892 // MS Silverlight SDK. We don't use it so far.
893 internal static Uri ToUri (string s)
895 return new Uri (s, UriKind.RelativeOrAbsolute);