The Great Asynchronous Learning Experiment – Day 6

[Updated 11:50pm 9/11 to make this a bit more readable]

 As you may know, I've become very interested in the idea of hypervideo. In my first HDI video on HyperVideo I hard coded the links to the markers in the movie but I talked about trying again, with decoupled links.

My original idea was to put the link-to-marker association in a database or an XML file, but Tim Heuer, suggested JSON (smart guy, that Tim!).

So, how do you do this with JSON?  This isn't finalized yet, but to give you a quick idea of how easy such a thing can be, try this: fire up Visual Studio 2008 (Beta 2) and create a new Web application. Place a rather large text box in your aspx page (I made mine 600 x 400); set it to multi-line, read only and set the wrap to true.

In your App_Code you'll add your classes in a file named JSON.cs.

Our long term goal is to be able to pass the movie name to a web service and  get back the list of markers and the list of links for those markers. In fact, we don't want to have to know the name of the web service, so we'll embed the name of the web service right in the first marker in the movie.

Our first class is Movie, which contains just three members: the name of the movie:, the URI of the web service that knows about that movie:, and the list of Marker objects:


[Serializable()]
[XmlRoot("Movie")]
public class Movie
{
    private string movieTitle;
    private string movieURI;
    private List<Marker> movieMarkers;

Notice that the class requires the attribute Serializable (we're going to serialize to JSON) and we mark the class with the XMLRoot attribute

We go on to mark each property with the XMLElement attribute:


[XmlElement("Title")]
public string Title
{
    get { return movieTitle; }
    set { movieTitle = value; }
}

[XmlElement("URI")]
public string URI
{
    get { return movieURI; }
    set { movieURI = value; }
}

[XmlElement("Markers")]
public List Markers
{
    get { return movieMarkers; }
    set { movieMarkers = value; }
}

Finally, we create the ToJson() method which will do the actual serialization…


public string ToJson()
{
    System.Web.Script.Serialization.JavaScriptSerializer js = 
        new System.Web.Script.Serialization.JavaScriptSerializer();
    return js.Serialize(this);
}

 The MarkerLink class has two member variables: linkURI and linkText that will be filled with the URI and text to display at a given marker.

[Serializable()]
public class MarkerLink
{
    private string linkURI;
    private string linkText;

    [XmlElement("LinkURI")]
    public string LinkURI
    {
        get { return linkURI; }
        set { linkURI = value; }
    }
   
    [XmlElement("Text")]
    public string Text
    {
        get { return linkText; }
        set { linkText = value; }
    }
}

The Marker class represents the information for a given marker at a particular location in a movie. The name of the marker is used to refer to it in code. Its type, text and time are embedded in the movie itself. The final member is a list of MarkerLink objects, allowing each Marker to have zero or more links associated with that marker.

These markers are then rolled up into the Movie class whose final member is a list of Markers.

[Serializable()]
public class Marker
{
    private string markerName;
    private string markerType;
    private string markerText;
    private string markerTime;
    private List<MarkerLink> links;

    [XmlElement("Name")]
    public string Name
    {
        get { return markerName; }
        set { markerName = value; }
    }

    [XmlElement("Type")]
    public string Type
    {
        get { return markerType; }
        set { markerType = value; }
    }

    [XmlElement("Text")]
    public string Text
    {
        get { return markerText; }
        set { markerText = value; }
    }

    [XmlElement("Time")]
    public string Time
    {
        get { return markerTime; }
        set { markerTime = value; }
    }

    [XmlElement("Links")]
    public List Links
    {
        get { return links; }
        set { links = value; }
    }

With this in place, we're ready to rock and roll.  In the Page_Load method of default.aspx we instantiate a Movie object and set its Title and URI properties, and intialize its list of markers. We then, for the purpose of this demonstration,  populate the indices randomly and serialize all of this data into a JSON text string.

This is where you have to let your imagination run a little wild….. Imagine that when we are creating the links for the movie we start by embedding markers (say) every 30 seconds with no intrinsic meaning (e.g., Marker1, Marker2, Marker3). We then present a web page that the movie owner uses that lets the movie owner  fill in the movie name, and then add information for each marker, including zero or more links for that marker (e.g, for marker04 there are three links).  Once the movie owner presses SAVE all of the movie information (including the markers and their links) are serialized into JSON format and stored (perhaps in a SQL Server database).

Later, the user comes along to download the movie. The movie viewer (to be created) reads the first marker and makes contact with the web service whose URI is embedded in the movie, and provides the movie name (also embedded in the movie) What the viewr gets back  a JSON structure.

To make sure we've saved all this information correctly we can format and display the JSON string in the text box in our ASXM file. I took a brute force approach to making it look readable,


     // place in text box
     SerializedText.Text = FormatJson(mov.ToJson());

}

private string FormatJson(string p)
{
     int level = 1;
     string output = string.Empty;
     foreach (char c in p)
     {
         output += FixJsonChar(c, ref level);
     }
     return output;
}
private string FixJsonChar(char c, ref int level)
{
     if (c == '{')
     {
         string output = string.Empty;
         output += "\n";
         ++level;
         for (int i = 0; i < level; i++)
         {
             output += "  ";
         }
         output += c;
         return output;
     }
     if (c == '}')
     {
         --level;
         return c.ToString();
     }
     if (c == '[' || c == ']')
     {
         string output = string.Empty;
         output += "\n";
         for (int i = 0; i < level; i++)
         {
             output += "  ";
         }
         output += c;
         return output;
     }
     if (c == ']')
     {
         string output = string.Empty;
         output += "\n";
         for (int i = 0; i < level; i++)
         {
             output += "  ";
         }
         output += c;
         return output;
     }

     if (c == ',')
     {
         string output = string.Empty;
         output += c;
         output += "\n";
         for (int i = 0; i < level; i++)
         {
             output += "  ";
         }

         return output;
     }
       
    
     else
     {
         return c.ToString();
     }

}

(NB: all the clever JSON work was done by Tim, but all the hacking was done by me, so shout at me.)

What you get back is very cool…


  {"Title":"All About Silverlight",
    "URI":"http://mymovieurl.com/AllAboutSL.wmv",
    "Markers":
    [
      {"Name":"Marker 0",
      "Type":"Uri",
      "Text":"This is marker 0",
      "Time":"00:00",
      "Links":
      [
        {"LinkURI":"http://0.resource.com",
        "Text":"Visit Resource 0"},
      
        {"LinkURI":"http://1.resource.com",
        "Text":"Visit Resource 1"}
      ]},
    
      {"Name":"Marker 1",
      "Type":"Uri",
      "Text":"This is marker 1",
      "Time":"00:01",
      "Links":
      [
        {"LinkURI":"http://0.resource.com",
        "Text":"Visit Resource 0"},
      
        {"LinkURI":"http://1.resource.com",
        "Text":"Visit Resource 1"}
      ]},
    
      {"Name":"Marker 2",
      "Type":"Uri",
      "Text":"This is marker 2",
      "Time":"00:02",
      "Links":
      [
        {"LinkURI":"http://0.resource.com",
        "Text":"Visit Resource 0"},
      
        {"LinkURI":"http://1.resource.com",
        "Text":"Visit Resource 1"}
      ]}
    ]}

Yes, there are other ways to do this, but this is quick, easy, human readable, works and plays well with Ajax and gives us tremendous flexibility.

 

-j

Share

About Jesse Liberty

Jesse Liberty is an independent consultant and programmer with three decades of experience writing and delivering software projects. He is the author of 2 dozen books and multiple Pluralsight courses, and has been a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Xamarin Certified Mobile Developer and a Xamarin MVP, Microsoft MVP and Telerik MVP.
This entry was posted in z Silverlight Archives and tagged , , , . Bookmark the permalink.

2 Responses to The Great Asynchronous Learning Experiment – Day 6

  1. Hector says:

    or @XmlJavaTypeAdapterJust define type for your JAXBElement of type JSONBody’FOr Ex. @XmlRootElement puiblc class ExtMessage { private String owner; @XmlElement(type=ExtConcreteBody.class) private JSONBody body; }

  2. Unknown says:

    Comment content removed

Leave a Reply

Your email address will not be published.