|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionDo you sometimes need a class for a complex data type? I do, but I think it too much work to type it myself. Did you know that .NET Framework has a tool to generate classes from XSD-files? It does, but it requires a command prompt, and every time you change the XML-schema, you have to run it again. Did you know that Visual Studio can work with Custom Tools? It does, but how to create one can be quite a mystery, at least, it was to me. This article will describe how to create a custom tool, install it, and use it. The example can generate classes from XML-schemas which you can incorporate right into Visual Studio 2005 and 2008 (tested). BackgroundVisual Studio uses xsd.exe internally when creating datasets. However, it is also capable of generating strongly typed classes from other XSD-files, which I will demonstrate in this article. In order to create a Custom Tool, you need to create a class that inherits from Using the codeThe projectFirst of all, I created a new class library project, which I called "xsd2class". Because a Custom Tool is a COM object, I checked COM-Visible (in Properties > Application > Assembly Information). Copy the project's GUID, because you'll need it again when we define the Custom Tool's class. This particular COM object is, in fact, a .NET assembly that Visual Studio should be able to find, so it needs to be in the GAC. In order to do that, it needs to be signed, so I created a new key.snk and signed the assembly with it. It saves some work if the assembly is added to the GAC automatically, so I set up a Post-build event with the command line: "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm" /codebase "$(TargetPath)"
The Custom Tool-class skeletonAs stated before, we need a class that is COM-Visible and inherits from namespace Xsd2Class
{
[ComVisible(true)]
[Guid("0e439c89-b1be-489d-a0f4-c2d191db6f9b")]
public class Xsd2Class : Microsoft.CustomTool.BaseCodeGeneratorWithSite
{
}
}
The protected override byte[] GenerateCode(string inputFileName, string inputFileContent)
{
return new byte[] { };
}
In order for the Custom Tool to be recognized by Visual Studio, it needs to be registered in the Registry (where else):
The Generators key has subkeys with GUIDs as names. This is what they mean:
We've got to register our Custom Tool for all three languages. So, in each key, we create another key with the name "Xsd2Class" and the values:
For Visual Studio 2005, the reg file would be: Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\
Generators\{164B10B9-B200-11D0-8C61-00A0C91E29D5}\Xsd2Class]
@="Xsd2Class"
"CLSID"="{0e439c89-b1be-489d-a0f4-c2d191db6f9b}"
"GeneratesDesignTimeSource"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\
Generators\{E6FDF8B0-F3D1-11D4-8576-0002A516ECE8}\Xsd2Class]
@="Xsd2Class"
"CLSID"="{0e439c89-b1be-489d-a0f4-c2d191db6f9b}"
"GeneratesDesignTimeSource"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\
Generators\{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}\Xsd2Class]
@="Xsd2Class"
"CLSID"="{0e439c89-b1be-489d-a0f4-c2d191db6f9b}"
"GeneratesDesignTimeSource"=dword:00000001
For Visual Studio 2008, simply replace "8.0" with "9.0". And now, we've got ourselves a working Custom Tool, doing absolutely nothing! Doing somethingXsd.exeAs said before, xsd.exe is a command line tool. So, let's create a wrapper for it. private void GenerateCodeFile(string inputFileName)
{
FileInfo FI = new FileInfo(inputFileName);
Process proc = new Process();
proc.StartInfo.FileName = @"C:\Program Files\" +
@"Microsoft Visual Studio 8\SDK\v2.0\Bin\xsd.exe";
string Namespace = FI.Name.Substring(0, FI.Name.Length - FI.Extension.Length);
string Language = GetLanguage();
proc.StartInfo.Arguments =
String.Format(@"/c /l:{0} /n:{1} ""{2}"" /out:""{3}""",
Language, Namespace, inputFileName, FI.DirectoryName);
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.RedirectStandardError = true;
proc.Start();
proc.WaitForExit();
if (proc.ExitCode != 0)
{
using (StreamReader SR = proc.StandardError)
{
string Errors = SR.ReadToEnd();
if (!(string.IsNullOrEmpty(Errors)))
{
throw new Exception(Errors);
}
}
}
}
We create a new
Usually, the Language parameter has the same value as the return value of the base class' If we were to have a The filename is the Because we don't want to see the command prompt appearing every time, we set Because we want to know if something goes wrong, we redirect the standard error. Returning the resultsThe private byte[] GetGeneratedFileContent(string inputFileName)
{
string DefaultExtension = GetDefaultExtension();
string outputFilename = inputFileName.Replace(".xsd", DefaultExtension);
FileInfo FI = new FileInfo(outputFilename);
using (StreamReader FS = FI.OpenText())
{
string Contents = FS.ReadToEnd();
return System.Text.Encoding.ASCII.GetBytes(Contents);
}
}
Putting it togetherNow, putting it together... protected override byte[] GenerateCode(string inputFileName,
string inputFileContent)
{
GenerateCodeFile(inputFileName);
byte[] GeneratedFileContent =
GetGeneratedFileContent(inputFileName);
return GeneratedFileContent;
}
... and we've got ourselves a working Custom Tool that can generate source code from an XSD file! User manualThat's all very nice, but how do I use it? Simple :-)
<?xml version="1.0" encoding="utf-8"?>
<xs:schema
id="Books"
targetNamespace="http://tempuri.org/Books.xsd"
elementFormDefault="http://tempuri.org/Books.xsd"
xmlns:mstns="http://tempuri.org/Books.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Books" type="Books">
<xs:annotation>
<xs:documentation>
Collection of Books.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:complexType name="Books">
<xs:sequence>
<xs:element name="Book" type="Book">
<xs:annotation>
<xs:documentation>
A book with a title and an author.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Book">
<xs:sequence>
<xs:element name="Title" type="xs:string">
<xs:annotation>
<xs:documentation>
The Book's main title.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Author" type="xs:string">
<xs:annotation>
<xs:documentation>
The name of the main writer.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
Et voila: your strongly typed
ConclusionFirst of all, I hope you find this tool useful :-). I also hope this will inspire you to write your own Custom Tools, which you hopefully will write an article about. History
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||