Quantcast
Channel: Charlie Holland » WCF
Viewing all articles
Browse latest Browse all 4

Sending and receiving SPListItem references via JSON

$
0
0

One of the big changes in SharePoint 2013 is the focus on client side processing. Over the next few years I suspect SharePoint developers will be spending a lot of time in the wacky world of JavaScript, JQuery and JSON. Bearing that in mind, there’s one problem that’s likely to come up time and time again: You’re laying down some funky UX that interacts with server side code via a custom web service. Your server side code naturally makes use of SPListItem objects and you need to handle references to these across the web service boundary.

For example, you have a custom web part that provides edit functionality for a list item, before the edits are applied additional processing must take place in your server side code. Your client side code therefore calls a custom web service that performs the steps required to apply the edits.

The server side business object

So let’s say we have a server-side business object that looks like this (of course it’d be more complicated than this in the real world):

public class BusinessObject
{
    public SPListItem Item { get; set; }

    public string AdditionalProp { get; set; }

    public int AnotherProp { get; set; }
}

We can see that this object is effectively a wrapper for an SPListItem. This is a common scenario since practically all data in SharePoint is an SPListItem. So what happens if we try to send this as a response from a REST endpoint? As is often the case we promptly drown in the quicksand that is ULS. The problem: SPListItem is not serializable.

Possible Solutions

There are a few possible solutions. Probably the most obvious is don’t send the business object as the response. Instead define a different business object that uses only serializable properties and send that instead. That would work – it’d mean writing code to convert between the two types and ensuring that changes were applied to both types – but it’d work.

Another possibility is to rage against the machine – viva la punk – make SPListItem serializable.

Here’s how it’s done

Serializing every property in an SPListItem would be madness. All we really need is enough info to enable us to recreate the object on the server side when a call is made back into the webservice. For all practical purposes all we need is the GUID of the SPSite, the GUID of the SPWeb, the GUID of the SPList, the GUID of the SPListItem and the SPFileSystemObject type of the SPListItem.

While we could pollute the interface of our business object with this data, that would just be plain nasty. Instead, we can have the serialization process take care of it for us by building a JavaScriptConverter.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Web.Script.Serialization;
using Microsoft.SharePoint;

namespace Acme.Widget
{
    internal class SPListItemScriptConverter : JavaScriptConverter
    {
        public override IEnumerable<Type> SupportedTypes
        {
            get { return new ReadOnlyCollection<Type>(new List<Type>(new[] {typeof (SPListItem)})); }
        }

        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            if (dictionary == null) throw new ArgumentNullException("dictionary");

            if (type == typeof (SPListItem))
            {
                using (var site = new SPSite(new Guid(dictionary[@"SiteId"].ToString())))
                {
                    SPWeb web = site.OpenWeb(new Guid(dictionary[@"WebId"].ToString()));
                    SPList list = web.Lists[new Guid(dictionary[@"ListId"].ToString())];
                    SPFileSystemObjectType objectType;

                    if(Enum.TryParse(dictionary[@"Type"].ToString(),out objectType))
                    {
                        if (objectType == SPFileSystemObjectType.Folder)
                        {
                            return list.Folders[new Guid(dictionary[@"ListItemId"].ToString())];
                        }
                    }
                    return list.Items[new Guid(dictionary[@"ListItemId"].ToString())];
                }
            }

            return null;
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            var listItem = obj as SPListItem;

            if (listItem != null)
            {
                var result = new Dictionary<string, object>
                    {
                        {@"SiteId", listItem.Web.Site.ID},
                        {@"WebId", listItem.Web.ID},
                        {@"Type", listItem.FileSystemObjectType},
                        {@"ListId", listItem.ParentList.ID},
                        {@"ListItemId", listItem.UniqueId}
                    };

                return result;
            }
            return new Dictionary<string, object>();
        }
    }
}

We can then make use of the convertor when serializing/deserializing a business object in our web service code like this:

public Stream GetBusinessObject(string someProperty)
{
    //Get the object from somewhere
    var theObj = new BusinessObject();

    if (WebOperationContext.Current != null)
        WebOperationContext.Current.OutgoingResponse.ContentType = "application/json; charset=utf-8";

    var serializer = new JavaScriptSerializer();
    serializer.RegisterConverters(new JavaScriptConverter[] {new SPListItemScriptConverter() });
    string json = serializer.Serialize(theObj);

    return new MemoryStream(Encoding.UTF8.GetBytes(json));
}

public Void SetBusinessObject(Stream jsonData)
{
    var jsonText = new StreamReader(jsonData).ReadToEnd();
    var serializer = new JavaScriptSerializer();
    serializer.RegisterConverters(new JavaScriptConverter[] { new SPListItemScriptConverter() });
    var theObj = serializer.Deserialize<BusinessObject>(jsonText);

    //Do something useful with the object complete with valid SPListItem reference
}

Viewing all articles
Browse latest Browse all 4

Trending Articles