Simple XML to DTO and Back Again

Data transfer objects, especially in this disconnected world we live in, have become quite popular and highly useful. Here's one way I've found to create XML file based DTOs with quick and easy loading and saving from and back to the original XML format. All you need is a schema and a little gem from thinktecture.

A couple of months ago I began working on a project that would require reading and modifying a number of XML based configuration files. I had previously used thinktecture's Web Services Contract First (WSCF) plugin for Visual Studio created by Christian Weyer and Buddhike de Silva to create contract-first web services. At the time I had discovered how convenient it was to use the web service client code generation to generate simple DTO object code as well, but it had to be done in a clever way to allow me to throw away the web service code and just use the DTO object code.

A quick visit to thinktecture's web site and I found that version 0.7 had just been released on October 25, 2006. And to my delight, the major new feature in version 0.7 was the ability to generate data transfer object or data contract code from the UI plugin for Visual Studio 2005. Just install WSCF version 0.7 and then in Visual Studio 2005 create a schema file like the one below. Then right click the XSD file in the solution explorer and select "Generate Data Contract Code" from the menu. A simple dialog like this one pops up.

This will produce a partial class and give you everything you need except a Load and Save method to store the DTO in XML format. It was a simple matter to add the following partial class and the static support class called Serializer as shown below. If you need something like this, I hope this helps.

partial class:

    public partial class Parser
    {
        private static string targetNamespace = "http://mynamespace/v1/parser.xsd";
        public static Parser Load(string fileName)
        {
            if (fileName == null) return null;
            return (Parser)Serializer.Load(fileName, typeof(MyNamespace.Parser), Parser.targetNamespace);
        }
        public void Save(string fileName)
        {
            Serializer.Save(fileName, this, this.GetType(), Parser.targetNamespace);
        }
    }

Serializer class:

    public static class Serializer
    {
        private static XmlSerializerNamespaces GetNamespaces(string targetNamespace)
        {
            XmlSerializerNamespaces ns;
            ns = new XmlSerializerNamespaces();
            ns.Add("", targetNamespace);
            ns.Add("xs", "http://www.w3.org/2001/XMLSchema");
            return ns;
        }

        public static object Load(string fileName, System.Type objType, string targetNamespace)
        {
            string xml = File.ReadAllText(fileName);
            object obj = Serializer.FromXml(xml, objType, targetNamespace);
            return obj;
        }

        public static void Save(string fileName, object obj, System.Type objType, string targetNamespace)
        {
            string xml = Serializer.ToXml(obj, objType, targetNamespace);
            File.WriteAllText(fileName, xml);
        }

        public static string ToXml(object obj, System.Type objType, string targetNamespace)
        {
            XmlSerializer ser;
            ser = new XmlSerializer(objType, targetNamespace);
            MemoryStream memStream;
            memStream = new MemoryStream();
            XmlTextWriter xmlWriter;
            xmlWriter = new XmlTextWriter(memStream, Encoding.UTF8);
            xmlWriter.Formatting = Formatting.Indented;
            xmlWriter.Indentation = 1;
            xmlWriter.IndentChar = '\t';
            xmlWriter.Namespaces = true;
            ser.Serialize(xmlWriter, obj, Serializer.GetNamespaces(targetNamespace));
            xmlWriter.Close();
            memStream.Close();
            string xml;
            xml = Encoding.UTF8.GetString(memStream.GetBuffer());
            xml = xml.Substring(xml.IndexOf('<'));
            xml = xml.Substring(0, (xml.LastIndexOf('>') + 1));
            return xml;
        }

        public static object FromXml(string xml, System.Type objType, string targetNamespace)
        {
            XmlSerializer ser;
            ser = new XmlSerializer(objType, targetNamespace);
            StringReader stringReader;
            stringReader = new StringReader(xml);
            XmlTextReader xmlReader;
            xmlReader = new XmlTextReader(stringReader);
            object obj;
            obj = ser.Deserialize(xmlReader);
            xmlReader.Close();
            stringReader.Close();
            return obj;
        }
    }

example schema:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="parser"
             targetNamespace="http://mynamespace/v1/parser.xsd"
             elementFormDefault="qualified"
             xmlns="http://mynamespace/v1/parser.xsd"
             xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="Parser">
        <xs:complexType>
            <xs:all>
                <xs:element ref="ColumnWidths" minOccurs="0" maxOccurs="1" />
                <xs:element name="ExpectedColumnCount" type="xs:int" />
                <xs:element name="MaxBufferSize" type="xs:int" />
                <xs:element name="MaxRows" type="xs:int" />
                <xs:element name="SkipDataRows" type="xs:int" />
                <xs:element name="FirstRowHasHeader" type="xs:boolean" />
                <xs:element name="TrimResults" type="xs:boolean" />
                <xs:element name="IncludeFileLineNumber" type="xs:boolean" />
                <xs:element name="FixedWidth" type="xs:boolean" />
                <xs:element name="RowDelimiter" type="xs:string" />
                <xs:element name="ColumnDelimiter" type="xs:string" />
                <xs:element name="TextQualifier" type="xs:int" />
                <xs:element name="EscapeCharacter" type="xs:int" />
                <xs:element name="CommentCharacter" type="xs:int" />
            </xs:all>
        </xs:complexType>
    </xs:element>
    <xs:element name="ColumnWidths">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="ColumnWidth" minOccurs="0" maxOccurs="unbounded" type="xs:int" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

 

Beware the Arbitration Agreement

I've been busy. Yeah. Sad excuse, but true.
 
On November 7, 2006, about two months after I was hired, my employer "invited" me to resign my position because I refused to sign a mandatory arbitration agreement which among other things included the following language: "I understand that by agreeing to this binding arbitration provision, both I and [company name] give up our respective rights to a trial by jury."

I told my employer that I didn't think I should have to waive my constitutional right to petition the government for redress just because so many others abuse that right. I had researched the issue on the Internet and found that these agreements are enforced by the courts and that in about 99% of all cases, the employee loses, regardless of the issues and facts.

I was annoyed. More at the law than at my employer. I know the law stands behind employers on this issue. And I even support the idea of arbitration as a first option, but I cannot abide the idea of just waiving my right to go to court just to keep a job. I don't think the law should allow an employer to require such a concession upon employees, but it does. Specifically, as long as both parties give up the same rights, the contract is enforceable.

This would be just fine except for the fact that in arbitration, the little guy is viewed by most arbiters (usually retired judges) as the money grubbing whiner and the employer as the victim of the evil, greedy employee. So you give up the same rights but you put yourself, as an employee, at a significant disadvantage if you run into some dispute with an employer.

All that said, I've never been sued by an employer and I've never sued an employer. Still, if I had to, I'd like to preserve the option of having a real court and a real jury hear my case rather than an arbiter who answers to no one regardless of his or her conduct and decisions in the face of the evidence. Take those odds? No thanks.

I was lucky. I found another job the same day I was "asked" to leave which happily pays even better. And I've been super busy with the new gig ever since. Not everyone has the same opportunity and flexibility that I enjoy, so I recognize this development as a true blessing.

Since that day, I've spent an hour or so contacting legislators about the issue. They are generally either indifferent or completely ignorant or in some cases both. Senator Hatch sent me a nice, completely off-base form letter reply referring me to legal counsel despite the fact that I had just asked for his opinion on whether employers should be allowed to continue this practice and whether he would support legislation to prohibit it. Many others just never responded. It's pretty sad when elected officials care so little about the way that employers are now forcing their employees to give up their constitutionally protected rights just because they are afraid of employees who abuse those rights.

It's typical fare for our culture. Punish those who have done nothing wrong in the false hope of protecting yourself from the real bad guys. Similar examples are not difficult to find. Such draconian practices are not needed. If you're going to get sued by employees, you're going to get sued. And if it happens a lot, you might want to consider changing your behavior and/or changing who and how you hire.

If you're reading this and you've signed employment documents without really reading them, you may have signed a similar document. I recommend reading every document your employer "invites" you to sign. Despite your excitement to have a new job and your high opinion of the people you'll be working for, you may be surprised at what they've asked you to agree to. There's only one way out of such an agreement. Don't sign it in the first place. I really liked the people at the former job, but regardless of my regard for them, I was not about to give up my rights in order to work for them.

Of course, everything I've said here is my own opinion. I'm sure my former employer sees it completely differently. I bear them no ill will and certainly have no plans to waste time and energy on the lawsuit that so many of my friends have recommended that I bring against them. I would just hope that they would see their actions for the paranoia I believe it to be and revise their agreements with their existing employees. I think it would be the right and moral thing to do. But that's up to them.

With that all said, I'll get back to coding and promise some real .NET coding posts here in the future.