将json反序列化为类(手动进行反射) [英] deserialize json to class (manually with reflection)

查看:61
本文介绍了将json反序列化为类(手动进行反射)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在一个没有使用第三方库灵活性的平台上使用C#3.5的简化版本.

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.

虽然我可以解析JSON(使用json流读取器),但是我不确定如何将其实际转换为类. (也无法访问通常的json到类反序列化器).

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).

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

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

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

{"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"}]} ]}

推荐答案

感谢Pete和其他人让我走上了正确的道路.就我而言,我还必须将JSON字符串反序列化为SQL CLR函数中的强类型"对象,因此,我只能使用安全"使用的库受到限制(

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).

我修改了 ParseJSON的代码,该代码反序列化为Dictionary<string, object>以能够反序列化数组,它无法做到的,我还开发了一些方法,可以在不使用JavaScriptConverterSystem.Runtime.Serialization库的情况下将结果字典转换为强类型对象,使用此代码,我们可以执行以下操作:

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

ParseJSON方法将JSON字符串作为输入,还可以选择Dictionary<string, string>作为输入,并尝试将其转换为Type T.字典用于将JSON字符串上的任何属性映射到对象的属性(例如上面声明的"jdata"/"data"字典).

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

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

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.

这篇关于将json反序列化为类(手动进行反射)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆