Is it possible to auto-generate object initialization code from a runtime object with values?

為{幸葍}努か 提交于 2020-01-15 05:56:15

问题


Maybe a long shot, but if this existed it would save me some time.

To explain in more detail. Let's say I have a long XML file and a mapped class. I want to test stuff and change values around before I run a test. I could re-construct the whole XML structure by writing C# code for initializing that mapped class, but what I want to know is - Do I absolutely have to ?

So basically I want to parse a big XML File into an object at runtime and then I generate the initialization code as a string I could just paste somewhere. Let's say the input is :

<MyObject>
    <Prop1>a</Prop1>
    <Prop2>b</Prop2>
    <Prop2>c</Prop2>
</MyObject>

And I would like a string like this for example:

"new MyObject() 
{
    Prop1 = "a",
    Prop2 = "b",
    Prop3 = "c"
}"

回答1:


You might want to check this Visual Studio extension.

However, you can start simple using the code below. Taken from this answer. It doesn't work on non typed objects but it helps.

PS: I will be updating this code once i come up with enhancements.

    public class ObjectInitGenerator
{
    public static string ToObjectInitializer(Object obj)
    {
        var sb = new StringBuilder(1024);

        sb.Append("var x = ");
        sb = WalkObject(obj, sb);
        sb.Append(";");

        return sb.ToString();
    }

    private static StringBuilder WalkObject(Object obj, StringBuilder sb)
    {
        var properties = obj.GetType().GetProperties();

        var type = obj.GetType();
        var typeName = type.Name;
        sb.Append("new " + type.Name + " {");

        bool appendComma = false;
        DateTime workDt;
        foreach (var property in properties)
        {
            if (appendComma) sb.Append(", ");
            appendComma = true;

            var pt = property.PropertyType;
            var name = pt.Name;

            var isList = property.PropertyType.GetInterfaces().Contains(typeof(IList));

            var isClass = property.PropertyType.IsClass;

            if (isList)
            {
                IList list = (IList)property.GetValue(obj, null);
                var listTypeName = property.PropertyType.GetGenericArguments()[0].Name;

                if (list != null && list.Count > 0)
                {
                    sb.Append(property.Name + " = new List<" + listTypeName + ">{");
                    sb = WalkList(list, sb);
                    sb.Append("}");
                }
                else
                {
                    sb.Append(property.Name + " = new List<" + listTypeName + ">()");
                }
            }
            else if (property.PropertyType.IsEnum)
            {
                sb.AppendFormat("{0} = {1}", property.Name, property.GetValue(obj));
            }
            else
            {
                var value = property.GetValue(obj);
                var isNullable = pt.IsGenericType && pt.GetGenericTypeDefinition() == typeof(Nullable<>);
                if (isNullable)
                {
                    name = pt.GetGenericArguments()[0].Name;
                    if (property.GetValue(obj) == null)
                    {
                        sb.AppendFormat("{0} = null", property.Name);
                        continue;
                    }
                }

                switch (name)
                {
                    case "Int64":
                    case "Int32":
                    case "Int16":
                    case "Double":
                    case "Float":
                        sb.AppendFormat("{0} = {1}", property.Name, value);
                        break;
                    case "Boolean":
                        sb.AppendFormat("{0} = {1}", property.Name, Convert.ToBoolean(value) == true ? "true" : "false");
                        break;
                    case "DateTime":
                        workDt = Convert.ToDateTime(value);
                        sb.AppendFormat("{0} = new DateTime({1},{2},{3},{4},{5},{6})", property.Name, workDt.Year, workDt.Month, workDt.Day, workDt.Hour, workDt.Minute, workDt.Second);
                        break;
                    case "String":
                        sb.AppendFormat("{0} = \"{1}\"", property.Name, value);
                        break;
                    default:
                        // Handles all user classes, should likely have a better way
                        // to detect user class
                        sb.AppendFormat("{0} = ", property.Name);
                        WalkObject(property.GetValue(obj), sb);
                        break;
                }
            }
        }

        sb.Append("}");

        return sb;
    }

    private static StringBuilder WalkList(IList list, StringBuilder sb)
    {
        bool appendComma = false;
        foreach (object obj in list)
        {
            if (appendComma) sb.Append(", ");
            appendComma = true;
            WalkObject(obj, sb);
        }

        return sb;
    }
}



回答2:


I was having the same issue since all my test data was stored as XML. The mentioned Visual Studio Extension (OmarElabd/ObjectExporter) was a good idea, but I needed to generate C# code from in-memory objects at runtime, during unit test execution.

This is what evolved from the original problem: https://www.nuget.org/packages/ObjectDumper.NET/

ObjectDumper.Dump(obj, DumpStyle.CSharp); returns C# initializer code from a variable. Please let me know if you find issues, you might want to report them on github.



来源:https://stackoverflow.com/questions/40524610/is-it-possible-to-auto-generate-object-initialization-code-from-a-runtime-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!