A small and elegant JSON implementation in C#
14.06.2014 [Software]
I recently needed a JSON implementation to serve as a transport layer for RPC for a project I am working on at the moment and JSON.net doesn't work with the Mono-version that comes bundled with Unity and would be absolute overkill for my purposes. So I wrote my own small JSON "library". Well, calling it a library is maybe a little too much because at the end it's only one dynamic data type that can hold the types supported by JSON, but it generates valid JSON and also does a very decent job when parsing JSON objects. And all of that in 500 lines of code.

The usage is fairly simply. Some examples:
Variant v = Variant.NewObject();
v.Add("func", "someString");
v["arg"] = 1.2345f;

Variant v2 = Variant.NewArray();
v2.Add(12345);
v2.Add(false);

foreach (Variant x in v2) {
    Console.Out.WriteLine( x.PrettyPrint() );
}

v.Add("someArray", v2);

Console.Out.WriteLine(v);

Variant v3 = Variant.Parse("{\"xyz\":0.981f}");
Console.Out.WriteLine(v3["xyz"]);

File: Variant.cs
#region License
// Copyright (c) 2014 Moritz Molch <mail@moritzmolch.com>
//
// Permission is hereby granted to do with this file whatever
// you want to with the only exception that this copyright
// and permission notice shall never be altered or removed.
#endregion

using System.Collections.Generic;
using System.Collections;

namespace MoritzMolch.Json
{
public class Variant : IEnumerable {
    protected Types m_type = Types.Null;
    
    protected string m_string;
    protected int m_int;
    protected float m_float;
    protected bool m_bool;
    protected List<Variant> m_array;
    protected Dictionary<string, Variant> m_object;

    protected enum Types {
        String,
        Int,
        Float,
        Bool,
        Array,
        Object,
        Null,
        Error
    }
    
    #region IEnumerable implementation
    public IEnumerator GetEnumerator()
    {
        switch (m_type) {
        case Types.Array:
            return m_array.GetEnumerator();
        case Types.Object:
            return m_object.GetEnumerator();
        default:
            return new IEnumeratorClass(this) as IEnumerator;

        }
    }
    #endregion
    
    #region IEnumerator implementation
    protected class IEnumeratorClass : IEnumerator 
    {
        Variant value;
        bool hasNext;
        public IEnumeratorClass( Variant value )
        {
            this.value = value;
            hasNext = true;
        }
        public bool MoveNext() { return hasNext; }
        public void Reset() { hasNext = true; }
        public object Current { get { hasNext = false; return value; } }
    }
    #endregion
    
    public static Variant NewError( string message = "An error occurred" )
    {
        Variant outp = new Variant();
        outp.m_type = Types.Error;
        outp.m_string = message;
        return outp;
    }
    
    public bool isError
    {
        get {
            return (m_type == Types.Error);
        }
    }
    
    public static Variant NewObject()
    {
        Variant outp = new Variant();
        outp.m_type = Types.Object;
        outp.m_object = new Dictionary<string, Variant>();
        return outp;
    }
    
    public static Variant NewArray()
    {
        Variant outp = new Variant();
        outp.m_type = Types.Array;
        outp.m_array = new List<Variant>();
        return outp;
    }
    
    public Dictionary<string, Variant> Add(string key, Variant value)
    {
        if (m_type != Types.Object)
            return null;

        m_object.Add(key, value);
        return m_object;
    }
    
    public List<Variant> Add(Variant value)
    {
        if (m_type != Types.Array)
            return null;

        m_array.Add(value);
        return m_array;
    }
    
    public Variant this[string key]
    {
        get {
            if (m_type != Types.Object)
                return null;

            return m_object[key];
        }
        set {
            if (m_type != Types.Object)
                return;

            m_object[key] = value;
        }
    }
    
    public int Count
    {
        get {
            if (m_type == Types.Array)
                return m_array.Count;
            if (m_type == Types.Object)
                return m_object.Count;
            return 1;
        }
    }
    
    public Variant this[int num]
    {
        get {
            if (m_type == Types.Array)
                return m_array[num];
            return this;
        }
    }

    public static implicit operator Variant(string value)
    {
        Variant outp = new Variant();
        outp.m_type = Types.String;
        outp.m_string = value;
        return outp;
    }
    public static implicit operator string(Variant value)
    {
        switch (value.m_type) {
        case Types.String:
            return value.m_string;
        case Types.Int:
            return value.m_int.ToString();
        case Types.Float:
            return value.m_float.ToString();
        case Types.Bool:
            if (value.m_bool) return "true";
            else return "false";
        case Types.Array:
            return value.ToString();
        case Types.Object:
            return value.ToString();
        case Types.Error:
            return value.m_string;

        }
        return default(string);
    }
    
    public static implicit operator Variant(int value)
    {
        Variant outp = new Variant();
        outp.m_type = Types.Int;
        outp.m_int = value;
        return outp;
    }
    public static implicit operator int(Variant value)
    {
        switch (value.m_type) {
        case Types.String:
            return int.Parse(value.m_string);
        case Types.Int:
            return value.m_int;
        case Types.Float:
            return (int)value.m_float;
        case Types.Bool:
            if (value.m_bool) return 0;
            else return 1;
        }
        return default(int);
    }
    
    public static implicit operator Variant(float value)
    {
        Variant outp = new Variant();
        outp.m_type = Types.Float;
        outp.m_float = value;
        return outp;
    }
    public static implicit operator float(Variant value)
    {
        switch (value.m_type) {
        case Types.String:
            return float.Parse(value.m_string);
        case Types.Int:
            return (float)value.m_int;
        case Types.Float:
            return value.m_float;
        case Types.Bool:
            if (value.m_bool) return 0f;
            else return 1f;
        }
        return default(float);
    }
    
    public static implicit operator Variant(bool value)
    {
        Variant outp = new Variant();
        outp.m_type = Types.Bool;
        outp.m_bool = value;
        return outp;
    }
    public static implicit operator bool(Variant value)
    {
        switch (value.m_type) {
        case Types.String:
            return bool.Parse(value.m_string);
        case Types.Int:
            return (value.m_int == 0);
        case Types.Float:
            return (value.m_float == 0f);
        case Types.Bool:
            return value.m_bool;
        }
        return default(bool);
    }
    
    public static implicit operator Variant(List<Variant> value)
    {
        Variant outp = new Variant();
        outp.m_type = Types.Array;
        outp.m_array = value;
        return outp;
    }
    public static implicit operator List<Variant>(Variant value)
    {
        return value.m_array;
    }
    
    public static implicit operator Variant(Variant[] value)
    {
        Variant outp = new Variant();
        outp.m_array = new List<Variant>();
        outp.m_type = Types.Array;
        foreach (Variant v in value) {
            outp.m_array.Add(v);
        }
        return outp;
    }
    public static implicit operator Variant[](Variant value)
    {
        return value.m_array.ToArray();
    }
    
    public static implicit operator Variant(Dictionary<string, Variant> value)
    {
        Variant outp = new Variant();
        outp.m_type = Types.Object;
        outp.m_object = value;
        return outp;
    }
    public static implicit operator Dictionary<string, Variant>(Variant value)
    {
        return value.m_object;
    }
    
    public override string ToString()
    {
        switch (m_type) {

        case Types.Array: {
            string outp = "[";
            int i = m_array.Count;
            foreach (Variant vari in m_array) {
                i--;
                if (vari.m_type == Types.String)
                    outp += "\""+vari.ToString()+"\"";
                else
                    outp += vari.ToString();
                if (i > 0) outp += ",";
            }
            outp += "]";
            return outp;
        }
        
        case Types.Object: {
            string outp = "{";
            int i = m_object.Count;
            foreach(KeyValuePair<string, Variant> entry in m_object) {
                i--;
                if (entry.Value.m_type == Types.String)
                    outp += "\""+entry.Key+"\":\""+entry.Value.ToString()+"\"";
                else
                    outp += "\""+entry.Key+"\":"+entry.Value.ToString();
                if (i > 0) outp += ",";
            }
            outp += "}";
            return outp;
        }
        
        default:
            return this;
        }
    }
    
    public string PrettyPrint(int depth = 0)
    {
        switch (m_type) {

        case Types.Array: {
            string outp = "[\n";
            int i = m_array.Count;
            foreach (Variant vari in m_array) {
                i--;
                for (int d=0; d<=depth; d++) outp += "\t";
                if (vari.m_type == Types.String)
                    outp += "\""+vari.PrettyPrint(depth+1)+"\"";
                else
                    outp += vari.PrettyPrint(depth+1);
                if (i > 0) outp += ",\n";
            }
            outp += "\n";
            for (int d=0; d<depth; d++) outp += "\t";
            outp += "]";
            return outp;
        }
        
        case Types.Object: {
            string outp = "{\n";
            int i = m_object.Count;
            foreach(KeyValuePair<string, Variant> entry in m_object) {
                i--;
                for (int d=0; d<=depth; d++) outp += "\t";
                if (entry.Value.m_type == Types.String)
                    outp += "\""+entry.Key+"\" : \""+entry.Value.PrettyPrint(depth+1)+"\"";
                else
                    outp += "\""+entry.Key+"\" : "+entry.Value.PrettyPrint(depth+1);
                if (i > 0) outp += ",\n";
            }
            outp += "\n";
            for (int d=0; d<depth; d++) outp += "\t";
            outp += "}";
            return outp;
        }
        
        default:
            return this;
        }
    }
    
    public static Variant Parse(string json)
    {
        json = json.Trim();
        
        if ((json[0] == '"') && (json[json.Length - 1] == '"')) {
            return json.Substring(1, json.Length - 2);
        }
        
        if ((json[0] == '{') && (json[json.Length - 1] == '}')) {
            Variant outp = new Dictionary<string, Variant>();
            Stack<char> currElem = new Stack<char>();
            int lastParsePos = 0;
            int currPos = 0;
            bool omitNext = false;
            Variant key = null;
            foreach (char c in json) {
                if (omitNext) {
                    currPos++;
                    omitNext = false;
                    continue;
                }
                switch (c) {
                    case '{':
                        currElem.Push('{');
                        break;
                    case '}':
                        if (currElem.Count == 1) {
                            outp.Add(key, Variant.Parse(json.Substring(lastParsePos + 1, currPos - lastParsePos - 1)));
                            lastParsePos = currPos;
                        }
                        currElem.Pop();
                        break;
                    case '[':
                        currElem.Push('[');
                        break;
                    case ']':
                        currElem.Pop();
                        break;
                    case ',':
                        if (currElem.Count == 1) {
                            outp.Add(key, Variant.Parse(json.Substring(lastParsePos + 1, currPos - lastParsePos - 1)));
                            lastParsePos = currPos;
                        }
                        break;
                    case ':':
                        if (currElem.Count == 1) {
                            key = Variant.Parse(json.Substring(lastParsePos + 1, currPos - lastParsePos - 1));
                            lastParsePos = currPos;
                        }
                        break;
                    case '"':
                        if (currElem.Peek() == '"') {
                            currElem.Pop();
                        } else {
                            currElem.Push('"');
                        }
                        break;
                    case '\\':
                        if (currElem.Peek() == '"') {
                            omitNext = true;
                        }
                        break;
                }
                currPos++;
            }
            return outp;
        }
        
        if ((json[0] == '[') && (json[json.Length - 1] == ']')) {
            Variant outp = new List<Variant>();
            Stack<char> currElem = new Stack<char>();
            int lastParsePos = 0;
            int currPos = 0;
            bool omitNext = false;
            foreach (char c in json) {
                if (omitNext) {
                    currPos++;
                    omitNext = false;
                    continue;
                }
                switch (c) {
                    case '[':
                        currElem.Push('[');
                        break;
                    case ']':
                        if (currElem.Count == 1) {
                            outp.Add(Variant.Parse(json.Substring(lastParsePos + 1, currPos - lastParsePos - 1)));
                            lastParsePos = currPos;
                        }
                        currElem.Pop();
                        break;
                    case '{':
                        currElem.Push('{');
                        break;
                    case '}':
                        currElem.Pop();
                        break;
                        
                    case ',':
                        if (currElem.Count == 1) {
                            outp.Add(Variant.Parse(json.Substring(lastParsePos + 1, currPos - lastParsePos - 1)));
                            lastParsePos = currPos;
                        }
                        break;
                    case '"':
                        if (currElem.Peek() == '"') {
                            currElem.Pop();
                        } else {
                            currElem.Push('"');
                        }
                        break;
                    case '\\':
                        if (currElem.Peek() == '"') {
                            omitNext = true;
                        }
                        break;
                }
                currPos++;
            }
            return outp;
        }
        
        {
            int x;
                if (int.TryParse(json, out x))
                return x;
        }
        {    
            float x;
                if (float.TryParse(json, out x))
                return x;
        }
        {    
            bool x;
            if (bool.TryParse(json, out x))
                return x;
        }
        return null;
    }
}
} // namespace MoritzMolch.Json