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 in 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"]);

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

No comments yet.

Leave a Comment

I respect your privacy
I don't run any trackers on this site.

Please use the share-buttons or leave comments so I know what might be worth writing about.

Thank you.
Contact