views:

176

answers:

1

I'm building a service that returns an XML (no SOAP, no ATOM, just plain old XML). Say that I have my domain objects already filled with data and just need to transform them to the XML format. What options do I have on .NET?

Requirements:

  • The transformation is not 1:1. Say that I have an Address property of type Address with nested properties like Line1, City, Postcode etc. This may need to result in an XML like <xaddr city="...">Line1, Postcode</xaddr>, i.e. quite different.
  • Some XML elements/attributes are conditional, for example, if a Customer is under 18, the XML needs to contain some additional information.
  • I only need to serialize the objects to XML, the other direction (XML to objects) is not important
  • Some technologies, i.e. Data Contracts use .NET attributes. Other means of configuration (external XML config, buddy classes etc.) would be a plus.

Here are the options as I see them as the moment. Corrections / additions will be very welcome.

  • String concatenation - forget it, it was a joke :)
  • Linq 2 XML - complete control but quite a lot of hand written code, would need good suite of unit tests
  • View engines in ASP.NET MVC (or even Web Forms theoretically), the logic being in controllers. It's a question how to structure it, I can have simple rules engine in my controller(s) and one view template per each possible output, or have the decision logic directly in the template. Both have upsides and downsides.
  • XML Serialization - I'm not sure about the flexibility here
  • Data Contracts from WCF - not sure about the flexibility either, plus would they work in a simple ASP.NET MVC app (non-WCF service)? Are they a super-set of the standard XML serialization now?
  • If it exists, some XML-to-object mapper. The more I think about it the more I think I'm looking for something like this but I couldn't find anything appropriate.

Any comments / other options?

+6  A: 

Xml Serialization works nicely. You use attributes to configure it.

You can conditionally include elements.

EDIT: I've updated the code to reflect your updated question.

[XmlRoot("pdata")]  // this element name used if serialized to doc root
public class PersonalData
{
     [XmlElement]  // with no name here, elt name = prop name
     public string Name;

     [XmlElement]  // with no name here, elt name = prop name
     public int Age;

     [XmlElement("xaddr")]  // override xml element name 
     public AddressData Address;

     [XmlElement] 
     public Under18Info Other {get; set;}

     // serialize the above element, only if Age < 18
     [XmlIgnore] // do not serialize the *Specified" property in any case
     private bool OtherSpecified {
        get { return Age < 18; }
     }
}

public class AddressData
{
     [XmlIgnore]   // do not serialize (see Composite prop)
     public string Line1 { get; set;}

     [XmlAttribute("city")] // serialize as an attribute on the parent 
     public string City {get; set;}

     [XmlIgnore]   // do not serialize 
     public string Postcode {get; set;}

     [XmlText]   // serialize as the Text node
     public string Composite 
     {
        get { return Line1 + ", " + Postcode; }
        set {
          var split = value.Split(',');
          Line1 = split[0];
          Postcode= split[1];
        }
     }
}

Then, to serialize an instance of that to a string, for example:

var pdata = new PersonalData
    {
        Name = "Gordon Brown",
        Age = 57,
        Address = new AddressData
        {
            Line1 = "10 Downing St.",
            Postcode = "1QR 3E4",
            City = "London"
        }
    };

var ns= new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add( "", "");
var s1 = new XmlSerializer(typeof(PersonalData));
var builder = new System.Text.StringBuilder();
var xmlws = new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
using ( var writer = System.Xml.XmlWriter.Create(builder, xmlws))
{
    s1.Serialize(writer, pdata, ns);
}
string xml = builder.ToString();

Results:

<pdata>
  <Name>Gordon Brown</Name>
  <Age>57</Age>
  <xaddr city="London">10 Downing St., 1QR 3E4</xaddr>
</pdata>
Cheeso
In worst case, if XML Serializer doesn't give what You want, You can always write You own serializer.
Rafal Ziolkowski
Sorry I have incorrectly formatted the XML sample (adress etc.) but thanks for your response anyway. Don't you know, are Data Contracts similar or maybe even more flexible than this old XML serialization?
Borek
You can also use the DataContractSerializer. It's similar. In some ways DCS is more flexible than XmlSerializer; in most ways, less. http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/
Cheeso
I updated the code in light of your updated question. I suggest you use XmlTextAttribute on a synthetic composite property in order to serialize the address the way you want.
Cheeso
The example looks simple enough, however, I don't quite like how presentation concerns leak into the model here. For instance, I may have more than 1 possible XML representation (say one for public consumption and one for internal storage). Or the business rules might come from some other place. Does XML serialization have answers to these problems or should I look elsewhere?
Borek
With XmlSerializer, you can specify "Attribute overrides" to override those present in the source code.
Cheeso