5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2007-2011 Novell, Inc (http://www.novell.com)
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System.Collections.Generic;
30 using System.Globalization;
35 namespace System.Runtime.Serialization.Json
37 // It is a subset of XmlInputStream from System.XML.
38 class EncodingDetecingInputStream : Stream
40 internal static readonly Encoding StrictUTF8, Strict1234UTF32, StrictBigEndianUTF16, StrictUTF16;
42 static EncodingDetecingInputStream ()
44 StrictUTF8 = new UTF8Encoding (false, true);
45 Strict1234UTF32 = new UTF32Encoding (true, false, true);
46 StrictBigEndianUTF16 = new UnicodeEncoding (true, false, true);
47 StrictUTF16 = new UnicodeEncoding (false, false, true);
56 static XmlException encodingException = new XmlException ("invalid encoding specification.");
58 public EncodingDetecingInputStream (Stream stream)
61 throw new ArgumentNullException ("stream");
65 private void Initialize (Stream stream)
67 buffer = new byte [6];
69 enc = StrictUTF8; // Default to UTF8 if we can't guess it
70 bufLength = stream.Read (buffer, 0, buffer.Length);
71 if (bufLength == -1 || bufLength == 0) {
75 int c = ReadByteSpecial ();
78 c = ReadByteSpecial ();
80 // BOM-ed little endian utf-16
81 enc = Encoding.Unicode;
83 // It doesn't start from "<?xml" then its encoding is utf-8
88 c = ReadByteSpecial ();
90 // BOM-ed big endian utf-16
91 enc = Encoding.BigEndianUnicode;
94 // It doesn't start from "<?xml" then its encoding is utf-8
99 c = ReadByteSpecial ();
101 c = ReadByteSpecial ();
106 buffer [--bufPos] = 0xEF;
110 // It could still be 1234/2143/3412 variants of UTF32, but only 1234 version is available on .NET.
111 c = ReadByteSpecial ();
113 enc = Strict1234UTF32;
115 enc = StrictBigEndianUTF16;
118 c = ReadByteSpecial ();
126 // Just like readbyte, but grows the buffer too.
127 int ReadByteSpecial ()
129 if (bufLength > bufPos)
130 return buffer [bufPos++];
132 byte [] newbuf = new byte [buffer.Length * 2];
133 Buffer.BlockCopy (buffer, 0, newbuf, 0, bufLength);
134 int nbytes = stream.Read (newbuf, bufLength, buffer.Length);
135 if (nbytes == -1 || nbytes == 0)
140 return buffer [bufPos++];
143 public Encoding ActualEncoding {
147 #region Public Overrides
148 public override bool CanRead {
150 if (bufLength > bufPos)
153 return stream.CanRead;
157 // FIXME: It should support base stream's CanSeek.
158 public override bool CanSeek {
159 get { return false; } // stream.CanSeek; }
162 public override bool CanWrite {
163 get { return false; }
166 public override long Length {
168 return stream.Length;
172 public override long Position {
174 return stream.Position - bufLength + bufPos;
177 if(value < bufLength)
180 stream.Position = value - bufLength;
184 public override void Close ()
189 public override void Flush ()
194 public override int Read (byte[] buffer, int offset, int count)
197 if (count <= bufLength - bufPos) { // all from buffer
198 Buffer.BlockCopy (this.buffer, bufPos, buffer, offset, count);
202 int bufRest = bufLength - bufPos;
203 if (bufLength > bufPos) {
204 Buffer.BlockCopy (this.buffer, bufPos, buffer, offset, bufRest);
208 stream.Read (buffer, offset + bufRest, count - bufRest);
213 public override int ReadByte ()
215 if (bufLength > bufPos) {
216 return buffer [bufPos++];
218 return stream.ReadByte ();
221 public override long Seek (long offset, System.IO.SeekOrigin origin)
223 int bufRest = bufLength - bufPos;
224 if (origin == SeekOrigin.Current)
225 if (offset < bufRest)
226 return buffer [bufPos + offset];
228 return stream.Seek (offset - bufRest, origin);
230 return stream.Seek (offset, origin);
233 public override void SetLength (long value)
235 stream.SetLength (value);
238 public override void Write (byte[] buffer, int offset, int count)
240 throw new NotSupportedException ();
245 class PushbackReader : StreamReader
249 public PushbackReader (Stream stream, Encoding encoding) : base (stream, encoding)
251 pushback = new Stack<int>();
254 public PushbackReader (Stream stream) : this (new EncodingDetecingInputStream (stream))
258 public PushbackReader (EncodingDetecingInputStream stream) : this (stream, stream.ActualEncoding)
262 public override void Close ()
267 public override int Peek ()
269 if (pushback.Count > 0) {
270 return pushback.Peek ();
277 public override int Read ()
279 if (pushback.Count > 0) {
280 return pushback.Pop ();
287 public void Pushback (int ch)
293 // FIXME: quotas check
294 class JsonReader : XmlDictionaryReader, IXmlJsonReaderInitializer, IXmlLineInfo
298 public readonly string Name;
299 public readonly string Type;
300 public bool HasContent;
302 public ElementInfo (string name, string type)
318 PushbackReader reader;
319 XmlDictionaryReaderQuotas quotas;
320 OnXmlDictionaryReaderClose on_close;
321 XmlNameTable name_table = new NameTable ();
323 XmlNodeType current_node;
324 AttributeState attr_state;
327 string current_runtime_type, next_object_content_name;
328 ReadState read_state = ReadState.Initial;
331 Stack<ElementInfo> elements = new Stack<ElementInfo> ();
333 int line = 1, column = 0;
337 public JsonReader (byte [] buffer, int offset, int count, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
339 SetInput (buffer, offset, count, encoding, quotas, onClose);
342 public JsonReader (Stream stream, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
344 SetInput (stream, encoding, quotas, onClose);
347 internal bool LameSilverlightLiteralParser { get; set; }
351 public bool HasLineInfo ()
356 public int LineNumber {
360 public int LinePosition {
361 get { return column; }
364 // IXmlJsonReaderInitializer
366 public void SetInput (byte [] buffer, int offset, int count, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
368 SetInput (new MemoryStream (buffer, offset, count), encoding, quotas, onClose);
371 public void SetInput (Stream stream, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
373 if (encoding != null)
374 reader = new PushbackReader (stream, encoding);
376 reader = new PushbackReader (stream);
378 throw new ArgumentNullException ("quotas");
379 this.quotas = quotas;
380 this.on_close = onClose;
383 // XmlDictionaryReader
385 public override int AttributeCount {
386 get { return current_node != XmlNodeType.Element ? 0 : current_runtime_type != null ? 2 : 1; }
389 public override string BaseURI {
390 get { return String.Empty; }
393 public override int Depth {
396 switch (attr_state) {
397 case AttributeState.Type:
398 case AttributeState.RuntimeType:
401 case AttributeState.TypeValue:
402 case AttributeState.RuntimeTypeValue:
405 case AttributeState.None:
406 if (NodeType == XmlNodeType.Text)
410 return read_state != ReadState.Interactive ? 0 : elements.Count - 1 + mod;
414 public override bool EOF {
416 switch (read_state) {
417 case ReadState.Closed:
418 case ReadState.EndOfFile:
426 public override bool HasValue {
429 case XmlNodeType.Attribute:
430 case XmlNodeType.Text:
438 public override bool IsEmptyElement {
439 get { return false; }
442 public override string LocalName {
444 switch (attr_state) {
445 case AttributeState.Type:
447 case AttributeState.RuntimeType:
451 case XmlNodeType.Element:
452 case XmlNodeType.EndElement:
453 return elements.Peek ().Name;
460 public override string NamespaceURI {
461 get { return String.Empty; }
464 public override XmlNameTable NameTable {
465 get { return name_table; }
468 public override XmlNodeType NodeType {
470 switch (attr_state) {
471 case AttributeState.Type:
472 case AttributeState.RuntimeType:
473 return XmlNodeType.Attribute;
474 case AttributeState.TypeValue:
475 case AttributeState.RuntimeTypeValue:
476 return XmlNodeType.Text;
483 public override string Prefix {
484 get { return String.Empty; }
487 public override ReadState ReadState {
488 get { return read_state; }
491 public override string Value {
493 switch (attr_state) {
494 case AttributeState.Type:
495 case AttributeState.TypeValue:
496 return elements.Peek ().Type;
497 case AttributeState.RuntimeType:
498 case AttributeState.RuntimeTypeValue:
499 return current_runtime_type;
501 return current_node == XmlNodeType.Text ? simple_value : String.Empty;
506 public override void Close ()
508 if (on_close != null) {
512 read_state = ReadState.Closed;
515 public override string GetAttribute (int index)
517 if (index == 0 && current_node == XmlNodeType.Element)
518 return elements.Peek ().Type;
519 else if (index == 1 && current_runtime_type != null)
520 return current_runtime_type;
521 throw new ArgumentOutOfRangeException ("index", "Index is must be either 0 or 1 when there is an explicit __type in the object, and only valid on an element on this XmlDictionaryReader");
524 public override string GetAttribute (string name)
526 if (current_node != XmlNodeType.Element)
530 return elements.Peek ().Type;
532 return current_runtime_type;
538 public override string GetAttribute (string localName, string ns)
540 if (ns == String.Empty)
541 return GetAttribute (localName);
546 public override string LookupNamespace (string prefix)
549 throw new ArgumentNullException ("prefix");
550 else if (prefix.Length == 0)
555 public override bool MoveToAttribute (string name)
557 if (current_node != XmlNodeType.Element)
561 attr_state = AttributeState.Type;
564 if (current_runtime_type == null)
566 attr_state = AttributeState.RuntimeType;
573 public override bool MoveToAttribute (string localName, string ns)
575 if (ns != String.Empty)
577 return MoveToAttribute (localName);
580 public override bool MoveToElement ()
582 if (attr_state == AttributeState.None)
584 attr_state = AttributeState.None;
588 public override bool MoveToFirstAttribute ()
590 if (current_node != XmlNodeType.Element)
592 attr_state = AttributeState.Type;
596 public override bool MoveToNextAttribute ()
598 if (attr_state == AttributeState.None)
599 return MoveToFirstAttribute ();
601 return MoveToAttribute ("__type");
604 public override bool ReadAttributeValue ()
606 switch (attr_state) {
607 case AttributeState.Type:
608 attr_state = AttributeState.TypeValue;
610 case AttributeState.RuntimeType:
611 attr_state = AttributeState.RuntimeTypeValue;
617 public override void ResolveEntity ()
619 throw new NotSupportedException ();
622 public override bool Read ()
624 switch (read_state) {
625 case ReadState.EndOfFile:
626 case ReadState.Closed:
627 case ReadState.Error:
629 case ReadState.Initial:
630 read_state = ReadState.Interactive;
631 next_element = "root";
632 current_node = XmlNodeType.Element;
638 if (content_stored) {
639 if (current_node == XmlNodeType.Element) {
640 if (elements.Peek ().Type == "null") {
641 // since null is not consumed as text content, it skips Text state.
642 current_node = XmlNodeType.EndElement;
643 content_stored = false;
646 current_node = XmlNodeType.Text;
648 } else if (current_node == XmlNodeType.Text) {
649 current_node = XmlNodeType.EndElement;
650 content_stored = false;
654 else if (current_node == XmlNodeType.EndElement) {
655 // clear EndElement state
657 if (elements.Count > 0)
658 elements.Peek ().HasContent = true;
665 attr_state = AttributeState.None;
666 // Default. May be overriden only as EndElement or None.
667 current_node = XmlNodeType.Element;
669 if (!ReadContent (false))
672 throw XmlError ("Multiple top-level content is not allowed");
676 bool TryReadString (string str)
678 for (int i = 0; i < str.Length; i ++) {
679 int ch = ReadChar ();
681 for (int j = i; j >= 0; j--)
690 bool ReadContent (bool objectValue)
692 int ch = ReadChar ();
698 bool itemMustFollow = false;
700 if (!objectValue && elements.Count > 0 && elements.Peek ().HasContent) {
702 switch (elements.Peek ().Type) {
707 itemMustFollow = true;
711 else if (ch != '}' && ch != ']')
712 throw XmlError ("Comma is required unless an array or object is at the end");
715 if (elements.Count > 0 && elements.Peek ().Type == "array")
716 next_element = "item";
717 else if (next_object_content_name != null) {
718 next_element = next_object_content_name;
719 next_object_content_name = null;
721 throw XmlError ("':' is expected after a name of an object content");
736 throw XmlError ("Invalid comma before an end of object");
738 throw XmlError ("Invalid end of object as an object content");
743 throw XmlError ("Invalid comma before an end of array");
745 throw XmlError ("Invalid end of array as an object content");
749 bool lame = LameSilverlightLiteralParser && ch != '"';
750 string s = ReadStringLiteral (lame);
751 if (!objectValue && elements.Count > 0 && elements.Peek ().Type == "object") {
760 ReadAsSimpleContent ("string", s);
766 if (TryReadString("ull")) {
767 ReadAsSimpleContent ("null", "null");
771 // the pushback for 'n' is taken care of by the
772 // default case if we're in lame silverlight literal
777 if (TryReadString ("rue")) {
778 ReadAsSimpleContent ("boolean", "true");
782 // the pushback for 't' is taken care of by the
783 // default case if we're in lame silverlight literal
788 if (TryReadString ("alse")) {
789 ReadAsSimpleContent ("boolean", "false");
793 // the pushback for 'f' is taken care of by the
794 // default case if we're in lame silverlight literal
799 if ('0' <= ch && ch <= '9') {
803 if (LameSilverlightLiteralParser) {
807 throw XmlError (String.Format ("Unexpected token: '{0}' ({1:X04})", (char) ch, (int) ch));
811 void ReadStartObject ()
813 ElementInfo ei = new ElementInfo (next_element, "object");
817 if (PeekChar () == '"') { // it isn't premise: the object might be empty
819 string s = ReadStringLiteral ();
825 current_runtime_type = ReadStringLiteral ();
827 ei.HasContent = true;
830 next_object_content_name = s;
834 void ReadStartArray ()
836 elements.Push (new ElementInfo (next_element, "array"));
839 void ReadEndObject ()
841 if (elements.Count == 0 || elements.Peek ().Type != "object")
842 throw XmlError ("Unexpected end of object");
843 current_node = XmlNodeType.EndElement;
848 if (elements.Count == 0 || elements.Peek ().Type != "array")
849 throw XmlError ("Unexpected end of array");
850 current_node = XmlNodeType.EndElement;
853 void ReadEndOfStream ()
855 if (elements.Count > 0)
856 throw XmlError (String.Format ("{0} missing end of arrays or objects", elements.Count));
857 read_state = ReadState.EndOfFile;
858 current_node = XmlNodeType.None;
861 void ReadAsSimpleContent (string type, string value)
863 elements.Push (new ElementInfo (next_element, type));
864 simple_value = value;
865 content_stored = true;
868 void ReadNumber (int ch)
870 elements.Push (new ElementInfo (next_element, "number"));
871 content_stored = true;
875 bool floating = false, exp = false;
877 StringBuilder sb = new StringBuilder ();
880 sb.Append ((char) ch);
884 if (prev == '-' && !IsNumber (ch)) // neither '.', '-' or '+' nor anything else is valid
885 throw XmlError ("Invalid JSON number");
891 throw XmlError ("Invalid JSON number token. Either 'E' or 'e' must not occur more than once");
892 if (!IsNumber (prev))
893 throw XmlError ("Invalid JSON number token. only a number is valid before 'E' or 'e'");
898 throw XmlError ("Invalid JSON number token. '.' must not occur twice");
900 throw XmlError ("Invalid JSON number token. '.' must not occur after 'E' or 'e'");
905 if (prev == 'E' || prev == 'e')
909 if (!IsNumber (ch)) {
917 if (!IsNumber (prev)) // only number is valid at the end
918 throw XmlError ("Invalid JSON number");
920 simple_value = sb.ToString ();
922 if (init == '0' && !floating && !exp && simple_value != "0")
923 throw XmlError ("Invalid JSON number");
926 bool IsNumber (int c)
928 return '0' <= c && c <= '9';
931 StringBuilder vb = new StringBuilder ();
933 string ReadStringLiteral ()
935 return ReadStringLiteral (false);
938 string ReadStringLiteral (bool endWithColon)
944 throw XmlError ("JSON string is not closed");
945 if (c == '"' && !endWithColon)
946 return vb.ToString ();
947 else if (c == ':' && endWithColon)
948 return vb.ToString ();
949 else if (c != '\\') {
950 vb.Append ((char) c);
954 // escaped expression
957 throw XmlError ("Invalid JSON string literal; incomplete escape sequence");
962 vb.Append ((char) c);
981 for (int i = 0; i < 4; i++) {
982 if ((c = ReadChar ()) < 0)
983 throw XmlError ("Incomplete unicode character escape literal");
985 if ('0' <= c && c <= '9')
986 cp += (ushort) (c - '0');
987 if ('A' <= c && c <= 'F')
988 cp += (ushort) (c - 'A' + 10);
989 if ('a' <= c && c <= 'f')
990 cp += (ushort) (c - 'a' + 10);
992 vb.Append ((char) cp);
995 throw XmlError ("Invalid JSON string literal; unexpected escape character");
1002 return reader.Peek ();
1007 int v = reader.Read ();
1017 void PushbackChar (int ch)
1019 // FIXME handle lines (and columns? ugh, how?)
1020 reader.Pushback (ch);
1023 void SkipWhitespaces ()
1026 switch (PeekChar ()) {
1039 void Expect (char c)
1041 int v = ReadChar ();
1043 throw XmlError (String.Format ("Expected '{0}' but got EOF", c));
1045 throw XmlError (String.Format ("Expected '{0}' but got '{1}'", c, (char) v));
1048 Exception XmlError (string s)
1050 return new XmlException (String.Format ("{0} ({1},{2})", s, line, column));
1053 // This reads the current element and all its content as a string,
1054 // with no processing done except for advancing the reader.
1055 public override string ReadInnerXml ()
1058 if (NodeType != XmlNodeType.Element)
1059 return base.ReadInnerXml ();
1061 StringBuilder sb = new StringBuilder ();
1062 bool isobject = elements.Peek ().Type == "object";
1063 char end = isobject ? '}' : ']';
1064 char start = isobject ? '{' : '[';
1069 // add the first child manually, it's already been read
1070 // but hasn't been processed yet
1071 if (isobject && !String.IsNullOrEmpty (next_object_content_name))
1072 sb.Append ("\"" + next_object_content_name + "\"");
1074 // keep reading until we hit the end marker, no processing is
1077 char c = (char)ReadChar ();
1083 } while (count > 0);
1085 // Replace the content we've read with an empty object so it gets
1086 // skipped on the following Read
1087 reader.Pushback (end);
1089 reader.Pushback ('"');
1090 reader.Pushback ('"');
1091 reader.Pushback (':');
1096 return sb.ToString ();