将json反序列化为类(使用反射手动)

时间:2013-02-19 21:18:20

标签: c# json reflection

我在一个没有使用第三方库的灵活性的平台上使用C#3.5的缩减版本。

虽然我可以解析JSON(使用json流阅读器),但我不确定如何将其转换为类。 (也没有通常的json访问类反序列化器。)

有谁知道如何使用反射手动(但动态地)将JSON字符串转换为类?

示例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"}]} ]}

2 个答案:

答案 0 :(得分:1)

好的,我正在根据反馈重做我的回答。动态对象生成器代码仍然来自:

Deserialize JSON into C# dynamic object?

这使用RegEx,Generic集合,它确实使用Linq,但只有2行,并且可以很容易地重写那些不使用Linq(DynamicJsonObject.TryGetMember()末尾的两个'result ='行)。如有必要,通用字典也可以用哈希表替换。

json解析器改编自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))));
    }
}

答案 1 :(得分:0)

再次感谢Pete和其他人的精彩帖子。我已经把我的SQL Server CLR标量函数包裹起来,这在查询存储在关系表中的JSON时非常有用(我知道有人会说只使用MongoDB!)。

请参阅以下内容:

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

CLR功能如下:

/// <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);
}

用法是:

-- 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');