Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX

By submitting your information, you agree that devx.com may send you DevX offers via email, phone and text message, as well as email offers about other products and services that DevX believes may be of interest to you. DevX will process your information in accordance with the Quinstreet Privacy Policy.


Build Custom Code Generators in C# : Page 3

You don't have to rely on libraries and frameworks to avoid writing repetitive code; instead, learn to generate such code automatically, using a custom input format to describe the code you want to generate.




Application Security Testing: An Integral Part of DevOps

Example Input and Output
The code below contains several samples of the input XML and the resuting generated code. Notice how the frame around the reading of each of these "property reads" is the same, but the part in the middle varies based on both the type and the cardinality:

Input XML:

<Property> <DeclaringTypeName>Address</DeclaringTypeName> <PropertyName>State</PropertyName> <PropType>Atom</PropType> <AtomicType>String</AtomicType> </Property>

Generated Code:

</i> if (reader.LocalName == "State") { // generated code for 'State' by GeneratePropertyFromXml() readString = reader.ReadString(); if (!string.IsNullOrEmpty(readString)) this.State = readString; continue; }

Input XML:

<Property> <DeclaringTypeName>Address</DeclaringTypeName> <PropertyName>Zip</PropertyName> <PropType>Atom</PropType> <AtomicType>Int32</AtomicType> </Property>

Generated Code:

if (reader.LocalName == "Zip") { // generated code for 'Zip' by GeneratePropertyFromXml() readString = reader.ReadString(); if (!string.IsNullOrEmpty(readString)) this.Zip = XmlConvert.ToInt32(readString); continue; }

Input XML:

<Property> <DeclaringTypeName>Address</DeclaringTypeName> <PropertyName>ArrayDemo</PropertyName> <PropType>ListAtoms</PropType> <AtomicType>Int32</AtomicType> </Property>

Generated Code:

if (reader.LocalName == "ArrayDemo") { // generated code for 'ArrayDemo' by GeneratePropertyFromXml() readString = reader.ReadString(); if (!string.IsNullOrEmpty(readString)) this.ArrayDemo.Add(XmlConvert.ToInt32(readString)); continue; }

For some operations, it doesn't make sense to write entire methods within a function, because each variation will differ only slightly (such as by System.Type or by property name.) Therefore, for these it's best to write short reusable helper methods that return small strings that the calling method can insert into the output. For example, this GetTypeName() function is a helper method that takes a PropertyDefinition as input and returns the .NET type name for the property it represents.

Author's Note: I purposely named the DataType enumerations to match their .NET counterparts. This makes outputting the name a simple conversion to string operation, as you can see in the code pDef.AtomicType.ToString().

private static string GetTypeName(PropertyDefinition pDef) { if (pDef.PropType == PropertyType.Composite || pDef.PropType == PropertyType.ListComposites) { return pDef.CompositeTypeName; } return pDef.AtomicType.ToString(); }

The code near the bottom of the hierarchy of methods generates the actual nitty-gritty output. Because the higher level (more generic) methods pass the IndentingWriter down through each step, all the output will go into the same place in the right order.

static void GenerateReadAtmoicListValue(PropertyDefinition pDef, IndentingWriter tw) { tw.WriteLine("readString = reader.ReadString();"); tw.WriteLine("if(!string.IsNullOrEmpty(readString))"); tw.Indent(); tw.WriteLine("this.{0}.Add({1});", pDef.PropertyName, ConvertAtomFromXml(pDef)); tw.OutDent(); }

The helper method ConvertAtomFromXml in the preceding method is another helper method because, again, this conversion will be very similar for all properties.

The downloadable source includes three test projects: one for testing the generator (TestGenerator.csproj) and another for testing the serialization of the generated classes (TestSerialization.csproj), as well as the example Web service.

Author's Note: In the projects, the two "...ODef.xml" files, AddressODef.xml and PersonODef.xml are marked to be copied to the output directory, so the project code can refer to them without any extra path info. However, the generated output files are coded to go into the Serialization test directory, so if you don't preserve the directory structure of the demo projects, it won't work. It's not apocalyptic, but just be aware that you'll have to modify those directory values to get the demo working if you re-arrange things.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



We have made updates to our Privacy Policy to reflect the implementation of the General Data Protection Regulation.
Thanks for your registration, follow us on our social networks to keep up-to-date