Created
August 28, 2012 12:22
-
-
Save kamyar1979/3497617 to your computer and use it in GitHub Desktop.
A class for deserializing a single JSON string into a C# on-the-fly object.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace DynamicJson | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Dynamic; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
public static class Json | |
{ | |
public static dynamic CreateObject(string json) | |
{ | |
var typeHash = json.GetHashCode(); | |
var re = new Regex(@"\[?{?(.*)}?\]?"); | |
Match match = re.Match(json); | |
if (!match.Success) | |
{ | |
throw new ArgumentException("Invalid JSON string"); | |
} | |
var typeBuilderStack = new Stack<DynamicTypeBuilder>(); | |
var nameStack = new Stack<string>(); | |
var valueStack = new Stack<Dictionary<string, object>>(); | |
var currentValues = new Dictionary<string, object>(); | |
var currentArray = new List<object>(); | |
DynamicTypeBuilder currentTypeBuilder = null; | |
string name = null; | |
var nameBuilder = new StringBuilder(); | |
var valueBuilder = new StringBuilder(); | |
bool isValue = false; | |
bool isArray = false; | |
object result = null; | |
foreach (var chr in json) | |
{ | |
switch (chr) | |
{ | |
case '[': | |
isArray |= true; | |
break; | |
case ']': | |
isArray &= false; | |
if (currentTypeBuilder != null) | |
{ | |
currentTypeBuilder.AddProperty(name, typeof(object[])); | |
} | |
else | |
{ | |
result = currentArray.ToArray(); | |
} | |
break; | |
case '{': | |
if (isValue) | |
{ | |
nameStack.Push(name); | |
typeBuilderStack.Push(currentTypeBuilder); | |
valueStack.Push(currentValues); | |
currentValues = new Dictionary<string, object>(); | |
currentTypeBuilder = new DynamicTypeBuilder(string.Concat("anonym_", typeHash, '_', typeBuilderStack.Count)); | |
isValue &= false; | |
} | |
else | |
{ | |
currentTypeBuilder = new DynamicTypeBuilder(string.Concat("anonym_", typeHash)); | |
} | |
break; | |
case '}': | |
if (isValue) | |
{ | |
var value = Json.ParseItem(valueBuilder.ToString()); | |
currentTypeBuilder.AddProperty(name, value.GetType()); | |
currentValues[name] = value; | |
valueBuilder.Clear(); | |
isValue &= false; | |
} | |
var type = currentTypeBuilder.CreateType(); | |
var instance = Activator.CreateInstance(type); | |
foreach (var item in currentValues) | |
{ | |
type.GetProperty(item.Key).SetValue(instance, item.Value, null); | |
} | |
if (typeBuilderStack.Count > 0) | |
{ | |
currentValues = valueStack.Pop(); | |
name = nameStack.Pop(); | |
currentValues[name] = instance; | |
currentTypeBuilder = typeBuilderStack.Pop(); | |
currentTypeBuilder.AddProperty(name, type); | |
} | |
else | |
{ | |
result = instance; | |
} | |
break; | |
case ':': | |
name = nameBuilder.ToString(); | |
nameBuilder.Clear(); | |
isValue |= true; | |
break; | |
case ',': | |
if (isArray) | |
{ | |
if (currentTypeBuilder == null) | |
{ | |
var value = Json.ParseItem(valueBuilder.ToString()); | |
currentArray.Add(value); | |
valueBuilder.Clear(); | |
} | |
else | |
{ | |
} | |
} | |
else if (isValue) | |
{ | |
var value = Json.ParseItem(valueBuilder.ToString()); | |
currentTypeBuilder.AddProperty(name, value.GetType()); | |
currentValues[name] = value; | |
valueBuilder.Clear(); | |
isValue &= false; | |
} | |
break; | |
case ' ': | |
case '\t': | |
if (nameBuilder.Length > 0) | |
{ | |
throw new ArgumentException("Property names can contain only alphanumeric charcaters."); | |
} | |
break; | |
case '\'': | |
case '"': | |
if (isValue) | |
{ | |
valueBuilder.Append(chr); | |
} | |
break; | |
default: | |
if (isArray || isValue) | |
{ | |
valueBuilder.Append(chr); | |
} | |
else | |
{ | |
if (char.IsLetter(chr)) | |
{ | |
nameBuilder.Append(chr); | |
} | |
else if (char.IsLetterOrDigit(chr)) | |
{ | |
if (nameBuilder.Length > 0) | |
{ | |
nameBuilder.Append(chr); | |
} | |
else | |
{ | |
throw new ArgumentException("Property names must start with letter."); | |
} | |
} | |
else | |
{ | |
throw new ArgumentException("Property names can contain only alphanumeric charcaters."); | |
} | |
} | |
break; | |
} | |
} | |
return result; | |
} | |
private static object ParseItem(string value) | |
{ | |
var re = new Regex(@"['""](.*)['""]"); | |
Match match = re.Match(value); | |
if (match.Success) | |
{ | |
return match.Groups[1].Value; | |
} | |
else | |
{ | |
if (value.Contains(".")) | |
{ | |
{ | |
decimal result; | |
if (decimal.TryParse(value, out result)) | |
{ | |
return result; | |
} | |
} | |
{ | |
double result; | |
if (double.TryParse(value, out result)) | |
{ | |
return result; | |
} | |
} | |
} | |
else | |
{ | |
{ | |
int result; | |
if (int.TryParse(value, out result)) | |
{ | |
return result; | |
} | |
} | |
{ | |
long result; | |
if (long.TryParse(value, out result)) | |
{ | |
return result; | |
} | |
} | |
} | |
{ | |
decimal result; | |
if (decimal.TryParse(value, out result)) | |
{ | |
return result; | |
} | |
} | |
{ | |
DateTime result; | |
if (DateTime.TryParse(value, out result)) | |
{ | |
return result; | |
} | |
} | |
return null; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment