Automate XSD code generation in Visual Studio

This is a technical post and requires knowledge of software development.

If you are like me, then you quickly get tired of creating classes that just represent a DTO when the XML schema would be easier to use.  Here’s a trick to help you if you don’t have add-ins to do the job for you.

Outcome

The result of this process is:

  1. Each time you want a new style of XSD to code generation you’ll need to adjust the project file, but only if certain things have changed such as the namespace mappings
  2. Automatic generation of code from an XSD document using either XmlSerializer or DataContractSerializer
  3. Version control applied solely or primarily to the XSD, and the possibility of disconnecting the automatic generation just by changing the build mode
  4. Only applies to files you choose that are actually in your project

Process

The process is as follows:

  1. Create a project in Visual Studio and add an XSD file.  The extension can be anything but will be used by the project file to determine the transformation to use
  2. Complete an initial XSD, it can be changed later
  3. Add a code file with the same name as the XSD but with .cs appended to the end (e.g. example.xsd.cs)
  4. Edit the project file itself in XML.  You can use VS if you unload the project from the solution (not closing the solution) or you can use a text editor.  VS will notice the changes and offer to reload the project
  5. Set the build type on the XSD file to “Content” (this allows the project file to locate it)
  6. Build the project – this generates the code file

Warning: if you make mistakes in the project file then VS may refuse to load it!

Example

In the following, I’ll use the file extension “.dcxsd” (Data Contract XSD) for the XSD.  The XSD target namespace I’ll use is “urn:tempuri.org”.  You map a single XML namespace to a single .NET namespace in this process.  You’ll need to make more complex changes to the project file if you need something more complex (such as a different approach for each file).  You can use multiple XSD files, each generating code in a different .NET namespace.

The project file

Be careful of line breaks.  This post has been edited for the web and so line breaks have been inserted where they would not normally be.

The changes to the project file create a task to perform the automatic generation of code.  This process uses the Microsoft svcutil.exe tool to perform the generation.

The following changes must be in the project file before the Target elements:

  <PropertyGroup>
    <WIN_SDK_PATH>$(Registry:HKEY_LOCAL_MACHINE\→
      SOFTWARE\Microsoft\Microsoft SDKs\→
      Windows@CurrentInstallFolder)</WIN_SDK_PATH>
    <WIN_SDK_BIN_PATH>→
      $(WIN_SDK_PATH)\bin</WIN_SDK_BIN_PATH>
    <SVCUTIL>"$(WIN_SDK_BIN_PATH)\svcutil.exe"</SVCUTIL>
  </PropertyGroup>

This provides properties that can be used to invoke svcutil.exe.

Next, we add another property (immediately below) that specifies the mapping between XML namespaces and .NET namespaces for code generation. Multiple mappings can be made. Refer to the documentation on svcutil.exe for more information.

  <PropertyGroup>
    <!-- Use the following options to pass
         serialization options to SVCUTIL -->
    <DCSerializationSchemaMapping>→
      "/n:urn:tempuri.org,MyNamespace.DTO"→
    </DCSerializationSchemaMapping>
  </PropertyGroup>

Now, add a new target block.  This should come before the existing target blocks but after the other elements.  Note that I’ve inserted line breaks to assist in reading on this blog, but command text is a command line and so shouldn’t include line breaks.

  <Target Name="XSDSerialization">
    <ItemGroup>
      <DCSerializationItems
        Include="@(Content -> '%(fullpath)')"
        Condition="'%(Extension)' == '.dcxsd'" />
    </ItemGroup>
    <Exec Command="attrib -r →
        &quot;%(DCSerializationItems.fullpath).cs&quot;"
        />
    <Exec Outputs="%(DCSerializationItems.fullpath).cs"
       Command="$(SVCUTIL) /dconly →
         /serializer:DataContractSerializer →
         /serializable →
         $(DCSerializationSchemaMapping) →
         /language:C# →
         &quot;/out:%(DCSerializationItems.fullpath).cs&quot; →
         &quot;%(DCSerializationItems.fullpath)&quot;"
       />
  </Target>

The ItemGroup identifes XSD files that should be transformed.  There are two conditions.  First, the file must be in the Content build set.  Visual Studio supports three standard build sets: None, Content and Compile.  Second, the file extension must be “.dcxsd” as per my example.

Next are two commands, the first one removes the read-only flag from the XSD file – this is needed if your version control software marks files as read only.  This approach is necessary because this relatively simplistic change to the build process regenerates the file on each build.

The second command invokes svcutil.exe.  The use of the %(DCSerializationItems.fullpath) causes the command to be executed once for each file in the ItemGroup.  This is an important aspect of MSBuild that is not documented particularly well!

Finally, we need to cause the target to be part of the build process.  Helpfully, Visual Studio provides a default “BeforeBuild” target that can be used for this purpose.  Typically, this section is commented out so remember to remove the XML comments:

  <Target Name="BeforeBuild" DependsOnTargets="XSDSerialization">
  </Target>

Save the project file and give it a go!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s