deserialize json to class (manually with reflection)

json deserialize c#
system.text.json deserialize dynamic
c# json
newtonsoft json custom deserializer for property
deserialize json as dynamic
c# deserialize json to var
c# auto deserialize json
deserialize json to object c# online

I am working with a cut down version of C# 3.5 on a platform that doesn't have the flexibility of using third party libraries.

While I can parse JSON (using a json stream reader), I am not sure how to actually turn it into a class. (there is also no access to the usual json to class deserializer).

Does anyone know how to use reflection to manually (yet dynamically) turn a JSON string into a class?

sample Json:

{"items":[ {"firstName":"bob", "lastName":"smith", "id":1001, "foods": [{"name":"fish", "name":"bacon", "name":"cereal"}]}, {"firstName":"sarah", "lastName":"smith", "id":1002, "foods": [{"name":"bacon", "name":"apples", "name":"chocolate"}]}, {"firstName":"tom", "lastName":"waffle", "id":1003, "foods": [{"name":"waffles", "name":"sticks", "name":"stones"}]}, {"firstName":"reginald", "lastName":"hamtuft", "id":1003, "foods": [{"name":"ham", "name":"cigars", "name":"noisy children"}]} ]}

Thanks Pete and the rest for getting me on the right track for this. In my case I also had to deserialize a JSON string to a Strongly Typed object in a SQL CLR Function, so I was limited on the libraries I could use "safely" (more info here).

I modified the code for ParseJSON, which deserializes into a Dictionary<string, object> to be able to deserialize arrays of arrays, which it couldn't do, I also developed some methods to cast the resulting Dictionary into a Strongly Typed Object without using the JavaScriptConverter or the System.Runtime.Serialization library, with this code we are able to do the following:

//we have a foo and bar classes with a variety of fields and properties
private class foo
{
    public List<double[][]> data;
    public IEnumerable<object> DataObj;
    public int integerField;
    public long longProperty { get; set; }
    public string stringValue;
    public int? nullableInt;
    public DateTime dateTimeValue;
    public List<bar> classValues;
}

private class bar
{
    public string stringValue;
    public DateTimeOffset dateTimeOffsetValue;
}

static void Main(string[] args)
{
    //lets deserialize the following JSON string into our foo object, 
    //the dictionary is optional, and not necessary if our JSON property names are the same as in our object.
    //in this case it's used to map the "jdata" property on the JSON string to the "data" property of our object,
    //in the case of the "dataObj", we are mapping to the uppercase field of our object
    string JSONstring = "{\"jdata\":[[[1526518800000,7.0],[1526518834200,7.0]],[[1526549272200,25.0],[1526549306400,25.0]]],\"dataObj\":[[[1526518800000,7.0],[1526518834200,7.0]],\"abc\",123],\"integerField\":623,\"longProperty\":456789,\"stringValue\":\"foo\",\"nullableInt\":\"\",\"dateTimeValue\":\"2018-05-17T01:00:00.0000000\", \"classValues\": [{\"stringValue\":\"test\",\"dateTimeOffsetValue\":\"2018-05-17T05:00:00.0000000\"},{\"stringValue\":\"test2\",\"dateTimeOffsetValue\":\"2018-05-17T06:00:00.0000000\"}]}";
    var mappingDict = new Dictionary<string, string>() { { "jdata", "data" }, { "dataObj", "DataObj" } };
    foo myObject = ParseJSON<foo>(JSONstring, mappingDict);
}

The ParseJSON method will take a JSON string as input and optionally a Dictionary<string, string> and will attempt to cast it into the Type T the dictionary is used to map any property on the JSON string into a property of the object (for example, the "jdata"/"data" dictionary declared above).

public static T ParseJSON<T>(string jsonString, Dictionary<string, string> mappingTable = null)
{
    Dictionary<string, object> jsonDictionary = ParseJSON(jsonString);
    T castedObj = CastAs<T>(jsonDictionary, mappingTable);
    return castedObj;
}

The following is my modified method for JSON parsing (can parse arrays of arrays):

public static Dictionary<string, object> ParseJSON(string json)
{
    int end;
    return ParseJSON(json, 0, out end);
}

private static Dictionary<string, object> ParseJSON(string json, int start, out int end)
{
    Dictionary<string, object> dict = new Dictionary<string, object>();
    bool escbegin = false;
    bool escend = false;
    bool inquotes = false;
    string key = null;
    int cend;
    StringBuilder sb = new StringBuilder();
    Dictionary<string, object> child = null;
    List<object> arraylist = null;
    Regex regex = new Regex(@"\\u([0-9a-z]{4})", RegexOptions.IgnoreCase);
    int autoKey = 0;
    int subArrayCount = 0;
    List<int> arrayIndexes = new List<int>();
    bool inSingleQuotes = false;
    bool inDoubleQuotes = false;
    for (int i = start; i < json.Length; i++)
    {
        char c = json[i];
        if (c == '\\') escbegin = !escbegin;
        if (!escbegin)
        {
            if (c == '"' && !inSingleQuotes)
            {
                inDoubleQuotes = !inDoubleQuotes;
                inquotes = !inquotes;
                if (!inquotes && arraylist != null)
                {
                    arraylist.Add(DecodeString(regex, sb.ToString()));
                    sb.Length = 0;
                }
                continue;
            }
            else if (c == '\'' && !inDoubleQuotes)
            {
                inSingleQuotes = !inSingleQuotes;
                inquotes = !inquotes;
                if (!inquotes && arraylist != null)
                {
                    arraylist.Add(DecodeString(regex, sb.ToString()));
                    sb.Length = 0;
                }
                continue;
            }
            if (!inquotes)
            {
                switch (c)
                {
                    case '{':
                        if (i != start)
                        {
                            child = ParseJSON(json, i, out cend);
                            if (arraylist != null)
                            {
                                arraylist.Add(child);
                            }
                            else
                            {
                                dict.Add(key.Trim(), child);
                                key = null;
                            }
                            i = cend;
                        }
                        continue;
                    case '}':
                        end = i;
                        if (key != null)
                        {
                            if (arraylist != null) dict.Add(key.Trim(), arraylist);
                            else dict.Add(key.Trim(), DecodeString(regex, sb.ToString().Trim()));
                        }
                        return dict;
                    case '[':
                        if (arraylist != null)
                        {
                            List<object> _tempArrayList = arraylist;
                            for (int l = 0; l < subArrayCount; l++)
                            {
                                if (l == subArrayCount - 1)
                                {
                                    _tempArrayList.Add(new List<object>());
                                }
                                else
                                {
                                    _tempArrayList = (List<object>)_tempArrayList[arrayIndexes[l]];
                                }
                            }

                            if (arrayIndexes.Count < subArrayCount)
                            {
                                arrayIndexes.Add(0);
                            }
                            subArrayCount++;
                        }
                        else
                        {
                            arraylist = new List<object>();
                            subArrayCount++;
                        }
                        continue;
                    case ']':
                        if (key == null)
                        {
                            key = "array" + autoKey.ToString();
                            autoKey++;
                        }
                        if (arraylist != null)
                        {
                            List<object> _tempArrayList = arraylist;
                            for (int l = 0; l < subArrayCount; l++)
                            {
                                if (l == subArrayCount - 1)
                                {
                                    if (sb.Length > 0)
                                    {
                                        _tempArrayList.Add(sb.ToString());
                                    }
                                    subArrayCount--;
                                    if (subArrayCount == arrayIndexes.Count)
                                    {
                                        if (arrayIndexes.Count > 0)
                                        {
                                            arrayIndexes[arrayIndexes.Count - 1]++;
                                        }
                                    }
                                    else if (subArrayCount == arrayIndexes.Count - 1)
                                    {
                                        arrayIndexes.RemoveAt(arrayIndexes.Count - 1);
                                        if (arrayIndexes.Count > 0)
                                        {
                                            arrayIndexes[arrayIndexes.Count - 1]++;
                                        }
                                    }
                                }
                                else
                                {
                                    _tempArrayList = (List<object>)_tempArrayList[arrayIndexes[l]];
                                }
                            }
                            sb.Length = 0;
                        }
                        if (subArrayCount == 0)
                        {
                            dict.Add(key.Trim(), arraylist);
                            arraylist = null;
                            key = null;
                        }
                        continue;
                    case ',':
                        if (arraylist == null && key != null)
                        {
                            dict.Add(key.Trim(), DecodeString(regex, sb.ToString().Trim()));
                            key = null;
                            sb.Length = 0;
                        }
                        if (arraylist != null && sb.Length > 0)
                        {
                            List<object> _tempArrayList = arraylist;
                            for (int l = 0; l < subArrayCount; l++)
                            {
                                if (l == subArrayCount - 1)
                                {
                                    _tempArrayList.Add(sb.ToString());
                                }
                                else
                                {
                                    _tempArrayList = (List<object>)_tempArrayList[arrayIndexes[l]];
                                }
                            }
                            sb.Length = 0;
                        }
                        continue;
                    case ':':
                        key = DecodeString(regex, sb.ToString());
                        sb.Length = 0;
                        continue;
                }
            }
        }
        sb.Append(c);
        if (escend) escbegin = false;
        if (escbegin) escend = true;
        else escend = false;
    }
    end = json.Length - 1;
    return dict; //shouldn't ever get here unless the JSON is malformed
}

private static string DecodeString(Regex regex, string str)
{
    return Regex.Unescape(regex.Replace(str, match => char.ConvertFromUtf32(Int32.Parse(match.Groups[1].Value, System.Globalization.NumberStyles.HexNumber))));
}

The following methods attempt to cast the returned dictionary from the previous method into a Strong Typed Object, I know is lengthy but it does the job:

private static T CastAs<T>(Dictionary<string, object> source, Dictionary<string, string> mappingTable = null)
{
    T outputData = (T)Activator.CreateInstance(typeof(T));
    TrySet(outputData, source, mappingTable);
    return outputData;
}

private static void TrySet(object target, Dictionary<string, object> source, Dictionary<string, string> mappingTable = null)
{
    if (target == null)
    {
        throw new ArgumentNullException("target");
    }
    bool useMappingTable = mappingTable != null && mappingTable.Count > 0;
    foreach (KeyValuePair<string, object> kv in source)
    {
        string propertyName = null;
        if (useMappingTable && mappingTable.ContainsKey(kv.Key))
        {
            propertyName = mappingTable[kv.Key];
        }
        else
        {
            propertyName = kv.Key;
        }

        if (!string.IsNullOrEmpty(propertyName))
        {
            UpdateMember(target, propertyName, kv.Value, mappingTable);
        }
    }
}

private static void UpdateMember(object target, string propertyName, object value, Dictionary<string, string> mappingTable)
{
    try
    {
        FieldInfo fieldInfo = target.GetType().GetField(propertyName);

        if (fieldInfo != null)
        {
            value = ConvertTo(value, fieldInfo.FieldType, mappingTable);
            fieldInfo.SetValue(target, value);
        }
        else
        {
            PropertyInfo propInfo = target.GetType().GetProperty(propertyName);

            if (propInfo != null)
            {
                value = ConvertTo(value, propInfo.PropertyType, mappingTable);
                propInfo.SetValue(target, value);
            }
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

private static object ConvertTo(object value, Type targetType, Dictionary<string, string> mappingTable)
{
    try
    {
        bool isNullable = false;
        Type sourceType = value.GetType();

        //Obtain actual type to convert to (this is necessary in case of Nullable types)
        if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            isNullable = true;
            targetType = targetType.GetGenericArguments()[0];
        }

        if (isNullable && string.IsNullOrWhiteSpace(Convert.ToString(value)))
        {
            return null;
        }
        //if we are converting from a dictionary to a class, call the TrySet method to convert its members
        else if (targetType.IsClass && sourceType.IsGenericType && sourceType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
        {
            //make sure our value is actually a Dictionary<string, object> in order to be able to cast
            if (sourceType.GetGenericArguments()[0] == typeof(string))
            {
                object convertedValue = Activator.CreateInstance(targetType);
                TrySet(convertedValue, (Dictionary<string, object>)value, mappingTable);
                return convertedValue;
            }
            return null;
        }
        else if (IsCollection(value))
        {
            Type elementType = GetCollectionElementType(targetType);

            if (elementType != null)
            {
                if (targetType.BaseType == typeof(Array))
                {
                    return ConvertToArray(elementType, value, mappingTable);
                }
                else
                {
                    return ConvertToList(elementType, value, mappingTable);
                }
            }
            else
            {
                throw new NullReferenceException();
            }
        }
        else if (targetType == typeof(DateTimeOffset))
        {
            return new DateTimeOffset((DateTime)ChangeType(value, typeof(DateTime)));
        }
        else if (targetType == typeof(object))
        {
            return value;
        }
        else
        {
            return ChangeType(value, targetType);
        }
    }
    catch (Exception ex)
    {
        if (targetType.IsValueType)
        {
            return Activator.CreateInstance(targetType);
        }
        return null;
    }
}

private static Array ConvertToArray(Type elementType, object value, Dictionary<string, string> mappingTable)
{
    Array collection = Array.CreateInstance(elementType, ((ICollection)value).Count);

    int i = 0;
    foreach (object item in (IEnumerable)value)
    {
        try
        {
            collection.SetValue(ConvertTo(item, elementType, mappingTable), i);
            i++;
        }
        catch (Exception ex)
        {
            //nothing here, just skip the item
        }
    }

    return collection;
}

private static IList ConvertToList(Type elementType, object value, Dictionary<string, string> mappingTable)
{
    Type listType = typeof(List<>);
    Type constructedListType = listType.MakeGenericType(elementType);
    IList collection = (IList)Activator.CreateInstance(constructedListType);

    foreach (object item in (IEnumerable)value)
    {
        try
        {
            collection.Add(ConvertTo(item, elementType, mappingTable));
        }
        catch (Exception ex)
        {
            //nothing here, just skip the item
        }
    }

    return collection;
}

private static bool IsCollection(object obj)
{
    bool isCollection = false;

    Type objType = obj.GetType();
    if (!typeof(string).IsAssignableFrom(objType) && typeof(IEnumerable).IsAssignableFrom(objType))
    {
        isCollection = true;
    }

    return isCollection;
}

private static Type GetCollectionElementType(Type objType)
{
    Type elementType;
    Type[] genericArgs = objType.GenericTypeArguments;
    if (genericArgs.Length > 0)
    {
        elementType = genericArgs[0];
    }
    else
    {
        elementType = objType.GetElementType();
    }

    return elementType;
}

private static object ChangeType(object value, Type castTo)
{
    try
    {
        return Convert.ChangeType(value, castTo);
    }
    catch (Exception ex)
    {
        //if the conversion failed, just return the original value
        return value;
    }
}

I hope this is helpful to anyone still looking for a way to do this.

c# - Deserialize JSON into different classes using Reflection, I am working with REST API and the the api return me JSON data. I want to create a method such that each time I call the API, I just send the type of class on  I have a static class with static fields and a json. I can deserialize the json into a dynamic object, so I have all the fields and they match exactly the static fields in the class. How can I use reflection to enumerate the fields and copy the values from the dynamic class into the static class fields?

Okay, I'm redoing my answer based on the feedback. The dynamic object generator code still comes from this:

Deserialize JSON into C# dynamic object?

This uses RegEx, Generic collections and it does use Linq, but only in 2 lines and those can easily rewritten to not use Linq (the two 'result = ' lines at the end of DynamicJsonObject.TryGetMember()). The generic dictionaries can also be replaced with hash tables if necessary.

The json parser is adapted from How can I deserialize JSON to a simple Dictionary<string,string> in ASP.NET?

class Program
{
    static void Main(string[] args)
    {
        string data = "{ 'test': 42, 'test2': 'test2\"', 'structure' : { 'field1': 'field1', 'field2': 44 } }";

        dynamic x = new DynamicJsonObject(JsonMaker.ParseJSON(data));
        Console.WriteLine(x.test2);
        Console.WriteLine(x.structure.field1);
        Console.ReadLine();
    }
}

public class DynamicJsonObject : DynamicObject
{
    private readonly IDictionary<string, object> _dictionary;

    public DynamicJsonObject(IDictionary<string, object> dictionary)
    {
        if (dictionary == null)
            throw new ArgumentNullException("dictionary");
        _dictionary = dictionary;
    }

    public override string ToString()
    {
        var sb = new StringBuilder();
        ToString(sb);
        return sb.ToString();
    }

    private void ToString(StringBuilder sb)
    {
        sb.Append("{");
        var firstInDictionary = true;
        foreach (var pair in _dictionary)
        {
            if (!firstInDictionary)
                sb.Append(",");
            firstInDictionary = false;
            var value = pair.Value;
            var name = pair.Key;
            if (value is string)
            {
                sb.AppendFormat("\"{0}\":\"{1}\"", name, value);
            }
            else if (value is IDictionary<string, object>)
            {
                sb.AppendFormat("\"{0}\":", name);
                new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
            }
            else if (value is ArrayList)
            {
                sb.Append("\"");
                sb.Append(name);
                sb.Append("\":[");
                var firstInArray = true;
                foreach (var arrayValue in (ArrayList)value)
                {
                    if (!firstInArray)
                        sb.Append(",");
                    firstInArray = false;
                    if (arrayValue is IDictionary<string, object>)
                        new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
                    else if (arrayValue is string)
                        sb.AppendFormat("\"{0}\"", arrayValue);
                    else
                        sb.AppendFormat("{0}", arrayValue);

                }
                sb.Append("]");
            }
            else
            {
                sb.AppendFormat("\"{0}\":{1}", name, value);
            }
        }
        sb.Append("}");
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (!_dictionary.TryGetValue(binder.Name, out result))
        {
            // return null to avoid exception.  caller can check for null this way...
            result = null;
            return true;
        }

        var dictionary = result as IDictionary<string, object>;
        if (dictionary != null)
        {
            result = new DynamicJsonObject(dictionary);
            return true;
        }

        var arrayList = result as ArrayList;
        if (arrayList != null && arrayList.Count > 0)
        {
            if (arrayList[0] is IDictionary<string, object>)
                result = new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)));
            else
                result = new List<object>(arrayList.Cast<object>());
        }

        return true;
    }
}

public static class JsonMaker
{
    public static Dictionary<string, object> ParseJSON(string json)
    {
        int end;
        return ParseJSON(json, 0, out end);
    }
    private static Dictionary<string, object> ParseJSON(string json, int start, out int end)
    {
        Dictionary<string, object> dict = new Dictionary<string, object>();
        bool escbegin = false;
        bool escend = false;
        bool inquotes = false;
        string key = null;
        int cend;
        StringBuilder sb = new StringBuilder();
        Dictionary<string, object> child = null;
        List<object> arraylist = null;
        Regex regex = new Regex(@"\\u([0-9a-z]{4})", RegexOptions.IgnoreCase);
        int autoKey = 0;
        bool inSingleQuotes = false;
        bool inDoubleQuotes = false;
        for (int i = start; i < json.Length; i++)
        {
            char c = json[i];
            if (c == '\\') escbegin = !escbegin;
            if (!escbegin)
            {
                if (c == '"' && !inSingleQuotes)
                {
                    inDoubleQuotes = !inDoubleQuotes;
                    inquotes = !inquotes;
                    if (!inquotes && arraylist != null)
                    {
                        arraylist.Add(DecodeString(regex, sb.ToString()));
                        sb.Length = 0;
                    }
                    continue;
                }
                else if (c == '\'' && !inDoubleQuotes)
                {
                    inSingleQuotes = !inSingleQuotes;
                    inquotes = !inquotes;
                    if (!inquotes && arraylist != null)
                    {
                        arraylist.Add(DecodeString(regex, sb.ToString()));
                        sb.Length = 0;
                    }
                    continue;
                }
                if (!inquotes)
                {
                    switch (c)
                    {
                        case '{':
                            if (i != start)
                            {
                                child = ParseJSON(json, i, out cend);
                                if (arraylist != null) arraylist.Add(child);
                                else
                                {
                                    dict.Add(key.Trim(), child);
                                    key = null;
                                }
                                i = cend;
                            }
                            continue;
                        case '}':
                            end = i;
                            if (key != null)
                            {
                                if (arraylist != null) dict.Add(key.Trim(), arraylist);
                                else dict.Add(key.Trim(), DecodeString(regex, sb.ToString().Trim()));
                            }
                            return dict;
                        case '[':
                            arraylist = new List<object>();
                            continue;
                        case ']':
                            if (key == null)
                            {
                                key = "array" + autoKey.ToString();
                                autoKey++;
                            }
                            if (arraylist != null && sb.Length > 0)
                            {
                                arraylist.Add(sb.ToString());
                                sb.Length = 0;
                            }
                            dict.Add(key.Trim(), arraylist);
                            arraylist = null;
                            key = null;
                            continue;
                        case ',':
                            if (arraylist == null && key != null)
                            {
                                dict.Add(key.Trim(), DecodeString(regex, sb.ToString().Trim()));
                                key = null;
                                sb.Length = 0;
                            }
                            if (arraylist != null && sb.Length > 0)
                            {
                                arraylist.Add(sb.ToString());
                                sb.Length = 0;
                            }
                            continue;
                        case ':':
                            key = DecodeString(regex, sb.ToString());
                            sb.Length = 0;
                            continue;
                    }
                }
            }
            sb.Append(c);
            if (escend) escbegin = false;
            if (escbegin) escend = true;
            else escend = false;
        }
        end = json.Length - 1;
        return dict; //theoretically shouldn't ever get here
    }

    private static string DecodeString(Regex regex, string str)
    {
        return Regex.Unescape(regex.Replace(str, match => char.ConvertFromUtf32(Int32.Parse(match.Groups[1].Value, System.Globalization.NumberStyles.HexNumber))));
    }
}

Performance Tips, Creating a contract involves inspecting a type with slow reflection, greater than 85kb in size to avoid the JSON string ending up in the large object heap. JSON is to use JsonTextReader/JsonTextWriter directly to manually serialize types. In the above code typeName is class name which is matching json properties. then create the type using Type.GetType() method. then pass this type to our JsonConvert.DeserializeObject method.

Thanks again to Pete and the other guys for their brilliant post. I have wrapped mine around a SQL Server CLR scalar function which was incredibly useful in interrogating JSON stored in relational tables (I know some would say just use MongoDB!).

Please see below:

    public class JsonHelper
{
    /// <summary>
    /// Parses the JSON.
    /// Thanks to http://stackoverflow.com/questions/14967618/deserialize-json-to-class-manually-with-reflection
    /// </summary>
    /// <param name="json">The json.</param>
    /// <returns></returns>
    public static Dictionary<string, object> DeserializeJson(string json)
    {
        int end;

        return DeserializeJson(json, 0, out end);
    }

    /// <summary>
    /// Parses the JSON.
    /// </summary>
    /// <param name="json">The json.</param>
    /// <param name="start">The start.</param>
    /// <param name="end">The end.</param>
    /// <returns></returns>
    private static Dictionary<string, object> DeserializeJson(string json, int start, out int end)
    {
        var dict = new Dictionary<string, object>();
        var escbegin = false;
        var escend = false;
        var inquotes = false;
        string key = null;
        var sb = new StringBuilder();
        List<object> arraylist = null;
        var regex = new Regex(@"\\u([0-9a-z]{4})", RegexOptions.IgnoreCase);
        var autoKey = 0;
        var inSingleQuotes = false;
        var inDoubleQuotes = false;

        for (var i = start; i < json.Length; i++)
        {
            var c = json[i];
            if (c == '\\') escbegin = !escbegin;
            if (!escbegin)
            {
                if (c == '"' && !inSingleQuotes)
                {
                    inDoubleQuotes = !inDoubleQuotes;
                    inquotes = !inquotes;
                    if (!inquotes && arraylist != null)
                    {
                        arraylist.Add(DecodeString(regex, sb.ToString()));
                        sb.Length = 0;
                    }

                    continue;
                }

                if (c == '\'' && !inDoubleQuotes)
                {
                    inSingleQuotes = !inSingleQuotes;
                    inquotes = !inquotes;
                    if (!inquotes && arraylist != null)
                    {
                        arraylist.Add(DecodeString(regex, sb.ToString()));
                        sb.Length = 0;
                    }

                    continue;
                }

                if (!inquotes)
                {
                    switch (c)
                    {
                        case '{':
                            if (i != start)
                            {
                                int cend;
                                var child = DeserializeJson(json, i, out cend);
                                if (arraylist != null)
                                {
                                    arraylist.Add(child);
                                }
                                else
                                {
                                    dict.Add(key.Trim(), child);
                                    key = null;
                                }

                                i = cend;
                            }

                            continue;

                        case '}':
                            end = i;

                            if (key != null)
                            {
                                if (arraylist != null) dict.Add(key.Trim(), arraylist);
                                else dict.Add(key.Trim(), DecodeString(regex, sb.ToString().Trim()));
                            }

                            return dict;

                        case '[':
                            arraylist = new List<object>();
                            continue;

                        case ']':
                            if (key == null)
                            {
                                key = "array" + autoKey;
                                autoKey++;
                            }

                            if (arraylist != null && sb.Length > 0)
                            {
                                arraylist.Add(sb.ToString());
                                sb.Length = 0;
                            }

                            dict.Add(key.Trim(), arraylist);
                            arraylist = null;
                            key = null;
                            continue;

                        case ',':
                            if (arraylist == null && key != null)
                            {
                                dict.Add(key.Trim(), DecodeString(regex, sb.ToString().Trim()));
                                key = null;
                                sb.Length = 0;
                            }

                            if (arraylist != null && sb.Length > 0)
                            {
                                arraylist.Add(sb.ToString());
                                sb.Length = 0;
                            }

                            continue;

                        case ':':
                            key = DecodeString(regex, sb.ToString());
                            sb.Length = 0;

                            continue;
                    }
                }
            }

            sb.Append(c);

            if (escend) escbegin = false;
            escend = escbegin;
        }

        end = json.Length - 1;
        return dict; // theoretically shouldn't ever get here
    }

    /// <summary>
    /// Decodes the string.
    /// </summary>
    /// <param name="regex">The regex.</param>
    /// <param name="str">The STR.</param>
    /// <returns></returns>
    private static string DecodeString(Regex regex, string str)
    {
        return
            Regex.Unescape(regex.Replace(str,
                match =>
                    char.ConvertFromUtf32(Int32.Parse(match.Groups[1].Value,
                        System.Globalization.NumberStyles
                            .HexNumber))));
    }

    /// <summary>
    /// Returns true if string has an "appearance" of being JSON-like
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public static bool IsJson(string input)
    {
        input = input.Trim();
        return input.StartsWith("{") && input.EndsWith("}")
               || input.StartsWith("[") && input.EndsWith("]");
    }
}

The CLR function is below:

/// <summary>
/// Json "extractor" capable of extracting a value of a key using the object notation.
/// </summary>
/// <param name="json"></param>
/// <param name="key"></param>
/// <returns></returns>
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString fn_GetKeyValue(SqlString json, SqlString key)
{
    var jsonString = json.ToString();

    // Return if N/A
    if (string.IsNullOrEmpty(jsonString) || !JsonHelper.IsJson(jsonString))
    {
        return json;
    }

    var keyString = key.ToString();

    var jsonDictionary = JsonHelper.DeserializeJson(jsonString);
    var lastNode = string.Empty;

    foreach (var node in keyString.Split('.'))
    {
        if (!jsonDictionary.ContainsKey(node)) continue;

        var childDictionary = jsonDictionary[node] as Dictionary<string, object>;

        if (childDictionary != null)
        {
            jsonDictionary = childDictionary;
        }

        lastNode = node;
    }

    if (!jsonDictionary.ContainsKey(lastNode))
    {
        return null;
    }

    var keyValueString = jsonDictionary[lastNode].ToString();

    return keyValueString == "null" ? null : new SqlString(keyValueString);
}

Usage would be:

-- Example 1 (querying a parent node)
SELECT  dbo.fn_GetKeyValue('{
    "ExchangeRates": {
        "GBP": "1.2",
        "USD": "2.0"
    },
    "Timestamp": "2015-04-10"
}', 'Timestamp');

-- Example 2 (querying a child node using a dot notation)
SELECT  dbo.fn_GetKeyValue('{
    "ExchangeRates": {
        "GBP": "1.2",
        "USD": "2.0"
    },
    "Timestamp": "2015-04-10"
}', 'ExchangeRates.GBP');

How to write custom converters for JSON serialization, Json , see How to serialize and deserialize JSON in .NET. Serialization; namespace SystemTextJsonSamples { public class DateTimeOffsetConverter Reflection; using System.Text.Json; using System.Text.Json. The converter code in the preceding example reads and writes each property manually. In this article you will learn how to serialize and deserialize JSON object using JSON.NET library. JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write and easy for machines to parse and generate. JSON is a text format that is completely language independent.

JSON Serialization - Unity, Using FromJson() with unknown types. If you don't know the type of an object ahead of time, deserialize the JSON into a class or struct that contains “common”​  Setting up json_serializable in a project. To include json_serializable in your project, you need one regular dependency, and two dev dependencies. In short, dev dependencies are dependencies that are not included in our app source code—they are only used in the development environment.

Reflections on Reflection, As a Java developer, when suggesting a reflection-based library to use in is to present different options relying on popular libraries, used to parse this JSON Each class therefore requires its parser to be manually written. I need to serialize many different objects with Json.NET. I really have no control over the objects being provided, so I'm generically serializing and de-serializing with TypeNameHandling.All. However, some of these objects cannot be de-serialized. Specifically, I am getting some System.Reflection.RuntimePropertyInfo types.

Reflection Deserialization and Custom Attributes, JsonConverter is an abstract class provides with JSON.NET that allows you to convert an object to and from JSON. By inheriting that, you can  This sample deserializes JSON to an object. Deserialize JSON from a file. JsonConverterAttribute on a class.

Comments
  • Does the DataContractJsonSeriazer also not an option?
  • unfortunately, it's not an option
  • Hey Pete. Thanks for the answer. Unfortunately, there is no JavaScriptConverter and I don't think I can use internal/sealed classes (due to the constraints of the system)
  • Does this do proper escaping of e.g. quote characters?
  • JavaScriptConverter: msdn.microsoft.com/en-us/library/… As for it being "internal sealed" change it to public and remove the sealed keyword.
  • @bmm6o How do you mean? Can you give me an example object that you feel might be problematic?
  • There is no JavaScriptConverter in this compact version of dot net. And I can't add the library containing it (due to the platform). This is why I am thinking that reflection is the only way