Circular reference exception on trying to serialize glass objects

While using sitecore 7 with TDS and glassmapper, we ran into this issue where serializing glass mapper objects as they are into json, resulted in a circular reference exception.

2014-11-01_165346

You will see that the T4 template file glassv3header.tt file has the Language property available as an object of the Sitecore.Globalization.Language class:

namespace <#=fullNamespace #>
{
	public partial interface IGlassBase{
		
		[SitecoreId]
		Guid Id{ get; }

		[SitecoreInfo(SitecoreInfoType.Name)]
		string Name { get; }

		[SitecoreInfo(SitecoreInfoType.Language)]
		Sitecore.Globalization.Language Language{ get; }

        [SitecoreInfo(SitecoreInfoType.Version)]
        int Version { get; }

		[SitecoreInfo(SitecoreInfoType.Url)]
		string Url { get; }
	}

	public abstract partial class GlassBase : IGlassBase{
		
		[SitecoreId]
		public virtual Guid Id{ get; private set;}

		[SitecoreInfo(SitecoreInfoType.Name)]
        public string Name { get; private set; }

		[SitecoreInfo(SitecoreInfoType.Language)]
		public virtual Sitecore.Globalization.Language Language{ get; private set; }

        [SitecoreInfo(SitecoreInfoType.Version)]
        public virtual int Version { get; private set; }

		[SitecoreInfo(SitecoreInfoType.Url)]
        public virtual string Url { get; private set; }
	}
}

You could ofcourse remove this property from your tt file, but the property does prove handy, so you might want to use it!
And the CultureInfo class has within itself a bunch of CultureInfo properties which leads to the circular reference.
2014-11-03_173449

I am using the Controller.Json method for serializing the object. You could definitely go for using other frameworks like newtonsoft etc, which would give you the option of limiting the depth in case recursion is encountered while serializing.
However, if you want to continue using the inherent Controller.Json and use the JsonResult type, the best route would be to serialize an on-the-fly created anonymous object instead:

            return Json(new
            {
                InspirationsList = inspirationListingModel.InspirationsList != null ? inspirationListingModel.InspirationsList.Select(x => new
                {
                    x.Listing_Image,
                    x.Url,
                    VideoUrl = (x.Custom_Video != null && !string.IsNullOrWhiteSpace(x.Custom_Video.Url)) ? x.Custom_Video.Url : string.Empty,
                    PrimaryInspirationtype = (x.Custom_InspirationTypes != null && x.Custom_InspirationTypes.Any())
                        ? x.Custom_InspirationTypes.First().Navigation_Title : string.Empty,
                    x.Navigation_Title,
                    SkillLevel = (x.Custom_SkillLevel != null ? x.Custom_SkillLevel.Title : string.Empty)
                }) : null,
                CTACallout = inspirationListingModel.CTACallout == null ? null : new
                {
                    inspirationListingModel.CTACallout.Title,
                    inspirationListingModel.CTACallout.Subtitle,
                    inspirationListingModel.CTACallout.Callout_Image,
                    inspirationListingModel.CTACallout.Custom_Theme,
                    inspirationListingModel.CTACallout.Call_To_Action
                },
                inspirationListingModel.IsMoreAvailable,
                inspirationListingModel.TotalInspirationsCount
            }, JsonRequestBehavior.AllowGet);

In the above example InspirationsList is a collection of glass objects while CTACallout is a single glass object.

This returns json similar to the following:

{
    "InspirationsList":[
        {
            "Listing_Image":{
                "Alt":"",
                "Border":"",
                "Class":"",
                "Height":262,
                "HSpace":0,
                "Src":"/en/~/media/***********************/28.jpg",
                "VSpace":0,
                "Width":314,
                "MediaId":"8a658e45-e172-4592-95bf-*****************",
                "Title":""
            },
            "Url":"/en/********************/2014/10/25/04/19/aenean-vulputate-eleifend",
            "VideoUrl":"",
            "PrimaryInspirationtype":"Outdoor DIY",
            "Navigation_Title":"Aenean vulputate eleifend",
            "SkillLevel":"Advanced"
        },
        {
            "Listing_Image":{
                "Alt":"",
                "Border":"",
                "Class":"",
                "Height":262,
                "HSpace":0,
                "Src":"/en/~/**************************.jpg",
                "VSpace":0,
                "Width":314,
                "MediaId":"8a658e45-e172-4592-95bf-*************",
                "Title":""
            },
            "Url":"/en/*************************************",
            "VideoUrl":"",
            "PrimaryInspirationtype":"Home Projects",
            "Navigation_Title":"Maecenas nec odio et",
            "SkillLevel":"Easy"
        }
    ],
    "CTACallout":{
        "Title":"Submit",
        "Subtitle":"Your \u003cbr\u003eDIY Project",
        "Callout_Image":{
            "Alt":"CTA",
            "Border":"",
            "Class":"",
            "Height":270,
            "HSpace":0,
            "Src":"/en/~/media/*************.jpg",
            "VSpace":0,
            "Width":313,
            "MediaId":"1f3a48b0-a5d4-4e19-b73b****************",
            "Title":""
        },
        "Custom_Theme":null,
        "Call_To_Action":{
            "Anchor":"",
            "Class":"",
            "Text":"Sample Text",
            "Query":"",
            "Title":"",
            "Url":"/en/******************",
            "Target":"",
            "TargetId":"20265cce-b2ff-472c-aa85-******************",
            "Type":4
        }
    },
    "IsMoreAvailable":true,
    "TotalInspirationsCount":2
}

Also, while we are at this, to be able to access a controller action directly in a sitecore mvc solution, you would use a Url similar to the following:

http://mydomain.com/api/sitecore/<controller>/<action>?<param1=value>&<param2=value>
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s