5

プロパティとその値のリストがあり、次のDictionary<string, object>ようにフォーマットされています。

Person.Name = "John Doe"
Person.Age = 27
Person.Address.House = "123"
Person.Address.Street = "Fake Street"
Person.Address.City = "Nowhere"
Person.Address.State = "NH"

2つのクラスがあります。Person文字列Nameとプリミティブ、および文字列プロパティを持つAge複雑なAddressクラスで構成されます。HouseStreetCityState

基本的に私がやりたいことはPerson、現在のアセンブリでクラスを検索し、そのインスタンスを作成し、クラスがどれほど複雑になっても、最も深いレベルでプリミティブ、文字列で構成されている限り、すべての値を割り当てることです、および などのいくつかの一般的な構造DateTime

トップレベルのプロパティを割り当てて、複雑なプロパティの1つに下げることができるソリューションがあります。これを解決するには再帰を使用する必要があると想定していますが、使用しないことをお勧めします。

ただし、再帰を使用しても、各プロパティにアクセスして値を割り当てる良い方法については途方に暮れています。

以下のこの例では、メソッドのパラメーターに基づいて、ドット表現をクラスに変換しようとしています。パラメータのタイプに基づいて適切なドット表現を検索し、一致するものを見つけようとします。DotField基本的にKeyValuePair<string, object>は、キーがNameプロパティである場所です。以下のコードは正しく動作しない可能性がありますが、アイデアは十分に表現されているはずです。

foreach (ParameterInfo parameter in this.method.Parameters)
{
    Type parameterType = parameter.ParameterType;
    object parameterInstance = Activator.CreateInstance(parameterType);

    PropertyInfo[] properties = parameterType.GetProperties();
    foreach (PropertyInfo property in properties)
    {
        Type propertyType = property.PropertyType;
        if (propertyType.IsPrimitive || propertyType == typeof(string))
        {
            string propertyPath = String.Format("{0}.{1}", parameterType.Name, propertyType.Name);
            foreach (DotField df in this.DotFields)
            {
                if (df.Name == propertyPath)
                {
                    property.SetValue(parameterInstance, df.Value, null);
                    break;
                }
            }
        }
        else
        {
            // Somehow dive into the class, since it's a non-primitive
        }
    }
}
4

3 に答える 3

2

そのためにリフレクションを使用することもできます。これを書き留めるのが楽しかったです:)

private object Eval(KeyValuePair<string, object> df)
{
    var properties = df.Key.Split('.');
    //line below just creates the root object (Person), you could replace it with whatever works in your example
    object root = Activator.CreateInstance(Assembly.GetExecutingAssembly().GetTypes().First(t => t.Name == properties.First()));

    var temp = root;

    for (int i = 1; i < properties.Length - 1; i++)
    {
        var propertyInfo = temp.GetType().GetProperty(properties[i]);
        var propertyInstance = Activator.CreateInstance(propertyInfo.PropertyType);                
        propertyInfo.SetValue(temp, propertyInstance, null);

        temp = propertyInstance;
    }

    temp.GetType().GetProperty(properties.Last()).SetValue(temp, df.Value, null);
    return root;
}
于 2013-10-17T17:53:58.513 に答える
2

あなたのDictionaryサウンドは、JSON 形式のデータに似ています。最初に互換性のある形式に変換する場合は、Json.Netを使用して辞書をオブジェクトに変換できます。その例を次に示します。

public static void Main()
{
    var dict = new Dictionary<string, object>
    {
        {"Person.Name", "John Doe"},
        {"Person.Age", 27},
        {"Person.Address.House", "123"},
        {"Person.Address.Street", "Fake Street"},
        {"Person.Address.City", "Nowhere"},
        {"Person.Address.State", "NH"},
    };
    var hierarchicalDict = GetItemAndChildren(dict, "Person");
    string json = JsonConvert.SerializeObject(hierarchicalDict);
    Person person = JsonConvert.DeserializeObject<Person>(json);
    // person has all of the values you'd expect
}
static object GetItemAndChildren(Dictionary<string, object> dict, string prefix = "")
{
    object val;
    if (dict.TryGetValue(prefix, out val))
        return val;
    else
    {
        if (!string.IsNullOrEmpty(prefix))
            prefix += ".";
        var children = new Dictionary<string, object>();
        foreach (var child in dict.Where(x => x.Key.StartsWith(prefix)).Select(x => x.Key.Substring(prefix.Length).Split(new[] { '.' }, 2)[0]).Distinct())
        {
            children[child] = GetItemAndChildren(dict, prefix + child);
        }
        return children;
    }
}
于 2013-10-17T16:26:58.787 に答える
1

これが私の完全なコード例です。私は、膨大な量のリフレクションとマッピングを避け、ドット リスト構造から多くのコンテキスト情報を取得することにしました。

彼らのソリューションと、このソリューションにたどり着いた情報を提供してくれたことに感謝Timします。rla4

    private static int GetPathDepth(string path)
    {
        int depth = 0;
        for (int i = 0; i < path.Length; i++)
        {
            if (path[i] == '.')
            {
                depth++;
            }
        }

        return depth;
    }

    private static string GetPathAtDepth(string path, int depth)
    {
        StringBuilder pathBuilder = new StringBuilder();

        string[] pathParts = path.Split('.');
        for (int i = 0; i < depth && i < pathParts.Length; i++)
        {
            string pathPart = pathParts[i];

            if (i == depth - 1 || i == pathParts.Length - 1)
            {
                pathBuilder.Append(pathPart);
            }
            else
            {
                pathBuilder.AppendFormat("{0}.", pathPart);
            }
        }

        string pathAtDepth = pathBuilder.ToString();
        return pathAtDepth;
    }

    private static string[] GetIntermediatePaths(string path)
    {
        int depth = GetPathDepth(path);

        string[] intermediatePaths = new string[depth];
        for (int i = 0; i < intermediatePaths.Length; i++)
        {
            string intermediatePath = GetPathAtDepth(path, i + 1);
            intermediatePaths[i] = intermediatePath;
        }

        return intermediatePaths;
    }

    private static PropertyInfo GetProperty(Type root, string path)
    {
        PropertyInfo result = null;

        string[] pathParts = path.Split('.');
        foreach (string pathPart in pathParts)
        {
            if (Object.ReferenceEquals(result, null))
            {
                result = root.GetProperty(pathPart);
            }
            else
            {
                result = result.PropertyType.GetProperty(pathPart);
            }
        }

        if (Object.ReferenceEquals(result, null))
        {
            throw new ArgumentException("A property at the specified path could not be located.", "path");
        }

        return result;
    }

    private static object GetParameter(ParameterInfo parameter, Dictionary<string, string> valueMap)
    {
        Type root = parameter.ParameterType;

        Dictionary<string, object> instanceMap = new Dictionary<string, object>();
        foreach (KeyValuePair<string, string> valueMapEntry in valueMap)
        {
            string path = valueMapEntry.Key;
            string value = valueMapEntry.Value;

            string[] intermediatePaths = GetIntermediatePaths(path);
            foreach (string intermediatePath in intermediatePaths)
            {
                PropertyInfo intermediateProperty = GetProperty(root, intermediatePath);

                object propertyTypeInstance;
                if (!instanceMap.TryGetValue(intermediatePath, out propertyTypeInstance))
                {
                    propertyTypeInstance = Activator.CreateInstance(intermediateProperty.PropertyType);
                    instanceMap.Add(intermediatePath, propertyTypeInstance);
                }
            }

            PropertyInfo property = GetProperty(root, path);

            TypeConverter converter = TypeDescriptor.GetConverter(property.PropertyType);
            object convertedValue = converter.ConvertFrom(value);

            instanceMap.Add(path, convertedValue);
        }

        object rootInstance = Activator.CreateInstance(root);

        foreach (KeyValuePair<string, object> instanceMapEntry in instanceMap)
        {
            string path = instanceMapEntry.Key;
            object value = instanceMapEntry.Value;

            PropertyInfo property = GetProperty(root, path);

            object instance;
            int depth = GetPathDepth(path);
            if (depth == 0)
            {
                instance = rootInstance;
            }
            else
            {
                string parentPath = GetPathAtDepth(path, depth);
                instance = instanceMap[parentPath];
            }

            property.SetValue(instance, value);
        }

        return rootInstance;
    }
于 2013-10-18T21:58:27.713 に答える