This is a response to John Sonmez post “Don’t Parse That Xml”.
I am not going to say that John is wrong on the generation part, it’s a good tip but he seems to totally have forgotten about LINQ to XML. I have had to work with some API’s that don’t benefit from that type of generated schema and classes because request / response can be different. Yes there is a schema for it but using that schema means adding god knows how many unneeded classes. For what reason should I then use this approach? Just to not have to parse the xml by hand? That’s like saying you should always use domain driven design even for displaying a simple table with some data in it on the web. What if all I need is that simple table with 4 columns? I would 10 times out of 10 drag and drop onto the designer window. Secondly I believe that all developers should get some hands on experience with reading/writing xml because it’s knowledge that will be of good use for many years to come.
But I am not just going to state my opinion I am going to give you some actual code. I am not interested in hearing things like –”Dude, you are using magic strings”. If you don’t like strings move on. And if you want to know how I handle getting xml values etc have a look over at github
Let’s take the following as an example. I want to search for a person in a country in some external API and check if that person actually exist. The external API is really complex, it supports searching in most major countries and the API does some deciding for you based on the data you provide so it needs to be dynamic.
The service has one single method and it takes a xml-as-string. I have a schema but as I said previously but it contains a lot of overhead.
<request type="search">
<country>SE</country>
<names>
<firstName>Mikael</firstName>
<lastName>Henriksson</lastName
<middleName>Kristofer</middleName>
</names>
<addresses>
<address type="default">
<streetName>Stallvägen</streetName>
<streetNumber>48</streetNumber>
<city>Växjö</city>
</address>
</addresses>
<!-- Rest of request removed for brevity -->
</request>
I could take another aproach here and use LINQ to XML. What I want here is to take some internal object and map it to the Xml. There are a multitude of ways to do this but I’ll stick with the simplest one possible for the simple reason that yes I need to do some logic on the server side to map the classes but ensuring that everything goes ok is down to unit tests in my humble opinion. Coming up is an incredibly simple class called RequestBuilder in the serialization scenario I still need to transfer data to the objects before I can serialize them so I don’t see any gains really unless of course I can use something like AutoMapper to transfer the data automatically. Here is the class:’
public class RequestBuilder
{
readonly XDocument _doc = new XDocument();
readonly User _user;
public RequestBuilder(User user) {
_user = user;
}
public XDocument BuildRequestDocument() {
_doc.Add(BuildCountry(),
BuildPerson(),
BuildAddress());
return _doc;
}
public XElement BuildAddress()
{
var addresses = new XElement("addresses");
foreach (var address in _user.Addresses) {
addresses.Add(new XElement("address",
new XAttribute("type", address.AddressType),
new XElement("streetName", address.StreetName),
new XElement("streetNumber", address.StreetNumber),
new XElement("city", address.City)));
}
return addresses;
}
public XElement BuildPerson() {
return new XElement("person",
new XElement("firstName", _user.FirstName),
new XElement("middleName", _user.MiddleName),
new XElement("lastName", _user.LastName));
}
public XElement BuildCountry() {
return new XElement("country", ParseCountry());
}
private string ParseCountry() {
var defaultAddress =
_user.Addresses.Single(x =>
x.AddressType == AddressType.Default);
switch (defaultAddress.Country) {
case "Sverige":
return "SE";
}
throw new NotImplementedException("Could not map the countries");
}
}
Just to make sure we are on the same page before I continue I’d like you to fully understand the two classes address and user.
public class User
{
public User() {
Addresses = new List<Address>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public IEnumerable<Address> Addresses { get; private set; }
}
public class Address
{
public AddressType AddressType { get; set; }
public string StreetName { get; set; }
public string StreetNumber { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
Now I can easily test this with something like the following
[TestFixture]
public class SearchRequestBuilderTests
{
[TestFixtureSetUp]
public void FixtureSetUp()
{
InitUser();
_builder = new RequestBuilder(_user);
}
[Test]
public void Can_build_person()
{
var element = _builder.BuildPerson();
Assert.That(
element.Element("firstName").Value == "Mikael" &&
element.Element("lastName").Value == "Henriksson" &&
element.Element("middleName").Value == "Kristofer");
}
private void InitUser()
{
_user = new User
{
FirstName = "Mikael",
LastName = "Henriksson",
MiddleName = "Kristofer"
};
_user.Add(new Address
{
AddressType = AddressType.Default,
City = "Växjö",
Country = "Sverige",
StreetName = "Stallvägen",
StreetNumber = "48"
});
}
static User _user;
static RequestBuilder _builder;
}
Quick summary
I never said that John’s suggestion was a bad one. I just said that there is in my opinion a really good alternative if you don’t need all the classes that the schema generation would produce. I really like his post there are a lot of people that don’t know that this can be done from a single xml document. If you need the whole darn thing and the classes makes sense then use it! But if you only need a subset of the information or you find that the classes generated are just too complicated to be used then the above is a viable solution.