/*
* Copyright (c) 2012-2016 Daniele Bartolini and individual contributors.
* License: https://github.com/taylor001/crown/blob/master/LICENSE
*/
/*
* Public Domain Niklas Frykholm
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace Crown
{
///
/// Provides functions for encoding and decoding files in the JSON format.
///
public class JSON
{
///
/// Encodes the hashtable t in the JSON format. The hash table can
/// contain, numbers, bools, strings, ArrayLists and Hashtables.
///
public static string Encode(object t)
{
StringBuilder builder = new StringBuilder();
Write(t, builder, 0);
return builder.ToString();
}
///
/// Decodes a JSON bytestream into a hash table with numbers, bools, strings,
/// ArrayLists and Hashtables.
///
public static object Decode(byte[] sjson)
{
int index = 0;
return Parse(sjson, ref index);
}
///
/// Convenience function for loading a file.
///
public static Hashtable Load(string path)
{
System.IO.FileStream fs = System.IO.File.Open(path, System.IO.FileMode.Open, System.IO.FileAccess.Read);
byte[] bytes = new byte[fs.Length];
fs.Read(bytes, 0, bytes.Length);
fs.Close();
return Decode(bytes) as Hashtable;
}
///
/// Convenience function for saving a file.
///
public static void Save(Hashtable h, string path)
{
string s = Encode(h);
System.IO.FileStream fs = System.IO.File.Open(path, System.IO.FileMode.Create);
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(s);
fs.Write(bytes, 0, bytes.Count());
fs.Close();
}
static void WriteNewLine(StringBuilder builder, int indentation)
{
builder.Append('\n');
for (int i = 0; i < indentation; ++i)
builder.Append('\t');
}
static void Write(object o, StringBuilder builder, int indentation)
{
if (o == null)
builder.Append("null");
else if (o is Boolean && (bool)o == false)
builder.Append("false");
else if (o is Boolean)
builder.Append("true");
else if (o is int)
builder.Append((int)o);
else if (o is float)
builder.Append((float)o);
else if (o is double)
builder.Append((double)o);
else if (o is string)
WriteString((String)o, builder);
else if (o is ArrayList)
WriteArray((ArrayList)o, builder, indentation);
else if (o is Hashtable)
WriteObject((Hashtable)o, builder, indentation);
else
throw new ArgumentException("Unknown object");
}
static void WriteString(String s, StringBuilder builder)
{
builder.Append('"');
for (int i = 0; i < s.Length; ++i)
{
Char c = s[i];
if (c == '"' || c == '\\')
{
builder.Append('\\');
builder.Append(c);
}
else if (c == '\n')
{
builder.Append('\\');
builder.Append('n');
}
else
builder.Append(c);
}
builder.Append('"');
}
static void WriteArray(ArrayList a, StringBuilder builder, int indentation)
{
bool write_comma = false;
builder.Append('[');
foreach (object item in a)
{
if (write_comma)
builder.Append(',');
WriteNewLine(builder, indentation + 1);
Write(item, builder, indentation + 1);
write_comma = true;
}
WriteNewLine(builder, indentation);
builder.Append(']');
}
static void WriteObject(Hashtable t, StringBuilder builder, int indentation)
{
builder.Append('{');
bool write_comma = false;
foreach (DictionaryEntry de in t)
{
if (write_comma)
builder.Append(", ");
WriteNewLine(builder, indentation);
Write(de.Key, builder, indentation);
builder.Append(" : ");
Write(de.Value, builder, indentation);
write_comma = true;
}
WriteNewLine(builder, indentation);
builder.Append('}');
}
static bool AtEnd(byte[] json, ref int index)
{
SkipWhitespace(json, ref index);
return (index >= json.Length);
}
static void SkipWhitespace(byte[] json, ref int index)
{
while (index < json.Length)
{
byte c = json[index];
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',')
++index;
else
break;
}
}
static void Consume(byte[] json, ref int index, String consume)
{
SkipWhitespace(json, ref index);
for (int i = 0; i < consume.Length; ++i)
{
if (json[index] != consume[i])
throw new FormatException();
++index;
}
}
static object Parse(byte[] json, ref int index)
{
byte c = Next(json, ref index);
if (c == '{')
return ParseObject(json, ref index);
else if (c == '[')
return ParseArray(json, ref index);
else if (c == '"')
return ParseString(json, ref index);
else if (c == '-' || c >= '0' && c <= '9')
return ParseNumber(json, ref index);
else if (c == 't')
{
Consume(json, ref index, "true");
return true;
}
else if (c == 'f')
{
Consume(json, ref index, "false");
return false;
}
else if (c == 'n')
{
Consume(json, ref index, "null");
return null;
}
else
throw new FormatException();
}
static byte Next(byte[] json, ref int index)
{
SkipWhitespace(json, ref index);
return json[index];
}
static Hashtable ParseObject(byte[] json, ref int index)
{
Hashtable ht = new Hashtable();
Consume(json, ref index, "{");
SkipWhitespace(json, ref index);
while (Next(json, ref index) != '}')
{
String key = ParseString(json, ref index);
Consume(json, ref index, ":");
if (key.EndsWith("_binary"))
ht[key] = ParseBinary(json, ref index);
else
ht[key] = Parse(json, ref index);
}
Consume(json, ref index, "}");
return ht;
}
static ArrayList ParseArray(byte[] json, ref int index)
{
ArrayList a = new ArrayList();
Consume(json, ref index, "[");
while (Next(json, ref index) != ']')
{
object value = Parse(json, ref index);
a.Add(value);
}
Consume(json, ref index, "]");
return a;
}
static byte[] ParseBinary(byte[] json, ref int index)
{
List s = new List();
Consume(json, ref index, "\"");
while (true)
{
byte c = json[index];
++index;
if (c == '"')
break;
else if (c != '\\')
s.Add(c);
else
{
byte q = json[index];
++index;
if (q == '"' || q == '\\' || q == '/')
s.Add(q);
else if (q == 'b') s.Add((byte)'\b');
else if (q == 'f') s.Add((byte)'\f');
else if (q == 'n') s.Add((byte)'\n');
else if (q == 'r') s.Add((byte)'\r');
else if (q == 't') s.Add((byte)'\t');
else if (q == 'u')
{
throw new FormatException();
}
else
{
throw new FormatException();
}
}
}
return s.ToArray();
}
static String ParseString(byte[] json, ref int index)
{
return new UTF8Encoding().GetString(ParseBinary(json, ref index));
}
static Double ParseNumber(byte[] json, ref int index)
{
int end = index;
while ("0123456789+-.eE".IndexOf((char)json[end]) != -1)
++end;
byte[] num = new byte[end - index];
Array.Copy(json, index, num, 0, num.Length);
index = end;
String numstr = new UTF8Encoding().GetString(num);
return Double.Parse(numstr);
}
}
}