3

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"
}"
valorl
  • 1,499
  • 2
  • 14
  • 30
  • Do you have an XSD file for the XML? – Ryan Searle Nov 10 '16 at 10:00
  • should not be a problem, you just have to parse XML and overwrite a constructor to work with the parsed data – Vladimir Nov 10 '16 at 10:00
  • I would go with deserialize into a dynamic type, if you don't want to construct the entire xml structure. – meJustAndrew Nov 10 '16 at 10:06
  • I don't think the XML part is important here. What I want is to somehow from a runtime object instance generate lines of code that would reproduce that object instance. I could then paste those lines of code into my test to initialize that object. It is a thing I would only run once. I am just trying to save myself from writing a lot of object initialization code because the XML file si huge. – valorl Nov 10 '16 at 10:10
  • What you want is called serialization. You happen to want an unusual serialization format in the form of C# code. There's no out of the box solution for that that I know of. It would be far less cumbersome to use an established format like XML or JSON and just deserialize that instead of insisting that plain C# is used in the test. – Jeroen Mostert Nov 11 '16 at 12:10
  • I am using classical XML serialization, and this serialization to C# code is not going to be used in any automated way, I just wanted a manual tool like that to help me setup the test by pasting the code. Thanks for the answer though :) – valorl Nov 11 '16 at 12:14
  • My point is that you can just setup tests by pasting `.Deserialize(@"...")`. Writing something to generate *those* statements is easy. – Jeroen Mostert Nov 11 '16 at 13:07
  • That is true. However, I am not really interested in testing the deserialization but something else that involves that deserialized object and for that it would be really convenient to have initialization code for that object before runtime so when I need to change things, I can just do it in the code and don't have to edit the XML file that is going to get serialized. But I do agree that I have not made myself completely clear about that, it was hard for me to explain. – valorl Nov 11 '16 at 13:11
  • *Using* the deserialization in your unit test is not *testing* it. You would still write your tests to do whatever you want with the object. In fact, if you need to test deserialization, then obviously do not use it in your unit tests because that might cause them to fail. :-) – Jeroen Mostert Nov 11 '16 at 13:18

2 Answers2

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.

thomasgalliker
  • 1,279
  • 13
  • 19
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;
    }
}
Anis Tissaoui
  • 834
  • 1
  • 7
  • 26