Improve Your Model Classes with OOP – Part 3: Serialization

In part 1 of this series, I discussed building model classes properly with Object-Oriented Programming (OOP), specifically encapsulation that must always include data validation. In part 2 I  showed you constructors, interfaces and more that you should implement in your model classes. In this article, we will build upon the Person.cs type from part 2 and end up with a new Person type ready for serialization. Serialization is something important to think about for good model class design, even though it’s not really part of OOP. If you implement what I discuss, other developers using these models will really appreciate the little bit of extra work.

In computer science, in the context of data storage, serialization is the process of translating data structures or object state into a format that can be stored (for example, in a file or memory buffer) or transmitted (for example, across a network connection link) and reconstructed later (possibly in a different computer environment). It goes on to add this for OOP: Serialization of object-oriented objects does not include any of their associated methods with which they were previously linked.

WikiPedia

Since these articles are focusing on model classes, the Wikipedia definition fits since these types of objects that need to be serialized to be sent via web-based API’s, ASP.NET view models and even for databases like Cosmos DB since its internal storage is JavaScript Object Notation (JSON). I even serialize configuration objects to and from disk for apps that I write to store app and user data.

256 Seconds with dotNetDave – Episode 8

Let us improve Person.cs to include what it needs to properly serialize to and from JSON and XML.

Serializing to JSON

I would say that the way most objects these days that get serialized would be using JSON. I would also say, that with over 266 million downloads, the Newtonsoft.JSON NuGet package is the most popular library to do JSON serialization.

Using this package, it only takes one line of code like this.

var json = JsonConvert.SerializeObject(people);
var people = JsonConvert.DeserializeObject<List<Person>>(json);

If we serialize a List<Person> with two items (with fake data), this is what it looks like  in JSON.

[

  {

    “Address1”: “yJuDoux_HGTsXod”,

    “Address2”: “qAUPPFylqn”,

    “BornOn”: “2005-12-14T12:55:24.2371442-08:00”,

    “CellPhone”: “681-064-3138”,

    “City”: “COgx^xpxdFMhFup”,

    “Country”: “OjpX\\AXEPDCl[[e”,

    “Email”: “cskccwnrg@cr.es”,

    “FirstName”: “ukEpbcSWkRPAHka”,

    “HomePhone”: “072-236-5678”,

    “Id”: “06ba87e0e0cc4a51a8dc00c827ad401a”,

    “LastName”: “eKXt\\MU]xJ[dPTNuSFGL”,

    “PostalCode”: “82268”

  },

  {

    “Address1”: “Ft^JaGY^vA]W\\df”,

    “Address2”: “xUqiuNjH[D”,

    “BornOn”: “2005-12-14T12:55:24.2423257-08:00”,

    “CellPhone”: “733-337-0770”,

    “City”: “Q\\Gla`j_M`^sW_s”,

    “Country”: “bbLaBUmDFlgBF_K”,

    “Email”: “mfbwia@vxkwm.org.uk”,

    “FirstName”: “\\`ecV]OEHU]Ktu`”,

    “HomePhone”: “807-472-7275”,

    “Id”: “a34f3777518e49e2acd62775e5503621”,

    “LastName”: “^YAAt_dyBDxNnJWS]DM^”,

    “PostalCode”: “17147”

  }

]

Fixing JSON Formatting

One thing we need to do is to fix the property name casing since it’s not compliant with JSON coding standards. In the OOP world, properties start with an uppercase (PascalCasing) and in the JSON world they should start with a lower case (camelCasing). It’s easy enough to do with attributes.

First, we need to add the DataContract attribute to the class like this:

[DataContract(Name = “person”)]

public class Person : IEquatable<Person>, IComparable, IComparable<Person>

Then we need to add the DataMember attribute to all the properties we want to serialize like this:

[DataMember(Name = “address1”)]

public string Address1

I’d like to point out that you can use these two attributes to change terribly named data properties from API endpoints from companies like Salesforce, to proper OOP naming. Here is an example:

[DataMember(Name = “Total_Units_Sold__c”)]

public double UnitsSold

Making a property required is easy too:

[DataMember(Name = “email”, IsRequired = true)]

public string Email

Now, with these attributes, the JSON looks like this:

[

  {

    “address1”: “gVpEAP_pLyRmnYY”,

    “address2”: “k\\^sqrx\\Dp”,

    “bornOn”: “2005-12-14T12:52:08.6788613-08:00”,

    “cellPhone”: “887-720-6030”,

    “city”: “HCHNugsfZcnknZc”,

    “country”: “\\slC[htlOFRfCws”,

    “firstName”: “`hWWQiTQxKxSB[B”,

    “homePhone”: “113-161-8082”,

    “lastName”: “NQrHdAqhaDGTWJtHbnr\\”,

    “postalCode”: “37228”,

    “id”: “3f1640265c844b908bcc31028ae3f238”,

    “email”: “rvlwon@wjykqw.net”

  },

  {

    “address1”: “mOPddKKZxTyeL\\l”,

    “address2”: “x^t][sSF\\f”,

    “bornOn”: “2005-12-14T12:52:08.6852134-08:00”,

    “cellPhone”: “346-363-7213”,

    “city”: “ybaCU[AFiYqwrWJ”,

    “country”: “G`uYv[InxKk\\FNu”,

    “firstName”: “FREHmWwrHArMWCo”,

    “homePhone”: “452-157-8818”,

    “lastName”: “pmcqdytqnT_sjdmJgmYD”,

    “postalCode”: “26616”,

    “id”: “81b195b94cb14ee0bf91716cfc3c4b6e”,

    “email”: “gveq@qlbbkmdiedplkw.ly”

  }

]

Serializing to XML

So that you don’t leave out applications that still uses XML as their format (which there are a lot), for now, you should make sure that your model-classes support it. Here is the code to serialize and serialize XML (available in my dotNetTips.Utility.Standard NuGet package).

public static string Serialize(object obj)

{

    using (var writer = new StringWriter())

    {

        using (var xmlWriter = XmlWriter.Create(writer))

        {

            var serializer = new XmlSerializer(obj.GetType());

            serializer.Serialize(xmlWriter, obj);

            return writer.ToString();

        }

    }

}

public static T Deserialize<T>(string xml)

{

    using (var sr = new StringReader(xml))

    {

        var xs = new XmlSerializer(typeof(T));

        return (T)xs.Deserialize(sr);

    }

}

If we serialize a List<Person> to XML, it will look like this:

<?xml version=”1.0″ encoding=”utf-16″?>

<ArrayOfPerson xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=”http://www.w3.org/2001/XMLSchema”&gt;

  <Person>

    <Address1>B[adUUt^r]SrhRq</Address1>

    <Address2>qkbll`gUk[</Address2>

    <BornOn />

    <CellPhone>468-244-4428</CellPhone>

    <City>KIqLScmLgZegq]O</City>

    <Country>PTsLiTFoJoevOWy</Country>

    <Email>jmkekoaq@lormhbesmjdicaxqy.org.uk</Email>

    <FirstName>lWVSgitVYIl\hRo</FirstName>

    <HomePhone>774-811-5255</HomePhone>

    <Id>a5938cd190ee4c49a532bbe83644744f</Id>

    <LastName>EThWhg_nggOQ^o`VwZCv</LastName>

    <PostalCode>73438</PostalCode>

  </Person>

  <Person>

    <Address1>Qcr`H^pMJJwiaiK</Address1>

    <Address2>WqlWXqoVbt</Address2>

    <BornOn />

    <CellPhone>708-847-5728</CellPhone>

    <City>EIFZC^YPfP\gwcP</City>

    <Country>ybC^T[MPfOXuQeS</Country>

    <Email>mgfucctrpjeysmfraqo@iqlufffqtqvikx.org</Email>

    <FirstName>VtPlFOlSUtX_jRX</FirstName>

    <HomePhone>112-642-4284</HomePhone>

    <Id>16738e0a16e34469bf80f2c43bb527d2</Id>

    <LastName>rrYKdbIdSsaWL^ANPgcu</LastName>

    <PostalCode>84820</PostalCode>

  </Person>

</ArrayOfPerson>

Houston, We Have A Problem

As you can easily see in the XML, the value for the BornOn property is missing. This is because the serializer in .NET does not support the newer type of DateTimeOffset. I guess the .NET team never went back and fixed this issue, but we can!

Before we get to that fix, let’s start off with adding the Serializable and XmlRoot attributes to the class.

[Serializable]

[XmlRoot(ElementName = “People”)]

public class Person : IEquatable<Person>, IComparable, IComparable<Person>

There are a few ways to fix this missing BornOn value. For this article, we are going to implement it quickly by adding a new property like this:

[EditorBrowsable(EditorBrowsableState.Never)]

[IgnoreDataMember]

[XmlElement(“BornOn”)]

public string BornOnForXml

{

    get

    {

        return BornOn.ToString(“o”);

    }

    set

    {

        BornOn = DateTimeOffset.Parse(value);

    }

}

I added the EditorBrowsable attribute to hide this property from other assemblies. Then the IgnoreDataMember so the JSON serialization will ignore this property. Then I added the XmlElement attribute to use BornOn as the element name in XML. I also added the XmlElement to the rest of the properties.

Then we need to hide the BornOn property from XML serialization like this:

[XmlIgnore]

public DateTimeOffset BornOn

You can also use the XmlElement attribute to make the property required.

[XmlElement(IsNullable = false)]

public string Id

The final XML looks like this:

<?xml version=”1.0″ encoding=”utf-16″?>

<ArrayOfPerson xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=”http://www.w3.org/2001/XMLSchema”&gt;

  <Person>

    <Address1>fSrGUNBGo`G[xiT</Address1>

    <Address2>YbTErOSHxE</Address2>

    <BornOn>2005-12-14T14:42:35.5808138-08:00</BornOn>

    <CellPhone>257-531-5721</CellPhone>

    <City>Is^y[yW\Hv[FApm</City>

    <Country>HETrESKsBEaL_WW</Country>

    <Email>vlspiramhqbxbr@fskkr.net</Email>

    <FirstName>Zgk^K`EUdsqqACd</FirstName>

    <HomePhone>158-720-2817</HomePhone>

    <Id>d3580c8684884098ac1bc656a87190a8</Id>

    <LastName>xy\utmrEj\nU\eNDijyM</LastName>

    <PostalCode>41738</PostalCode>

  </Person>

  <Person>

    <Address1>TDloXg\PAsNjsPb</Address1>

    <Address2>EjsJsx`ngQ</Address2>

    <BornOn>2005-12-14T14:42:35.5873074-08:00</BornOn>

    <CellPhone>334-364-7782</CellPhone>

    <City>lDVuskEaDhfus\u</City>

    <Country>OgxEXnOF^yPxpPT</Country>

    <Email>fscidsoqfnolscythouuei@fioxxjtdbayndy.us</Email>

    <FirstName>WvxCpBUwPIsxjxe</FirstName>

    <HomePhone>440-447-7643</HomePhone>

    <Id>525f843089a740cb8ab15888add465fe</Id>

    <LastName>]xquclOQiQivqfttjiv^</LastName>

    <PostalCode>74074</PostalCode>

  </Person>

</ArrayOfPerson>

One more thing, the XML serializer will try to serialize the private fields in our class, so we need to tell it to ignore them like this:

[NonSerialized]

private string _address1;

Summary

Id like to note that with both JSON and XML, the attributes also allow you to order the properties, but I couldn’t get them to work. In JSON, they didn’t seem to have any affect. With XML, when I set the order, the serialized object would come back blank. If I figure it out, I will update this article.

As you can see, there is much more to building good model classes than just implementing properties. Wouldn’t it be a great value add to Visual Studio if it made designing classes like this easier? Maybe that should be the next extension from Mads Kristensen who builds a lot of my favorite Visual Studio extensions.  There is a lot more to good OOP design that I will tackle in future articles, so check back here often.

The Person class is now over 600 lines of code and documentation, too large to post in this article. You can view it by going to: https://gist.github.com/RealDotNetDave/d635ce8eab4b20ac1b8a4cededb0fb46#file-person-article3-cs

After reading these three articles, do you practice good OOP design? Do have any tips you’d like to share? Please make a comment below.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.