Comment sérialiser un object en code d’initialiseur d’object C #?

Je cherche à prendre un object en mémoire (ou la sérialisation JSON d’un object) et à émettre du code C # pour produire un object équivalent.

Cela serait utile pour extraire des exemples connus d’un référentiel afin de les utiliser comme points de départ dans les tests unitaires. Nous avons envisagé de désérialiser JSON, mais le code C # aurait un avantage en ce qui concerne le refactoring.

Si votre modèle est simple, vous pouvez utiliser la reflection et un générateur de chaînes pour générer directement du C #. Je l’ai fait pour renseigner les données de test unitaire exactement comme vous en avez parlé.

L’exemple de code ci-dessous a été écrit en quelques minutes et a généré un initialiseur d’object nécessitant quelques modifications de la main. Une fonction plus robuste / moins boguée pourrait être écrite si vous envisagez de le faire beaucoup.

La seconde fonction est récursive: elle parcourt toutes les listes de l’object et génère du code pour celles-ci également.

Avertissement: Cela a fonctionné pour mon modèle simple avec des types de données de base. Il générait du code qui nécessitait un nettoyage, mais me permettait de passer rapidement à autre chose. C’est seulement ici pour servir d’exemple de la façon dont cela pourrait être fait. Espérons que cela inspire quelqu’un pour écrire le leur.

Dans mon cas, j’avais une instance de ce grand dataset (résultats) qui a été chargée à partir de la firebase database. Afin de supprimer la dépendance à la firebase database de mon test unitaire, j’ai passé l’object à cette fonction qui a recraché le code qui m’a permis de simuler l’object dans ma classe de test.

private void WriteInstanciationCodeFromObject(IList results) { //declare the object that will eventually house C# initialization code for this class var testMockObject = new System.Text.SsortingngBuilder(); //start building code for this object ConstructAndFillProperties(testMockObject, results); var codeOutput = testMockObject.ToSsortingng(); } private void ConstructAndFillProperties(SsortingngBuilder testMockObject, IList results) { testMockObject.AppendLine("var testMock = new " + results.GetType().ToSsortingng() + "();"); foreach (object obj in results) { //if this object is a list, write code for it's contents if (obj.GetType().GetInterfaces().Contains(typeof(IList))) { ConstructAndFillProperties(testMockObject, (IList)obj); } testMockObject.AppendLine("testMock.Add(new " + obj.GetType().Name + "() {"); foreach (var property in obj.GetType().GetProperties()) { //if this property is a list, write code for it's contents if (property.PropertyType.GetInterfaces().Contains(typeof(IList))) { ConstructAndFillProperties(testMockObject, (IList)property.GetValue(obj, null)); } testMockObject.AppendLine(property.Name + " = (" + property.PropertyType + ")\"" + property.GetValue(obj, null) + "\","); } testMockObject.AppendLine("});"); } } 

Il existe une extension intéressante de Visual Studio qui répond à cela; l’ exportateur d’object . Il permet la sérialisation d’un object en mémoire dans le code d’initialisation d’object C #, JSON et XML. Je ne l’ai pas encore essayé, mais j’ai l’air insortingguant. mettra à jour après l’avoir essayé.

Je suis également novice dans ce domaine, mais je devais aussi prendre un object C # qui définissait une hiérarchie et l’extraire dans un initialiseur d’object pour faciliter la configuration d’un test unitaire. J’ai beaucoup emprunté à ce qui précède et je me suis retrouvé avec ça. J’aimerais améliorer la gestion de la reconnaissance des classes d’utilisateurs.

http://github.com/jefflomax/csharp-object-to-object-literal/blob/master/Program.cs

 using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ObjectInitializer { public class Program { public enum Color { Red, Green, Blue, Yellow, Fidget } ; public class Foo { public int FooId { get; set; } public ssortingng FooName { get; set; } } public class Thing { public int ThingId { get; set; } public ssortingng ThingName { get; set; } public List Foos { get; set; } } public class Widget { public long Sort { get; set; } public char FirstLetter { get; set; } } public class TestMe { public Color Color { get; set; } public long Key { get; set; } public ssortingng Name { get; set; } public DateTime Created { get; set; } public DateTime? NCreated { get; set; } public bool Deleted { get; set; } public bool? NDeleted { get; set; } public double Amount { get; set; } public Thing MyThing { get; set; } public List Things { get; set; } public List Widgets { get; set; } } static void Main(ssortingng[] args) { var testMe = new TestMe { Color = Program.Color.Blue, Key = 3, Name = "SAK", Created = new DateTime(2013,10,20,8,0,0), NCreated = (DateTime?)null, Deleted = false, NDeleted = null, Amount = 13.1313, MyThing = new Thing(){ThingId=1,ThingName="Thing 1"}, Things = new List { new Thing { ThingId=4, ThingName="Thing 4", Foos = new List { new Foo{FooId=1, FooName="Foo 1"}, new Foo{FooId=2,FooName="Foo2"} } }, new Thing { ThingId=5, ThingName="Thing 5", Foos = new List() } }, Widgets = new List() }; var objectInitializer = ToObjectInitializer(testMe); Console.WriteLine(objectInitializer); // This is the returned C# Object Initializer var x = new TestMe { Color = Program.Color.Blue, Key = 3, Name = "SAK", Created = new DateTime(2013, 10, 20, 8, 0, 0), NCreated = null, Deleted = false, NDeleted = null, Amount = 13.1313, MyThing = new Thing { ThingId = 1, ThingName = "Thing 1", Foos = new List() }, Things = new List { new Thing { ThingId = 4, ThingName = "Thing 4", Foos = new List { new Foo { FooId = 1, FooName = "Foo 1" }, new Foo { FooId = 2, FooName = "Foo2" } } }, new Thing { ThingId = 5, ThingName = "Thing 5", Foos = new List() } }, Widgets = new List() }; Console.WriteLine(""); } public static ssortingng ToObjectInitializer(Object obj) { var sb = new SsortingngBuilder(1024); sb.Append("var x = "); sb = WalkObject(obj, sb); sb.Append(";"); return sb.ToSsortingng(); } private static SsortingngBuilder WalkObject(Object obj, SsortingngBuilder 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; } } } 

Je suis tombé sur cela en cherchant le même type de méthode que Matthew a décrit, et la réponse d’Evan m’a inspiré pour écrire ma propre méthode d’extension. Il génère un code C # compilable sous forme de chaîne pouvant être copiée / collée dans Visual Studio. Je ne me suis pas soucié de la mise en forme, j’ai simplement sorti le code sur une ligne et utilisé ReSharper pour le formater correctement. Je l’ai utilisé avec certains gros DTO que nous faisions circuler et jusqu’à présent, cela fonctionne à merveille.

Voici la méthode d’extension et quelques méthodes d’assistance:

 public static ssortingng ToCreationMethod(this object o) { return Ssortingng.Format("var newObject = {0};", o.CreateObject()); } private static SsortingngBuilder CreateObject(this object o) { var builder = new SsortingngBuilder(); builder.AppendFormat("new {0} {{ ", o.GetClassName()); foreach (var property in o.GetType().GetProperties()) { var value = property.GetValue(o); if (value != null) { builder.AppendFormat("{0} = {1}, ", property.Name, value.GetCSharpSsortingng()); } } builder.Append("}"); return builder; } private static ssortingng GetClassName(this object o) { var type = o.GetType(); if (type.IsGenericType) { var arg = type.GetGenericArguments().First().Name; return type.Name.Replace("`1", ssortingng.Format("<{0}>", arg)); } return type.Name; } 

La méthode GetCSharpSsortingng contient la logique et est ouverte à l’extension pour tout type particulier. C’était suffisant pour moi qu’il gère les chaînes, les entiers, les décimales, les dates de tout ce qui implémente IEnumerable:

 private static ssortingng GetCSharpSsortingng(this object o) { if (o is Ssortingng) { return ssortingng.Format("\"{0}\"", o); } if (o is Int32) { return ssortingng.Format("{0}", o); } if (o is Decimal) { return ssortingng.Format("{0}m", o); } if (o is DateTime) { return ssortingng.Format("DateTime.Parse(\"{0}\")", o); } if (o is IEnumerable) { return Ssortingng.Format("new {0} {{ {1}}}", o.GetClassName(), ((IEnumerable)o).GetItems()); } return ssortingng.Format("{0}", o.CreateObject()); } private static ssortingng GetItems(this IEnumerable items) { return items.Cast().Aggregate(ssortingng.Empty, (current, item) => current + Ssortingng.Format("{0}, ", item.GetCSharpSsortingng())); } 

J’espère que quelqu’un trouvera cela utile!

Il est possible que l’object ait un TypeConverter qui prenne en charge la conversion en InstanceDescriptor , ce que le concepteur WinForms utilise lors de l’émission de code C # pour générer un object. S’il ne peut pas convertir en InstanceDescriptor, il tentera d’utiliser un constructeur sans paramètre et simplement définir des propriétés publiques. Le mécanisme InstanceDescriptor est pratique car il vous permet de spécifier diverses options de construction, telles que des constructeurs avec des parameters ou même des appels de méthodes statiques.

J’ai un code utilitaire que j’ai écrit qui émet le chargement d’un object en mémoire à l’aide de IL, qui suit essentiellement le modèle ci-dessus (utilisez InstanceDescriptor si possible et, sinon, écrivez simplement des propriétés publiques). Notez que cela ne produira qu’un object équivalent si InstanceDescriptor est correctement implémenté ou si la définition de propriétés publiques suffit pour restaurer l’état de l’object. Si vous émettez IL, vous pouvez également sortingcher et lire / écrire les valeurs de champ directement (c’est ce que le DataContractSerializer prend en charge), mais il y a beaucoup de cas difficiles à prendre en compte.

Il existe une solution similaire à celle proposée par Evan , mais un peu mieux adaptée à ma tâche particulière.

Après avoir joué un peu avec CodeDOM et Reflection, il s’est avéré que ce serait trop compliqué dans mon cas.

L’object a été sérialisé en XML. La solution naturelle a donc été d’utiliser XSLT pour le transformer simplement en expression de création d’object.

Bien sûr, il ne couvre que certains types de cas, mais peut-être fonctionnera pour quelqu’un d’autre.

Voici une mise à jour de la solution de @ revlucio qui prend en charge les booléens et les énumérations.

 public static class ObjectInitializationSerializer { private static ssortingng GetCSharpSsortingng(object o) { if (o is bool) { return $"{o.ToSsortingng().ToLower()}"; } if (o is ssortingng) { return $"\"{o}\""; } if (o is int) { return $"{o}"; } if (o is decimal) { return $"{o}m"; } if (o is DateTime) { return $"DateTime.Parse(\"{o}\")"; } if (o is Enum) { return $"{o.GetType().FullName}.{o}"; } if (o is IEnumerable) { return $"new {GetClassName(o)} \r\n{{\r\n{GetItems((IEnumerable)o)}}}"; } return CreateObject(o).ToSsortingng(); } private static ssortingng GetItems(IEnumerable items) { return items.Cast().Aggregate(ssortingng.Empty, (current, item) => current + $"{GetCSharpSsortingng(item)},\r\n"); } private static SsortingngBuilder CreateObject(object o) { var builder = new SsortingngBuilder(); builder.Append($"new {GetClassName(o)} \r\n{{\r\n"); foreach (var property in o.GetType().GetProperties()) { var value = property.GetValue(o); if (value != null) { builder.Append($"{property.Name} = {GetCSharpSsortingng(value)},\r\n"); } } builder.Append("}"); return builder; } private static ssortingng GetClassName(object o) { var type = o.GetType(); if (type.IsGenericType) { var arg = type.GetGenericArguments().First().Name; return type.Name.Replace("`1", $"<{arg}>"); } return type.Name; } public static ssortingng Serialize(object o) { return $"var newObject = {CreateObject(o)};"; } }