Click here to Skip to main content
15,886,110 members
Articles / Programming Languages / C#

Small LINQ to JSON Library

Rate me:
Please Sign up or sign in to vote.
4.96/5 (28 votes)
6 Dec 2011CPOL13 min read 81K   2K   79  
A small LinqToJSON library in C#, and how it works
using System;
using System.IO;
using System.Text;

namespace Ranslant.JSON.Linq
{
    public class JDocument : JObject
    {
        public JDocument()
        { }

        /// <summary>
        /// Creates a new JDocument instance and copies the data from jObject
        /// </summary>
        /// <param name="jObject">source of the data. The data is copied.</param>
        public JDocument(JObject jObject)
        {
            foreach (JObjectMember jom in jObject.GetMembers())
                Add(jom);
        }
        
        /// <summary>
        /// Creates a new JDocument instance and fills it with the jObjectMembers
        /// </summary>
        /// <param name="jObjectMembers">a comma separated list of JObjectMember</param>
        public JDocument(params JObjectMember[] jObjectMembers): base(jObjectMembers)
        {
        }
        
        /// <summary>
        /// Loads JSON data from a file. An exception is thrown if something goes wrong. The file has to contain nothing but JSON data.
        /// </summary>
        /// <param name="uri">URI (path) to the file. Must contain only JSON Data</param>
        /// <param name="encoding">specific encoding (default is UFT8Encoding)</param>
        /// <returns></returns>
        public static JDocument Load(string uri, Encoding encoding)
        {
            if (uri.Length == 0)
                throw new Exception("Load: empty file name");

            using (FileStream stream = new FileStream(uri, FileMode.Open))
            {
                JDocument doc = Load(stream, encoding);
                stream.Close();
                return doc;
            }
        }

        /// <summary>
        /// Loads JSON data from a file. An exception is thrown if something goes wrong. The file has to contain nothing but JSON data.
        /// The encoding is UTF8Encoding.
        /// </summary>
        /// <param name="uri">URI (path) to the file. Must contain only JSON Data</param>
        /// <returns></returns>
        public static JDocument Load(string uri)
        {
            return Load(uri, new UTF8Encoding());
        }

        /// <summary>
        /// Loads JSON data from a stream. An exception is thrown if something goes wrong. The stream has to contain nothing but JSON data from its current position to its end.
        /// </summary>
        /// <param name="stream">stream from where to read. The stream object is allowed to not support stream.Length</param>
        /// <param name="encoding">specific encoding (default is UTF8Encoding). If you provide 'null' as parameter for the encoding, then Load will try to detect the encoding type based on the ByteOrderMark (currently detected: UTF16(BE and LE), UTF8 and ASCII)</param>
        /// <returns></returns>
        public static JDocument Load(Stream stream, Encoding encoding)
        {
            if(stream == null)
                throw new ArgumentNullException("stream");

            if(!stream.CanRead)
                throw new Exception("stream is not readable");

            byte[] bytes;

            try
            {
                int length = (int)stream.Length;
                bytes = new byte[length];
                int read = stream.Read(bytes, 0, length);
                if(read != length)
                    throw new IOException("stream could not be fully read");
            }
            catch (NotSupportedException)
            {
                // it may happen that the Stream derived type does not support stream.Length
                // in this case we have to read the stream almost byte by byte
                
                const int sizeOfBuffer = 1024;
                
                byte[] buffer = new byte[sizeOfBuffer]; // let's read the stream 1k at a time
                int offset = 0;
                int readBytes = stream.Read(buffer, offset, sizeOfBuffer);
                int totalRead = readBytes;

                bytes = new byte[readBytes];

                while (readBytes > 0)
                {
                    buffer.CopyTo(bytes, offset);

                    offset += readBytes;

                    readBytes = stream.Read(buffer, offset, sizeOfBuffer);
                    totalRead += readBytes;

                    if (readBytes > 0)
                        Array.Resize(ref bytes, totalRead);
                }
            }

            // Load tries to detect the encoding with the BOM
            if (encoding == null)
            {
                encoding = Utilities.TryDetectEncoding(bytes);
            }

            string text = encoding.GetString(bytes);
            return JDocument.Parse(text);
        }

        /// <summary>
        /// Loads JSON data from a stream. An exception is thrown if something goes wrong. The stream has to contain nothing but JSON data from its current position to its end.
        /// The encoding is UTF8Encoding.
        /// </summary>
        /// <param name="stream">stream from where to read</param>
        /// <returns></returns>
        public static JDocument Load(Stream stream)
        {
            return Load(stream, new UTF8Encoding());
        }

        /// <summary>
        /// Parses the given text. Only JSON data is expected.
        /// An exception is thrown if something goes wrong, for instance if the JSON data is not properly formatted
        /// </summary>
        /// <param name="text">raw text of the whole JSON data</param>
        /// <returns></returns>
        public static JDocument Parse(string text)
        {
            if (text.Length == 0)
                throw new Exception("Parse: data is empty");

            // When .NET Framework encodes from UTF to UTF (no matter which one), then it adds a ByteOrderMark
            // UTF16: http://en.wikipedia.org/wiki/UTF-16#Byte_order_encoding_schemes
            // since C# encodes strings as UTF 16 LE, we may need to remove the BOM
            if (text[0] == 0xFEFF)
            {
                text = text.Remove(0, 1);

                if (text.Length == 0)
                    throw new Exception("Parse: data is empty");
            }

            JParser parser = new JParser();
            JDocument jDoc = parser.ParseDocument(text);

            return jDoc;
        }

        /// <summary>
        /// Write the JDocument as JObject to the stream. The stream is not closed.
        /// An exception is thrown if something goes wrong. Encoding is UTF8Encoding.
        /// </summary>
        /// <param name="stream">stream where to write</param>
        public void Save(Stream stream)
        {
            Save(stream, new UTF8Encoding(), true);
        }
        
        /// <summary>
        /// Write the JDocument as JObject to the stream. The stream is not closed.
        /// An exception is thrown if something goes wrong.
        /// </summary>
        /// <param name="stream">stream where to write</param>
        /// <param name="encoding">specific encoding (default is UTF8Encoding)</param>
        /// <param name="addByteOrderMark">whether or not the Byte Order Mark has to be added to the file</param>
        public void Save(Stream stream, Encoding encoding, bool addByteOrderMark)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");

            if(encoding == null)
                throw new ArgumentNullException("encoding");

            if(!stream.CanWrite)
                throw new IOException("stream is not writable");

            byte[] bytes = encoding.GetBytes(this.ToString(1));

            if (addByteOrderMark)
            {
                if (encoding is UTF8Encoding || encoding is UnicodeEncoding)
                {
                    byte[] byteOrderMark = encoding.GetPreamble();
                    stream.Write(byteOrderMark, 0, byteOrderMark.Length);
                }
            }
            stream.Write(bytes, 0, bytes.Length);

            // the stream is left open, it is not this method's job to close it.
        }

        /// <summary>
        /// Saves the JSON data to a file. Encoding is UTF8Encoding.
        /// </summary>
        /// <param name="uri">URI (path) of the file. If it already exists, it will be overwritten</param>
        public void Save(string uri)
        {
            Save(uri, new UTF8Encoding(), true);
        }

        /// <summary>
        /// Saves the JSON data to a file.
        /// </summary>
        /// <param name="uri">URI (path) of the file. If it already exists, it will be overwritten</param>
        /// <param name="encoding">specific encoding (default is UTF8Encoding)</param>
        /// <param name="addByteOrderMark">whether or not the Byte Order Mark has to be added to the file</param>
        public void Save(string uri, Encoding encoding, bool addByteOrderMark)
        {
            using (FileStream writer = new FileStream(uri, FileMode.Create))
            {
                this.Save(writer, encoding, addByteOrderMark);
                writer.Close();
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer IPG
Germany Germany
since 2010: C# with WPF
since 2002: C++ (MFC / QT)
since 1995: C, Java, Pascal


"if a drummer can do it, anobody can" - Bruce Dickinson

Comments and Discussions