JsonReader.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Server / System / Data / Services / Serializers / JsonReader.cs / 1305376 / JsonReader.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a reader implementaion for Json format
//  
// 
// @owner  [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services.Serializers
{
    #region Namespaces. 

    using System; 
    using System.Collections; 
    using System.Diagnostics;
    using System.Globalization; 
    using System.IO;
    using System.Text;
    using System.Text.RegularExpressions;
using System.Collections.Generic; 

    #endregion Namespaces. 
 
    /// Json text reader.
    /// Does not dispose the reader, since we don't own the underlying stream. 
    internal sealed class JsonReader
    {
        /// Compiled Regex for DateTime Format.
        private static readonly Regex DateTimeFormat = new Regex(@"^/Date\((?-?[0-9]+)\)/", RegexOptions.Compiled); 

        /// Maximum recursion limit on reader. 
        private const int RecursionLimit = 200; 

        ///  Reader to reader text into  
        private readonly StreamReader reader;

        /// Depth of recursion.
        private int recursionDepth; 

        ///  
        /// Creates a new instance of Json reader which readers the json text 
        /// from the given reader
        ///  
        /// text reader from which json payload needs to be read.
        /// Does not dispose the reader, since we don't own the underlying stream.
        public JsonReader(StreamReader reader)
        { 
            Debug.Assert(reader != null, "reader != null");
            this.reader = reader; 
        } 

        ///  
        /// Converts the given value into the right type
        /// 
        /// returns the clr object instance which 
        public object ReadValue() 
        {
            this.RecurseEnter(); 
 
            object value = null;
            bool allowNull = false; 

            char ch = this.PeekNextSignificantCharacter();
            if (ch == '[')
            { 
                value = this.ReadArray();
            } 
            else if (ch == '{') 
            {
                value = this.ReadObject(); 
            }
            else if ((ch == '\'') || (ch == '"'))
            {
                bool hasLeadingSlash; 
                string s = this.ReadString(out hasLeadingSlash);
                value = s; // may be overwritten with a DateTime if ends up being a date/time 
 
                // Atlas format for date/time
                if (hasLeadingSlash) 
                {
                    Match match = DateTimeFormat.Match(s);
                    if (match.Success)
                    { 
                        string ticksStr = match.Groups["ticks"].Value;
 
                        long ticks; 
                        if (long.TryParse(ticksStr, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out ticks))
                        { 
                            // The javascript ticks start from 1/1/1970 but FX DateTime ticks start from 1/1/0001
                            DateTime dateTime = new DateTime(ticks * 10000 + JsonWriter.DatetimeMinTimeTicks, DateTimeKind.Utc);
                            value = dateTime;
                        } 
                    }
                } 
            } 
            else if (Char.IsDigit(ch) || (ch == '-') || (ch == '.'))
            { 
                value = this.ReadNumber();
            }
            else if ((ch == 't') || (ch == 'f'))
            { 
                value = this.ReadBoolean();
            } 
            else if (ch == 'n') 
            {
                this.ReadNull(); 
                allowNull = true;
            }

            if ((value == null) && (allowNull == false)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidContent); 
            } 

            this.RecurseLeave(); 

            // if there is junk data at the end of the stream ex. {...}junk
            // then an exception will be thrown.
            if (this.recursionDepth == 0) 
            {
                // at the end of the stream 
                if (this.PeekNextSignificantCharacter() != '\0') 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidContent); 
                }
            }

            return value; 
        }
 
        ///  
        /// Gets the next character from the reader. This function moves the enumerator by 1 position
        ///  
        /// the next charater from the current reader position
        private char ReadNextCharacter()
        {
            int result = this.reader.Read(); 
            if (result < 0)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidContent); 
            }
 
            Debug.Assert(result <= char.MaxValue, "result <= char.MaxValue");
            return (char)result;
        }
 
        /// 
        /// peeks the next character from the reader. This does not move the current reader position. 
        ///  
        /// the next character from the current reader position
        private char PeekNextCharacter() 
        {
            if (this.reader.EndOfStream)
            {
                return '\0'; 
            }
 
            int result = this.reader.Peek(); 
            Debug.Assert(result >= 0, "Peek must not return value < 0 since we are not at EndOfStream yet.");
            Debug.Assert(result <= char.MaxValue, "result <= char.MaxValue"); 
            return (char)result;
        }

        ///  
        /// Returns the next count characters from the reader's current position
        ///  
        /// number of characters to return 
        /// string consisting of next count characters
        private string GetCharacters(int count) 
        {
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < count; i++)
            { 
                char ch = this.ReadNextCharacter();
                stringBuilder.Append(ch); 
            } 

            return stringBuilder.ToString(); 
        }

        /// 
        /// Sets the readers position to the next significant character position 
        /// 
        /// returns the next significant character without changing the current position of the reader 
        private char PeekNextSignificantCharacter() 
        {
            char ch = this.PeekNextCharacter(); 
            while ((ch != '\0') && Char.IsWhiteSpace(ch))
            {
                this.ReadNextCharacter();
                ch = this.PeekNextCharacter(); 
            }
 
            return ch; 
        }
 
        /// 
        /// Converts the given text into an arrayList
        /// 
        /// returns the arraylist containing the list of objects 
        private ArrayList ReadArray()
        { 
            ArrayList array = new ArrayList(); 

            // Consume the '[' 
            this.ReadNextCharacter();

            while (true)
            { 
                char ch = this.PeekNextSignificantCharacter();
                if (ch == '\0') 
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidContent);
                } 

                if (ch == ']')
                {
                    this.ReadNextCharacter(); 
                    return array;
                } 
 
                if (array.Count != 0)
                { 
                    if (ch != ',')
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_MissingArrayMemberSeperator);
                    } 
                    else
                    { 
                        this.ReadNextCharacter(); 
                    }
                } 

                object item = this.ReadValue();
                array.Add(item);
            } 
        }
 
        ///  
        /// Reads the boolean value
        ///  
        /// returns the boolean value as read from the reader
        private bool ReadBoolean()
        {
            string s = this.ReadName(/* allowQuotes */ false); 

            if (s != null) 
            { 
                if (s.Equals(XmlConstants.XmlTrueLiteral, StringComparison.Ordinal))
                { 
                    return true;
                }
                else if (s.Equals(XmlConstants.XmlFalseLiteral, StringComparison.Ordinal))
                { 
                    return false;
                } 
            } 

            throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidKeyword(s)); 
        }

        /// 
        /// Reads the name string from the reader 
        /// 
        /// true, if you want to allow quotes, otherwise false 
        /// string name value 
        private string ReadName(bool allowQuotes)
        { 
            char ch = this.PeekNextSignificantCharacter();

            if ((ch == '"') || (ch == '\''))
            { 
                if (allowQuotes)
                { 
                    return this.ReadString(); 
                }
            } 
            else
            {
                StringBuilder sb = new StringBuilder();
 
                while (true)
                { 
                    ch = this.PeekNextCharacter(); 
                    if ((ch == '_') || Char.IsLetterOrDigit(ch) || ch == '$')
                    { 
                        this.ReadNextCharacter();
                        sb.Append(ch);
                    }
                    else 
                    {
                        return sb.ToString(); 
                    } 
                }
            } 

            return null;
        }
 
        /// 
        /// Reads the null literal from the reader 
        ///  
        private void ReadNull()
        { 
            string s = this.ReadName(/* allowQuotes */ false);

            if (s == null)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_MissingMemberName);
            } 
            else if (!s.Equals("null", StringComparison.Ordinal)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidKeyword(s)); 
            }
        }

        /// Reads the number from the reader. 
        /// reads the clr number object
        private object ReadNumber() 
        { 
            char ch = this.ReadNextCharacter();
 
            StringBuilder sb = new StringBuilder();
            sb.Append(ch);
            while (true)
            { 
                ch = this.PeekNextSignificantCharacter();
 
                if (Char.IsDigit(ch) || (ch == '.') || (ch == 'E') || (ch == 'e') || (ch == '-') || (ch == '+')) 
                {
                    this.ReadNextCharacter(); 
                    sb.Append(ch);
                }
                else
                { 
                    break;
                } 
            } 

            string s = sb.ToString(); 
            Double doubleValue;
            int intValue;

            // We will first try and convert this int32. If this succeeds, great. Otherwise, we will try 
            // and convert this into a double.
            if (Int32.TryParse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out intValue)) 
            { 
                return intValue;
            } 
            else if (Double.TryParse(s, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out doubleValue))
            {
                return doubleValue;
            } 

            throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidContent); 
        } 

        ///  
        /// Reads the object from the reader
        /// 
        /// returns hashtable containing the list of property names and values
        private JsonObjectRecords ReadObject() 
        {
            JsonObjectRecords record = new JsonObjectRecords(); 
 
            // Consume the '{'
            this.ReadNextCharacter(); 

            while (true)
            {
                char ch = this.PeekNextSignificantCharacter(); 
                if (ch == '\0')
                { 
                    // Unterminated Object literal 
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidContent);
                } 

                if (ch == '}')
                {
                    this.ReadNextCharacter(); 
                    return record;
                } 
 
                if (record.Count != 0)
                { 
                    if (ch != ',')
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_MissingMemberSeperator);
                    } 
                    else
                    { 
                        this.ReadNextCharacter(); 
                    }
                } 

                string name = this.ReadName(/* allowQuotes */ true);
                if (String.IsNullOrEmpty(name))
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidJsonNameSpecifiedOrExtraComma);
                } 
 
                ch = this.PeekNextSignificantCharacter();
 
                // Unexpected name/value pair syntax in object literal
                if (ch != ':')
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_MissingNameValueSeperator(name)); 
                }
                else 
                { 
                    this.ReadNextCharacter();
                } 

                object item = this.ReadValue();

                record.Add(name, item); 
            }
        } 
 
        /// 
        /// Read the string value from the reader 
        /// 
        /// returns the string value read from the reader
        private string ReadString()
        { 
            bool dummy;
            return this.ReadString(out dummy); 
        } 

        ///  
        /// Read the string value from the reader
        /// 
        /// out parameter indicating whether the string has a leading slash or not
        /// returns the string value read from the reader 
        private string ReadString(out bool hasLeadingSlash)
        { 
            char endQuoteCharacter = this.ReadNextCharacter(); 
            char ch = this.ReadNextCharacter();
 
            hasLeadingSlash = (ch == '\\') ? true : false;
            StringBuilder sb = new StringBuilder();

            while (true) 
            {
                if (ch == '\\') 
                { 
                    ch = this.ReadNextCharacter();
 
                    // From 4627, section 2.5: Strings, here's the list of characters that we should be escaping
                    if (ch == 'u')
                    {
                        string unicodeSequence = this.GetCharacters(4); 
                        Debug.Assert(unicodeSequence != null, "unicodeSequence != null");
                        ch = (char)Int32.Parse(unicodeSequence, NumberStyles.HexNumber, CultureInfo.InvariantCulture); 
                        sb.Append(ch); 
                    }
                    else if (ch == 'b') 
                    {
                        sb.Append('\b');
                    }
                    else if (ch == 'f') 
                    {
                        sb.Append('\f'); 
                    } 
                    else if (ch == 'n')
                    { 
                        sb.Append('\n');
                    }
                    else if (ch == 'r')
                    { 
                        sb.Append('\r');
                    } 
                    else if (ch == 't') 
                    {
                        sb.Append('\t'); 
                    }
                    else if (ch == '\\' || ch == '\"' || ch == '/' || ch == '\'')
                    {
                        sb.Append(ch); 
                    }
                    else 
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidJsonUnrecognizedEscapeSequence);
                    } 
                }
                else
                if (ch == endQuoteCharacter)
                { 
                    return sb.ToString();
                } 
                else 
                {
                    sb.Append(ch); 
                }

                ch = this.ReadNextCharacter();
            } 
        }
 
        /// Marks the fact that a recursive method was entered, and checks that the depth is allowed. 
        private void RecurseEnter()
        { 
            WebUtil.RecurseEnter(RecursionLimit, ref this.recursionDepth);
        }

        /// Marks the fact that a recursive method is leaving.. 
        private void RecurseLeave()
        { 
            WebUtil.RecurseLeave(ref this.recursionDepth); 
        }
 
        /// 
        /// Represent one Json Object.
        /// 
        /// This class honors the original ordering of the inner elements of the json object. 
        public class JsonObjectRecords
        { 
            ///  
            /// A list of keys in the object
            ///  
            private List orderedKeys;

            /// 
            /// The actual storage of key-value pair 
            /// 
            private Dictionary entries; 
 
            /// 
            /// Constructor 
            /// 
            public JsonObjectRecords()
            {
                this.orderedKeys = new List(); 
                this.entries = new Dictionary(EqualityComparer.Default);
            } 
 
            /// 
            /// Number of elements in this object 
            /// 
            public int Count
            {
                get { return this.orderedKeys.Count; } 
            }
 
            ///  
            /// A list of keys in the object, in the order which they are deserialized
            ///  
            public List OrderedKeys
            {
                get { return this.orderedKeys; }
            } 

            ///  
            /// The actual storage of key-value pair 
            /// 
            public Dictionary Entries 
            {
                get { return this.entries; }
            }
 
            /// 
            /// Add a new element into the object 
            ///  
            /// Key
            /// Value 
            public void Add(String key, Object value)
            {
                // Json Object used to be a hashtable
                // It will automatically resolve duplicated keys 
                // however, for that scenario, we must not update the order table
                if (!this.entries.ContainsKey(key)) 
                { 
                    this.orderedKeys.Add(key);
                } 

                this.entries[key] = value;
            }
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK