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",
125 private static string TryDecoding (string s)
127 if (s == null || s.Length < 6)
132 c = (char) Int32.Parse (s.Substring (1, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
134 return s [0] + DecodeName (s.Substring (1));
138 return c.ToString ();
139 return c + DecodeName (s.Substring (6));
142 public static string DecodeName (string name)
144 if (name == null || name.Length == 0)
147 int pos = name.IndexOf ('_');
148 if (pos == -1 || pos + 6 >= name.Length)
151 if ((name [pos + 1] != 'X' && name [pos + 1] != 'x') || name [pos + 6] != '_')
152 return name [0] + DecodeName (name.Substring (1));
154 return name.Substring (0, pos) + TryDecoding (name.Substring (pos + 1));
157 public static string EncodeLocalName (string name)
162 string encoded = EncodeName (name);
163 int pos = encoded.IndexOf (':');
166 return encoded.Replace (":", encodedColon);
169 internal static bool IsInvalid (char c, bool firstOnlyLetter)
171 if (c == ':') // Special case. allowed in EncodeName, but encoded in EncodeLocalName
175 return !XmlChar.IsFirstNameChar (c);
177 return !XmlChar.IsNameChar (c);
180 private static string EncodeName (string name, bool nmtoken)
182 if (name == null || name.Length == 0)
185 StringBuilder sb = new StringBuilder ();
186 int length = name.Length;
187 for (int i = 0; i < length; i++) {
189 if (IsInvalid (c, i == 0 && !nmtoken))
190 sb.AppendFormat ("_x{0:X4}_", (int) c);
191 else if (c == '_' && i + 6 < length && name [i+1] == 'x' && name [i + 6] == '_')
192 sb.Append ("_x005F_");
196 return sb.ToString ();
199 public static string EncodeName (string name)
201 return EncodeName (name, false);
204 public static string EncodeNmToken (string name)
206 if (name == String.Empty)
207 throw new XmlException ("Invalid NmToken: ''");
208 return EncodeName (name, true);
211 // {true, false, 1, 0}
212 public static bool ToBoolean(string s)
214 s = s.Trim (XmlChar.WhitespaceChars);
226 throw new FormatException(s + " is not a valid boolean value");
230 // LAMESPEC: It has been documented as public, but is marked as internal.
231 internal static string ToBinHexString (byte [] buffer)
233 StringWriter w = new StringWriter ();
234 WriteBinHex (buffer, 0, buffer.Length, w);
235 return w.ToString ();
238 internal static void WriteBinHex (byte [] buffer, int index, int count, TextWriter w)
241 throw new ArgumentNullException ("buffer");
243 throw new ArgumentOutOfRangeException (
247 "index must be non negative integer.");
250 throw new ArgumentOutOfRangeException (
254 "count must be non negative integer.");
256 if (buffer.Length < index + count)
257 throw new ArgumentOutOfRangeException ("index and count must be smaller than the length of the buffer.");
259 // Copied from XmlTextWriter.WriteBinHex ()
260 int end = index + count;
261 for (int i = index; i < end; i++) {
262 int val = buffer [i];
266 w.Write ((char) (high + 55));
268 w.Write ((char) (high + 0x30));
270 w.Write ((char) (low + 55));
272 w.Write ((char) (low + 0x30));
276 public static byte ToByte(string s)
278 return Byte.Parse(s, CultureInfo.InvariantCulture);
281 public static char ToChar(string s)
283 return Char.Parse(s);
289 public static DateTime ToDateTime (string s)
291 return ToDateTime (s, datetimeFormats);
295 public static DateTime ToDateTime (string value, XmlDateTimeSerializationMode mode)
297 string modestr = null;
300 case XmlDateTimeSerializationMode.Local:
301 dt = ToDateTime (value, "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz");
302 return dt == DateTime.MinValue || dt == DateTime.MaxValue ? dt : dt.ToLocalTime ();
303 case XmlDateTimeSerializationMode.RoundtripKind:
304 return ToDateTime (value, "yyyy-MM-ddTHH:mm:ss.FFFFFFFK");
305 case XmlDateTimeSerializationMode.Utc:
306 dt = ToDateTime (value, "yyyy-MM-ddTHH:mm:ss.FFFFFFFZ");
307 return dt == DateTime.MinValue || dt == DateTime.MaxValue ? dt : dt.ToUniversalTime ();
308 case XmlDateTimeSerializationMode.Unspecified:
310 return ToDateTime (value, "yyyy-MM-ddTHH:mm:ss.FFFFFFF");
314 public static DateTime ToDateTime(string s, string format)
316 //DateTimeFormatInfo d = new DateTimeFormatInfo();
317 //d.FullDateTimePattern = format;
318 //return DateTime.Parse(s, d);
319 DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
320 DateTimeStyles.AllowTrailingWhite;
321 return DateTime.ParseExact (s, format, DateTimeFormatInfo.InvariantInfo, style);
324 public static DateTime ToDateTime(string s, string[] formats)
326 DateTimeStyles style = DateTimeStyles.AllowLeadingWhite |
327 DateTimeStyles.AllowTrailingWhite;
328 return DateTime.ParseExact (s, formats, DateTimeFormatInfo.InvariantInfo, style);
331 public static Decimal ToDecimal(string s)
333 return Decimal.Parse(s, CultureInfo.InvariantCulture);
336 public static double ToDouble(string s)
339 throw new ArgumentNullException();
341 return Double.PositiveInfinity;
343 return Double.NegativeInfinity;
346 return Double.Parse (s, floatStyle, CultureInfo.InvariantCulture);
349 public static Guid ToGuid(string s)
354 public static short ToInt16(string s)
356 return Int16.Parse (s, NumberStyles.Integer, CultureInfo.InvariantCulture);
359 public static int ToInt32(string s)
361 return Int32.Parse (s, NumberStyles.Integer, CultureInfo.InvariantCulture);
364 public static long ToInt64(string s)
366 return Int64.Parse (s, NumberStyles.Integer, CultureInfo.InvariantCulture);
369 [CLSCompliant (false)]
370 public static SByte ToSByte(string s)
372 return SByte.Parse(s, CultureInfo.InvariantCulture);
375 public static float ToSingle(string s)
378 throw new ArgumentNullException();
380 return Single.PositiveInfinity;
382 return Single.NegativeInfinity;
385 return Single.Parse(s, floatStyle, CultureInfo.InvariantCulture);
388 public static string ToString(Guid value)
390 return value.ToString("D", CultureInfo.InvariantCulture);
393 public static string ToString(int value)
395 return value.ToString(CultureInfo.InvariantCulture);
398 public static string ToString(short value)
400 return value.ToString(CultureInfo.InvariantCulture);
403 public static string ToString(byte value)
405 return value.ToString(CultureInfo.InvariantCulture);
408 public static string ToString(long value)
410 return value.ToString(CultureInfo.InvariantCulture);
413 public static string ToString(char value)
415 return value.ToString(CultureInfo.InvariantCulture);
418 public static string ToString(bool value)
420 if (value) return "true";
424 [CLSCompliant (false)]
425 public static string ToString(SByte value)
427 return value.ToString(CultureInfo.InvariantCulture);
430 public static string ToString(Decimal value)
432 return value.ToString (CultureInfo.InvariantCulture);
435 [CLSCompliant (false)]
436 public static string ToString(UInt64 value)
438 return value.ToString(CultureInfo.InvariantCulture);
441 public static string ToString (TimeSpan value)
443 if (value == TimeSpan.Zero)
446 StringBuilder builder = new StringBuilder ();
447 if (value.Ticks < 0) {
448 builder.Append ('-');
449 value = value.Negate ();
451 builder.Append ('P');
453 builder.Append (value.Days).Append ('D');
454 if (value.Days > 0 || value.Hours > 0 || value.Minutes > 0 || value.Seconds > 0 || value.Milliseconds > 0) {
457 builder.Append (value.Hours).Append ('H');
458 if (value.Minutes > 0)
459 builder.Append (value.Minutes).Append ('M');
460 if (value.Seconds > 0 || value.Milliseconds > 0) {
461 builder.Append (value.Seconds);
462 long ticks = value.Ticks % TimeSpan.TicksPerMillisecond;
464 builder.Append ('.').AppendFormat ("{0:0000000}", value.Ticks % TimeSpan.TicksPerSecond);
465 else if (value.Milliseconds > 0)
466 builder.Append ('.').AppendFormat ("{0:000}", value.Milliseconds);
468 builder.Append ('S');
471 return builder.ToString ();
474 public static string ToString(double value)
476 if (Double.IsNegativeInfinity(value)) return "-INF";
477 if (Double.IsPositiveInfinity(value)) return "INF";
478 if (Double.IsNaN(value)) return "NaN";
479 return value.ToString(CultureInfo.InvariantCulture);
482 public static string ToString(float value)
484 if (Single.IsNegativeInfinity(value)) return "-INF";
485 if (Single.IsPositiveInfinity(value)) return "INF";
486 if (Single.IsNaN(value)) return "NaN";
487 return value.ToString(CultureInfo.InvariantCulture);
490 [CLSCompliant (false)]
491 public static string ToString(UInt32 value)
493 return value.ToString(CultureInfo.InvariantCulture);
496 [CLSCompliant (false)]
497 public static string ToString(UInt16 value)
499 return value.ToString(CultureInfo.InvariantCulture);
505 public static string ToString (DateTime value)
507 return value.ToString ("yyyy-MM-ddTHH:mm:ss.fffffffzzz", CultureInfo.InvariantCulture);
511 public static string ToString (DateTime value, XmlDateTimeSerializationMode mode)
513 // Unlike usual DateTime formatting, it preserves
514 // MaxValue/MinValue as is.
515 string modestr = null;
517 case XmlDateTimeSerializationMode.Local:
518 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToLocalTime ()).ToString (
519 "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
520 CultureInfo.InvariantCulture);
522 case XmlDateTimeSerializationMode.RoundtripKind:
523 return value.ToString (
524 "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
525 CultureInfo.InvariantCulture);
528 return value.ToString (
529 "yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz",
530 CultureInfo.InvariantCulture);
532 case XmlDateTimeSerializationMode.Utc:
533 return (value == DateTime.MinValue ? DateTime.MinValue : value == DateTime.MaxValue ? value : value.ToUniversalTime ()).ToString (
534 "yyyy-MM-ddTHH:mm:ss.FFFFFFFZ",
535 CultureInfo.InvariantCulture);
537 case XmlDateTimeSerializationMode.Unspecified:
538 return value.ToString (
539 "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
540 CultureInfo.InvariantCulture);
546 public static string ToString(DateTime value, string format)
548 return value.ToString(format, CultureInfo.InvariantCulture);
551 public static TimeSpan ToTimeSpan(string s)
554 throw new ArgumentException ("Invalid format string for duration schema datatype.");
559 bool minusValue = (start == 1);
561 if (s [start] != 'P')
562 throw new ArgumentException ("Invalid format string for duration schema datatype.");
572 int parsedDigits = 0;
577 while (i < s.Length) {
585 for (; i < s.Length; i++)
586 if (s [i] < '0' || '9' < s [i])
589 parsedDigits = i - start;
590 int value = int.Parse (s.Substring (start, i - start), CultureInfo.InvariantCulture);
591 if (parseStep == 7) {
592 // adjust to 7 digits so that it makes sense as millisecond digits
593 for (; parsedDigits > 7; parsedDigits--)
595 for (; parsedDigits < 7; parsedDigits++)
608 days += 365 * (value / 12) + 30 * (value % 12);
610 } else if (isTime && parseStep < 6) {
626 if (!isTime || parseStep > 4)
636 if (!isTime || parseStep > 7)
657 throw new ArgumentException ("Invalid format string for duration schema datatype.");
658 TimeSpan ts = new TimeSpan (days, hours, minutes, seconds);
660 return TimeSpan.FromTicks (- (ts.Ticks + ticks));
662 return TimeSpan.FromTicks (ts.Ticks + ticks);
665 [CLSCompliant (false)]
666 public static UInt16 ToUInt16(string s)
668 return UInt16.Parse(s, CultureInfo.InvariantCulture);
671 [CLSCompliant (false)]
672 public static UInt32 ToUInt32(string s)
674 return UInt32.Parse(s, CultureInfo.InvariantCulture);
677 [CLSCompliant (false)]
678 public static UInt64 ToUInt64(string s)
680 return UInt64.Parse(s, CultureInfo.InvariantCulture);
683 public static string VerifyName (string name)
685 if (name == null || name.Length == 0)
686 throw new ArgumentNullException("name");
688 if (!XmlChar.IsName (name))
689 throw new XmlException("'" + name + "' is not a valid XML Name");
694 public static string VerifyNCName (string ncname)
696 if (ncname == null || ncname.Length == 0)
697 throw new ArgumentNullException("ncname");
699 if (!XmlChar.IsNCName (ncname))
700 throw new XmlException ("'" + ncname + "' is not a valid XML NCName");
705 public static string VerifyTOKEN (string name)
707 internal static string VerifyTOKEN (string name)
711 throw new ArgumentNullException("name");
713 if (name.Length == 0)
716 if (XmlChar.IsWhitespace (name [0]) ||
717 XmlChar.IsWhitespace (name [name.Length - 1]))
718 throw new XmlException ("Whitespace characters (#xA, #xD, #x9, #x20) are not allowed as leading or trailing whitespaces of xs:token.");
720 for (int i = 0; i < name.Length; i++)
721 if (XmlChar.IsWhitespace (name [i]) && name [i] != ' ')
722 throw new XmlException ("Either #xA, #xD or #x9 are not allowed inside xs:token.");
728 public static string VerifyNMTOKEN (string name)
730 internal static string VerifyNMTOKEN (string name)
734 throw new ArgumentNullException("name");
736 if (!XmlChar.IsNmToken (name))
737 throw new XmlException("'" + name + "' is not a valid XML NMTOKEN");
742 // It is documented as public method, but in fact it is not.
743 internal static byte [] FromBinHexString (string s)
745 char [] chars = s.ToCharArray ();
746 byte [] bytes = new byte [chars.Length / 2 + chars.Length % 2];
747 FromBinHexString (chars, 0, chars.Length, bytes);
751 internal static int FromBinHexString (char [] chars, int offset, int charLength, byte [] buffer)
753 int bufIndex = offset;
754 for (int i = 0; i < charLength - 1; i += 2) {
755 buffer [bufIndex] = (chars [i] > '9' ?
756 (byte) (chars [i] - 'A' + 10) :
757 (byte) (chars [i] - '0'));
758 buffer [bufIndex] <<= 4;
759 buffer [bufIndex] += chars [i + 1] > '9' ?
760 (byte) (chars [i + 1] - 'A' + 10) :
761 (byte) (chars [i + 1] - '0');
764 if (charLength %2 != 0)
765 buffer [bufIndex++] = (byte)
766 ((chars [charLength - 1] > '9' ?
767 (byte) (chars [charLength - 1] - 'A' + 10) :
768 (byte) (chars [charLength - 1] - '0'))
771 return bufIndex - offset;