Skip to content

Tutorial: From Types to Descriptions to Formats, and Back Again! (A SIMPL overview in CSharp)

AndrewKoons edited this page Apr 8, 2013 · 3 revisions

Overview

This tutorial will guide you through the entirety of S.IM.PL at a very high level. It will start with a humble data class and will cover annotating and describing the class. Once that is done, it will discuss how to serialize the class to different formats. Finally, it will cover how to deserialize S.IM.PL data.

A Book Data Type

Let's suppose that we have a very basic type that represents a book. In C-Sharp, we may represent this type like so:

namespace Simpl.Tutorials
{
    public class Book 
    {
        String title; 
	
        String authorName;
	
        int bookID;
	
        /** Getters and setters elided for brevity ** /
    }
}

In order for S.IM.PL to support data types, they must be annotated with DBAL annotations. DBAL stands for "Data Binding Annotation Language" DBAL annotations cover a wide array of different data binding scenarios, such as collections, composites, scalars, tag name overrides, and more.

In general, you probably won't have to use everything that DBAL supports. The most common annotations are as follows:

// It is worth noting that the specific syntax of these annotations is C# specific

// Field level annotations:
       [SimplScalar] // For scalar types; such as Integers, Strings, etc.
       [SimplComposite] // For composite types; other objects that are S.IM.PL types! (Supports cycles, graphs, and references)
       [SimplCollection]// For collections, such as Lists or Sets
       [SimplMap]// For maps
       [SimplTag("some_tag_name")] // Overrides the tag/name binding for a field or class.
       [SimplOtherTags("Other", "tag", "names")] // Adds additional tag/name bindings for a field or class; handy for supporting old formats. 

// Class level annotations:
         [SimplInherit] // Determines that a Simpl Object should inherit S.IM.PL type information from its superclass

To make a class S.IM.PL serializable, we must:

  1. Annotate our fields with DBAL annotations appropriately

  2. Make sure that the class has a public, parameter-less constructor

  3. Initialize default values for fields which are defined as interfaces in the public, parameter-less constructor. (Lists, other composite types, etc)

Our correctly annotated Book class may look as follows:

namespace Simpl.Tutorials
{
    public class Book
    {
        [SimplScalar]
        String title;

        [SimplScalar]
        String authorName;

        [SimplScalar]
        [SimplTag("book_number")]
        int bookID;
        //We're going to map bookID to "book_number"
        //These sorts of mappings can be handy, especially for handling old/pre-existing data schemas.
        // [simpl_other_tags()] is also a great tool for this



        public Book()
        {
            /* If we had a field like:
             * [simple_collection] private List<int>() myList;
             *  We would initialize it here like so: */
            // myList = new ArrayList<Integer>();
            // To select our default implementation behind the interface.
            // In this case, we don't have to initialize anything


        }
   
        /** Getters and setters elided for brevity */ 
    }
}

Describing a Book

S.IM.PL's "cross-language type system" helps make mappings between different languages onto one common set of types. (For more on the Cross-Language Type System and how it maps between languages, see some additional tutorials.) We call the representations of type information in S.IM.PL "Descriptions," because they describe all of the relevant type information for data objects.

Descriptions are similar to classes such as Java's Class and Field or C#'s Type and FieldInfo. (In fact, in C# and Java, descriptions are often simply derived from these types). The crucial difference is that descriptions can be serialized from language to language and that descriptions contain all of the relevant type information needed to map data across programming languages.

You can obtain ClassDescriptor (which describe all of the fields and aspects of a given class) with the following code:

ClassDescriptor book_descriptor = ClassDescriptor.GetClassDescriptor(typeof(Book)); 

As an exercise, try getting a class descriptor and looking at the sorts of data they contain!

Descriptors are a core feature of S.IM.PL: They guide the serialization and deserialization process and enable translations of object representations across languages. Remembering this while working with (or even reimplementing) S.IM.PL will prove immensely helpful.

Serializing a Book

We can serialize a book object in as many different formats as are supported by our S.IM.PL implementation.

Suppose we have an instance of a book object, initialized below:

        Book abook = new Book();
//Here's an instance of our type to serialize

        abook.setAuthorName("Michael Feathers");
        abook.setBookID(1337);
        abook.setTitle("Working Effectively with Legacy Code");

To S.IM.PL serialize this object, we simply call SimplTypesScope.serialize()!

To serialize to JSON:

// Serialize to JSON
        String jsonResult = SimplTypesScope.serialize(abook, StringFormat.Json)

or, XML:

// Serialize to XML
// (Just change the StringFormat parameter!) 
        String xmlResult = SimplTypesScope.serialize(abook, StringFormat.Xml);

As you may notice, all we changed was the StringFormat to change the format of serialization. There are a large number of ways that we can serialize objects... We can serialize them to a File, an output stream, and Appendable, etc. This is just the simplest way!

Our serialized representations for the book would look like the following: JSON:

        {"book":{"title":"Working Effectively with Legacy Code","author_name":"Michael Feathers","book_number":"1337"}}

XML:

        <book title="Working Effectively with Legacy Code" author_name="Michael Feathers" book_number="1337"/>

(You'll notice that in both of the formats, the bookID value is stored in the attribute book_number, as dictated by the [SimplTag("book_number")] annotation added to the bookID field.)

Now that we've serialized some data... how can we go about deserializing it?

Deserializing a Book

In order to deserialize some data, S.IM.PL needs to have access to all of the descriptions for types that may be deserialized. The way we transmit this information is with a SimplTypesScope. You can think of a SimplTypesScope as a mapping between tag names and Descriptions. (Obviously, it does more than that behind the scenes!)

We have two options when we work with type scopes. We can either:

  1. Get the SimplTypesScope by loading a file

  2. Create the SimplTypesScope from descriptions in our source

In languages which don't have reflection or strong type information (Python, Ruby, Objective C, etc), you'll need to use Option 1 and load your SimplTypesScope. In languages where the type information IS present, you simply can create it.

Creating a SimplTypesScope to deserialize our book looks like so, in C#:

// A STS should contain all of the classes we expect to encounter
// Here, we're just expecting books! 
        SimplTypesScope book_example_sts = new SimplTypesScope("book_example", typeof(Book)); 

// We name this type scope "book_example"... the name has use w/ Polymorphic data. 
// (Not relevant here, though!)

Once we have a SimplTypesScope, we can go ahead and deserialize our data. Deserialization can work on many different sources, such as files or input streams... we'll just deserialize a String:

         Object result1 = book_example_sts.Deserialize(jsonResult, StringFormat.Json);

After deserialization, the object we get back is, in fact, the book we were serializing!

// We get back a book
        Assert.IsTrue(result1.GetType().IsAssignableFrom(abook.GetType()));
        Book book_from_json = (Book)result1;
	
// Validate that our book is what we expected...
        Assert.AreEqual(book_from_json.getAuthor(), "Michael Feathers");
        Assert.AreEqual(book_from_json.getBookID(), 1337);
        Assert.AreEqual(book_from_json.getTitle(), "Working Effectively with Legacy Code");

        // (It should be! S.IM.PL should just simply work!) 

This works just as well for XML deserialization, just be sure to change the StringFormat.

        Object result2 = book_example_sts.Deserialize(xmlResult, StringFormat.Xml);

Conclusion

With this tutorial, you've learned about the core of S.IM.PL and how it progresses from data types to descriptions to serialized formats, and all the way back again! Now you can keep diving into S.IM.PL by completing other tutorials, or simply by writing some applications that use S.IM.PL!

Further reading

You can read a code sample that implements the different parts of this tutorial at https://github.com/ecologylab/ecologylabFundamental.NET/tree/master/Simpl.Tutorials You can work with the code by setting up a development environment (https://github.com/ecologylab/simpl/wiki/Check-out-code-and-set-up-development-environment). Feel free to step through the code to get a better idea of how it all works!

More tutorials can be found on the Tutorials page on the wiki. If you have any other questions, don't hesitate to contact us!