Comparison of Microsoft Xsd, Mono Xsd and Dingo
For those who are not familiar with Xml Schema and schema compilers, I thought it would be interesting to document the differences between the existing .NET compilers. Before I started writing Dingo, I looked for a schema compiler for .NET that is capable of generating JAXB style code, but couldn't find one. After a couple of days, I gave up and decided to write my own. The source in the example was generated using .NET 1.1, Mono 1.0 and Dingo Alpha. I used itinerary.xsd that ships with JWSDP 1.4 from Sun.
Mono - TravelType
namespace Schemas {
///
[System.Xml.Serialization.XmlType(Namespace="http://example.org"),
System.Xml.Serialization.XmlRoot("travel",Namespace="http://example.org"),
System.Xml.Serialization.XmlInclude(typeof(PlaneType)),
System.Xml.Serialization.XmlInclude(typeof(AutoType)),
System.Xml.Serialization.XmlInclude(typeof(TrainType))]
public class TravelType {
///
public string origin;
///
public string destination;
}
|
.NET 1.1 - TravelType
using System.Xml.Serialization;
///
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://example.org")]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(TrainType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(AutoType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(PlaneType))]
[System.Xml.Serialization.XmlRootAttribute("travel",
Namespace="http://example.org", IsNullable=false)]
public class TravelType {
///
[System.Xml.Serialization.XmlElementAttribute(
Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string origin;
///
[System.Xml.Serialization.XmlElementAttribute(
Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string destination;
}
|
Dingo Alpha - TravelType
namespace example.org
{
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://example.org")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://example.org",
IsNullable=false)]
public class TravelType
{
#region fields
[System.Xml.Serialization.XmlElementAttribute("origin")]
public string origin;
[System.Xml.Serialization.XmlElementAttribute("destination")]
public string destination;
#endregion
public TravelType()
{
}
#region properties
[System.Xml.Serialization.XmlIgnoreAttribute()]
public string Origin
{
get { return origin; }
set { origin = value; }
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public string Destination
{
get { return destination; }
set { destination = value; }
}
#endregion
#region methods
public string getOrigin()
{
return origin;
}
public void setOrigin(string param)
{
origin = param;
}
public string getDestination()
{
return destination;
}
public void setDestination(string param)
{
destination = param;
}
#endregion
}
}
|
Schema definition - TravelType
<xsd:complexType name="TravelType">
<xsd:annotation><xsd:appinfo><jxb:class
implClass="extend.TravelTypeExtend" interface=""/>
</xsd:appinfo> </xsd:annotation>
<xsd:sequence>
<xsd:element name="origin" type="xsd:string"/>
<xsd:element name="destination" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
|
If we look at the schema definition, both elements within TravelType are strings, but both Mono and .NET generate "XmlIncludeAttribute". Dingo on the other hand, does not generate the include metadata. Although the attributes do not cause a problem, generating it may be questionable. Now, if I set Dingo to generate using JAXB style, we get the following.
Dingo Alpha (JAXB style) - TravelType
namespace example.org
{
public interface TravelType
{
#region methods
public string getOrigin();
public void setOrigin(string param);
public string getDestination();
public void setDestination(string param);
#endregion
}
}
using example.org;
namespace example.org.impl
{
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://example.org")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://example.org",
IsNullable=false)]
public class TravelTypeImpl : TravelType
{
#region fields
[System.Xml.Serialization.XmlElementAttribute("origin")]
public string origin;
[System.Xml.Serialization.XmlElementAttribute("destination")]
public string destination;
#endregion
public TravelType()
{
}
#region properties
[System.Xml.Serialization.XmlIgnoreAttribute()]
public string Origin
{
get { return origin; }
set { origin = value; }
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public string Destination
{
get { return destination; }
set { destination = value; }
}
#endregion
#region methods
public string getOrigin()
{
return origin;
}
public void setOrigin(string param)
{
origin = param;
}
public string getDestination()
{
return destination;
}
public void setDestination(string param)
{
destination = param;
}
#endregion
}
}
|
Using JAXB style code generation, the concrete class uses a different namespace and the class includes the suffix "Impl". What this does is it allows you to use a factory pattern to return different concrete implementations depending on the environment. Say you want to use the same object in a GUI, but you need to add event notification support. On the server side, it wouldn't make sense to use the same concrete class. This is especially true if the server requires adding transactional support. By using a factory pattern and configuration settings, the server and GUI both can use the interface and improve flexibility. The GUI framework should take care of the UI specific needs and the server should take care of the transactional framework. If the GUI needs to add more features to the framework, the concrete class can change without affecting the GUI widgets or the server side.
Here is an example of code generated with interfaceSample plugin. The set method has different implementation than the previous example.
Dingo - with custom plugin
using woolfel.dingo.example;
namespace example.org
{
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://example.org")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://example.org",
IsNullable=false)]
public class TravelType : PropertyChange
{
#region fields
[System.Xml.Serialization.XmlElementAttribute("origin")]
public string origin;
[System.Xml.Serialization.XmlElementAttribute("destination")]
public string destination;
#endregion
public TravelType()
{
}
#region properties
[System.Xml.Serialization.XmlIgnoreAttribute()]
public string Origin
{
get { return origin; }
set { origin = value; }
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public string Destination
{
get { return destination; }
set { destination = value; }
}
#endregion
#region methods
public string getOrigin()
{
return origin;
}
public void setOrigin(string param)
{
if (origin != param ){
string old = origin;
origin = param;
// we would call a notify method of some kind to notify the listeners
}
}
public string getDestination()
{
return destination;
}
public void setDestination(string param)
{
if (destination != param ){
string old = destination;
destination = param;
// we would call a notify method of some kind to notify the listeners
}
}
#endregion
}
}
|
These patterns have been used in system for several years now and are fairly mature at this point. Dingo can also generate the class without XmlSerialization attributes. Although JAXB style code generation produces more code, that doesn't really matter. After all, the code is generated by Dingo and not by hand. I'll do a more complete comparison in the coming weeks.
Revised 9-10-2004