Posts Tagged Programming

Simple Class Serialization With JsonCpp

A recent project of mine necessitated that I find a way to share data between a client application written in C++ and a host application written in C#. After talking with the other programmer on the project, we decided that using Json to share data between the applications would be the best solution. This sent me on a search for a decent API for parsing Json data, which led to JsonCpp.

A cursory glance led me to conclude that this API would likely provide me with everything I would need for object serialization. The problem, however, was coming up with a decent design that would allow for full featured serialization. The goals were simple. I needed to be able to serialize and deserialze a class. I would also need access to all data members, including primitives, data structures, and nested classes.

JsonCpp provides us with the capability to do this with relative ease. As such, I hope to provide you all with a basic design to accomplish this. Luckily, we won’t need a deep understanding of JsonCpp to accomplish our goals. As such, I’m not going to overly explain much of the JsonCpp concepts, as the JsonCpp documentation is a good enough resource.

First, let’s consider a simple test class.

class TestClassA
{
public:
   TestClassA( void );
   virtual ~TestClassA( void );

private:
   int           m_nTestInt;
   double        m_fTestFloat;
   std::string   m_TestString;
   bool          m_bTestBool;
};

This class should provide us with a basic framework to test serialization.  We’re going to start with primitive data first (granted, std::string isn’t strictly primitive, but it’s primitive enough).

Now that we have a basic test case, lets design a basic interface that all of our serializable classes can inherit from.

class IJsonSerializable
{
public:
   virtual ~IJsonSerializable( void ) {};
   virtual void Serialize( Json::Value& root ) =0;
   virtual void Deserialize( Json::Value& root) =0;
};

This should be self explanitory. We obviously need to include json.h in order to have access to the Json::Value class. The Json::Value class is actually fairly complex, and as such we aren’t going to spent much time examining it. Suffice to say, we are going to treat it as a sort of map, mapping our metadata tags to the actual values we will be serializing. This will make much more sense once we work on the implementation of the Serialize() and Deserialize() methods in our classes.

So let’s update our updated test class definition.

class TestClassA : public IJsonSerializable
{
public:
   TestClassA( void );
   virtual ~TestClassA( void );
   virtual void Serialize( Json::Value& root );
   virtual void Deserialize( Json::Value& root);

private:
   int           m_nTestInt;
   double        m_fTestFloat;
   std::string   m_TestString;
   bool          m_bTestBool;
};

Now before we progress further on the C++ side of things, let’s cook up some test Json data to work with.

{
"testboolA" : true,
"testfloatA" : 3.14159,
"testintA" : 42,
"teststringA" : "foo"
}

Whether you know Json or not, this data structure should be completely self-explanatory.  The goal here is going to be to deserialize this data into our class.  However, we have one more thing to do.

The last class we need to design here is the actual serializer class.

class CJsonSerializer
{
public:
   static bool Serialize( IJsonSerializable* pObj, std::string& output );
   static bool Deserialize( IJsonSerializable* pObj, std::string& input );

private:
   CJsonSerializer( void ) {};
};

This is a simple little “static class”.  We’ll make the constructor private this way we can’t actually instantiate it.  We will simply deal with the methods directly.  This will let us add a second layer of abstraction to JsonCpp.

Alright, our basic design is done.  Let’s get into the implementation.  Let’s start with our test class.

void TestClassA::Serialize( Json::Value& root )
{
   // serialize primitives
   root["testintA"] = m_nTestInt;
   root["testfloatA"] = m_fTestFloat;
   root["teststringA"] = m_TestString;
   root["testboolA"] = m_bTestBool;
}

void TestClassA::Deserialize( Json::Value& root )
{
   // deserialize primitives
   m_nTestInt = root.get("testintA",0).asInt();
   m_fTestFloat = root.get("testfloatA", 0.0).asDouble();
   m_TestString = root.get("teststringA", "").asString();
   m_bTestBool = root.get("testboolA", false).asBool();
}

Remember when I said we were going to look at the Json::Value class like a map?  That should make more sense now.  The Serialize() method should make sense.  We are simply adding the values to our root object as if it was a map.  The keys we are using map directly to the metadata in the Json data.  The Deserialize() method is only slightly more complex.  We need to use the get() method in order to retrieve our data.  The get() method takes two parameters: get( <key>, <defaultvalue>).  Knowing that, the method should make sense.  We simply call get() with each key and place the values from the Json into our members.

Now let’s move on to the CJsonSerializer class implementation.

bool CJsonSerializer::Serialize( IJsonSerializable* pObj, std::string& output )
{
   if (pObj == NULL)
      return false;

   Json::Value serializeRoot;
   pObj->Serialize(serializeRoot);

   Json::StyledWriter writer;
   output = writer.write( serializeRoot );

   return true;
}

bool CJsonSerializer::Deserialize( IJsonSerializable* pObj, std::string& input )
{
   if (pObj == NULL)
      return false;

   Json::Value deserializeRoot;
   Json::Reader reader;

   if ( !reader.parse(input, deserializeRoot) )
      return false;

   pObj->Deserialize(deserializeRoot);

   return true;
}

It should be obvious now why we wanted a second level of abstraction from JsonCpp.

Let’s look at the Serialize() method here.  We need to pass two parameters, a pointer to the IJsonSerializable object to be serialized, and a reference to a std::string.  That string is going to hold the serialized Json data from our class.  This is very rudimentary.  We simply create an instance of a Json::Value object to act as our root, and pass it to the Serialize() method of the object in question.  That Serialize() method will fill the Json::Value object with all of the serialized data.  We then create an instance of Json::StyledWriter, and use it to write the Json data to the empty std::string we originally passed.

As for the Deserialize() method, it’s not terribly different aside from the fact that the std::string we pass is going to contain Json instead of being empty.  We will create an instance of Json::Reader and use it to parse our input string and fill a Json::Value object for us.  We will then use that new Json::Value object and pass it to the IJsonSerializable objects Deserialze() method, which will set the data members based on the Json::Value object’s values.

As you can see, this is all pretty simple.  We can now create a simple test case.

TestClassA testClass;
std::string input = "{ \"testintA\" : 42, \"testfloatA\" : 3.14159, \"teststringA\" : \"foo\", \"testboolA\" : true }\n";
CJsonSerializer::Deserialize( &testClass, input );

std::cout << "Raw Json Input\n" << input << "\n\n";

std::string output;
CJsonSerializer::Serialize( &testClass, output);

std::cout << "testClass Serialized Output\n" << output << "\n\n\n";

If everything went according to plan, you should see the following:

Raw Json Input
{ "testintA" : 42, "testfloatA" : 3.14159, "teststringA" : "foo", "testboolA" : true }

testClass Serialized Output
{
   "testboolA" : true,
   "testfloatA" : 3.14159,
   "testintA" : 42,
   "teststringA" : "foo"
}

Essentially our test case simply created an “empty” TestClass object.  It then deserialized a string of input Json data into the class, filling it’s members.  We then create a new empty output string, and deserialize our TestClass object into that string.  If the outut is what we expect, then we can assume that basic serialization and deserialization is working!

,

23 Comments

Developing a simple plugin architecture in C#

Once my editor officially began being used for two different, completely unrelated games, I had a few decisions to make.  The main problem was the fact that an RPG needs very specific helper functions (NPC editing, scripting, etc) whereas a platformer would need things like event scripting.  Rather than make additional tools to handle these functions, having them combined would be ideal.  However, I didn’t want to combine functions into an editor that would be useless for the other game.

The solution: a very simple plugin architecture.  By very simple, I mean that the actual Map classes would not be changed at all, in fact the XML map that the editor exported would not be changed in the slightest.  The plugin would not so much be given access to the Map in any way, but more so it would be given a place to run.  Essentially (at this point anyway) the plugin would be aesthetic.  It would allow level designers to use additional functionality within the level editor, which would already be running on the system.

But enough about that, the reason for this post is that I wanted to share a bit of code that let this happen.  After doing some research, I came across an old post by Shawn Walcheske called “Implementing a Plug-In Architecture in C#” (located here: http://www.ddj.com/cpp/184403942).  My implementation is, basically, identical, however the source download on that page is down, and he doesn’t really explain the System.Activator class which leaves the reader hanging. Update from a reader. Apparently the source is still available here: ftp://ftp.cuj.com/sourcecode/cuj/2003/cujjan2003.zip. Thanks!

So with that, I thought I’d show how I went about doing things, and basically explain the few loose ends that Shawn leaves out.  I think it goes without saying that if you really want to understand this entire concept, read his article as well.

The first thing we need to do is create a simple interface for our plugin.  This interface needs to be in it’s own assembly, for example I created ParadigmCommon.dll for plugins to reference in order to have access to this interface.  As the plugin system I am developing is very simple, the interface only provides an ActivatePlugin() method, as all we want to do is be able to launch new plugins from the main editor.

    public interface IParadigmPlugin
    {
        /// <summary>
        /// Activates the plugin, showing it's main form
        /// </summary>
        void ActivatePlugin();
    }

Next we need to define some custom attributes.  These attributes will hold some metadata for our class, this way our main application can reference that data.  We are, for this example, going to create a custom DisplayName attribute, and a custom Description attribute.  They are fairly self explanatory.

    /// <summary>
    /// This class defines the custom attribute for plugin display names
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class ParadigmPluginDisplayNameAttribute : System.Attribute
    {
        private string displayName;
 
        public ParadigmPluginDisplayNameAttribute(string displayName)
            : base()
        {
            this.displayName = displayName;
        }
 
        public override string ToString()
        {
            return displayName;
        }
    }
    /// <summary>
    /// This class defines the custom attribute for plugin descriptions
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class ParadigmPluginDescriptionAttribute : System.Attribute
    {
        private string description;
 
        public ParadigmPluginDescriptionAttribute(string description)
            : base()
        {
            this.description = description;
        }
 
        public override string ToString()
        {
            return description;
        }
    }

Here we have an actual plugin.  As you can see, we need to use the ParadigmCommon namespace, as well as reference the DLL from the project.  The plugin itself merely implements the IParadigmPlugin interface and as such implements the ActivatePlugin() method, which only serves to open a new form for the plugin.  Simple enough.  Similar in implementation to the way Program.cs would launch the main form in a Windows application.  As you can see, we are also using our custom attributes.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms; // referencing System.Windows.Forms is obviously needed for form usage, make sure to reference it in the project as well
using ParadigmCommon; // you must reference the ParadigmCommon namespace to access the needed attributes and interface
 
namespace TestPlugin
{
    /// <summary>
    /// This is an example of a paradigm plugin
    /// </summary>
    [ParadigmPluginDisplayName("Test Plugin")] // you must provice a display name
    [ParadigmPluginDescription("Test Plugin")] // as well as a description
    public class TestPlugin : IParadigmPlugin  // Your main class must implement the IParadigmPlugin interface
    {
        public void ActivatePlugin() // by implementing the interface, you must implement an ActivatePlugin method
        {
            TestPluginMainForm mainForm = new TestPluginMainForm();
            mainForm.Show();
            mainForm.Activate();
        }
    }
}

In our host applciation, we need to define a few things.  First, we need a wrapper class. This class will hold a reference to an object implementing our IParagidmPlugin interface (ie: any plugin we create), as well as the metadata for the plugin (in our case, name and description). In this case we also implement an Activate() method that will call the actual plugin’s ActivatePlugin() method.

    /// <summary>
    /// This class wraps our plugins
    /// </summary>
    public class ParadigmPlugin
    {
        #region Fields
        private IParadigmPlugin plugin;
        private string name;
        private string description;
        #endregion
 
        #region Attributees
        public string Name
        {
            get { return name; }
        }
 
        public string Description
        {
            get { return description; }
        }
        #endregion
 
        /// <summary>
        /// Constructs a plugin wrapper
        /// </summary>
        public ParadigmPlugin(IParadigmPlugin plugin, string name, string description)
        {
            this.plugin = plugin;
            this.name = name;
            this.description = description;
        }
 
        /// <summary>
        /// Activate Plugin
        /// </summary>
        public void Activate()
        {
            plugin.ActivatePlugin();
        }

We also need to define a quick custom exception, which we will use later on. There is really no implementation here in the example, but you can handle the exception however you wish.

    class PluginNotValidException : ApplicationException
    {
        public PluginNotValidException(Type type, string param1)
        {
        }
    }

First, we need to define a list to contain all of the plugins we load at runtime.  Next, we need a LoadPlugins() method.  This method uses reflection to handle plugin loading.  Essentially, it steps through our plugin directory looking for any file that matches the *.dll file name mask.  When it finds a DLL, it attempts to load the assembly.  If it fails, it will throw an exception and alert the user.

If it succeeds in loading the assembly, and looks for our IParadigmInterface.  If it finds it, it then gets our custom attributes.

Next, the System.Activator class comes into play.  This wasn’t mentioned in Shawn’s article as I said before.  Basically, we are going to create an object of a specific type, which in this case our type is going to be pulled from the loaded assembly, this way we create the correct type of object.

Next, we create an instance of our wrapper class.  Upon constructing, we are going to make sure to cast the object returned by System.Activator as IParadigmPlugin.  We will then add it to our list of loaded plugins.  Thats it.

The last part of this function is optional, but pretty cool and may be helpful.  Basically, we want our loaded plugins to show up dynamically on a menu strip.  So, we will iterate through our list of loaded plugins, and for each one we will add a new item to a toolstrip menu dropdown list.  We will set the name and text to the plugin display name, and the tool tip to the plugin description (see, we used the metadata for something afterall).  Then, we will set the new items Click event to be a delegate referencing the plugin’s Activate() method.  Simple right?  When this method executes, our tool strip will be dynamically populated by the plugins, and clicking them will launch them.

private List<ParadigmPlugin> loadedPlugins = new List<ParadigmPlugin>();
        private void LoadPlugins()
        {
            // search plugin directory for dlls
            string[] files = Directory.GetFiles("Plugins", "*.dll");
 
            // check each file in plugin direction
            foreach (string file in files)
            {
                try
                {
                    // load the assembly and get types
                    Assembly assembly = Assembly.LoadFrom(file);
                    System.Type[] types = assembly.GetTypes();
 
                    // look for our interface in the assembly
                    foreach (System.Type type in types)
                    {
                        // if we found our interface, verify attributes
                        if (type.GetInterface("IParadigmPlugin") != null)
                        {
 
                            if (type.GetCustomAttributes(typeof(ParadigmPluginDisplayNameAttribute), false).Length != 1)
                                throw new PluginNotValidException(type, "Plugin display name is not supported!");
 
                            if (type.GetCustomAttributes(typeof(ParadigmPluginDescriptionAttribute), false).Length != 1)
                                throw new PluginNotValidException(type, "Plugin description is not supported");
 
                            // get custom attributes from plugin
                            string name = type.GetCustomAttributes(typeof(ParadigmPluginDisplayNameAttribute), false)[0].ToString();
                            string description = type.GetCustomAttributes(typeof(ParadigmPluginDescriptionAttribute), false)[0].ToString();
 
                            // create the plugin using reflection
                            Object o = Activator.CreateInstance(type);
 
                            ParadigmPlugin plugin = new ParadigmPlugin(o as IParadigmPlugin, name, description);                                                               
 
                            loadedPlugins.Add(plugin);
                        }
                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message, "Plugin Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
 
            // iterate through list and add them to our form control
            for (int i = 0; i < loadedPlugins.Count; i++)
            {
                ParadigmPlugin plugin = loadedPlugins[i];
 
                pluginsToolStripMenuItem.DropDownItems.Add(plugin.Name);
                pluginsToolStripMenuItem.DropDownItems[i].Click += delegate { plugin.Activate(); };
                pluginsToolStripMenuItem.DropDownItems[i].Text = plugin.Name;
                pluginsToolStripMenuItem.DropDownItems[i].ToolTipText = plugin.Description;
            }
        }

You could easily expose much more via the interface and make some complicated plugins, but that isn’t needed in this scenario.  However, I hope this helps to illuminate the basics of plugin architecture in C#.

, ,

3 Comments