【问题标题】:deserialize json to class (manually with reflection)将 json 反序列化为类(手动使用反射)
【发布时间】:2013-02-04 17:34:20
【问题描述】:

我在一个不具备使用第三方库的灵活性的平台上使用 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"}]} ]}

【问题讨论】:

标签: c# json reflection


【解决方案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))));
    }
}

【讨论】:

  • 嘿皮特。感谢您的回答。不幸的是,没有 JavaScriptConverter,我认为我不能使用内部/密封类(由于系统的限制)
  • 这是否正确转义例如引号字符?
  • JavaScriptConverter:msdn.microsoft.com/en-us/library/…至于“内部密封”改为public,去掉sealed关键字。
  • @bmm6o 你是什么意思?您能否给我一个您认为可能有问题的示例对象?
  • 在这个紧凑的 dot net 版本中没有 JavaScriptConverter。而且我无法添加包含它的库(由于平台)。这就是为什么我认为反思是唯一的方法
【解决方案2】:

再次感谢 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');

【讨论】:

    【解决方案3】:

    感谢皮特和其他人让我走上了正确的道路。就我而言,我还必须将 JSON 字符串反序列化为 SQL CLR 函数中的强类型对象,因此我只能“安全”使用的库 (more info here)。

    我修改了the code for ParseJSON,它反序列化为Dictionary&lt;string, object&gt;,以便能够反序列化数组的数组,这是它无法做到的,我还开发了一些方法来将生成的字典转换为强类型对象而不使用JavaScriptConverterSystem.Runtime.Serialization 库,使用此代码,我们可以执行以下操作:

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

    ParseJSON 方法将 JSON 字符串和可选的 Dictionary&lt;string, string&gt; 作为输入,并将尝试将其转换为类型 T 字典用于将 JSON 字符串上的任何属性映射到对象的属性(例如,上面声明的“jdata”/“data”字典)。

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

    以下是我修改后的JSON解析方法(可以解析数组的数组):

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

    以下方法尝试将返回的字典从前一个方法转换为强类型对象,我知道这很长,但它可以完成工作:

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

    我希望这对仍在寻找方法的人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-02
      相关资源
      最近更新 更多