Search code examples
c#asp.net-mvcjavascriptserializer

JavaScriptSerializer [ScriptIgnore] not effective on virtual properties?


I have returning a Json(myObj) action result. The myObj is of type Badge

The only two objects that Badge has that could cause a loop from a serializer, are:

public class Badge
{
    public Badge() { }

    public Badge(String Name, String Description)
    {
        this.Name = Name;
        this.Description = Description;
    }

    [ScriptIgnore]
    public virtual BadgeType BadgeType { get; set; }
    [ScriptIgnore]
    public virtual ICollection<User> Users { get; set; }

    public int ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string PrerequisiteCriteriaRef { get; set; }

    //PrerequisiteID

    public static Badge CreateForSeeder(BaseDBContext db, String Name, String Description, int TypeID)
    {
        Badge b = new Badge();
        b.Name = Name;
        b.Description = Description;
        b.BadgeType = db.BadgeTypes.Where(x => x.TypeID == TypeID).FirstOrDefault();
        return b;
    }
}

Which I've given the attribute, but it's not helping out at all...?


Solution

  • The JavaScriptSerializer (which is what is used when you return Json) definitely honors the [ScriptIgnore] attribute.

    Here's a proof:

    Model:

    public class User
    {
        public Badge Badge { get; set; }
    }
    
    public class Badge
    {
        [ScriptIgnore]
        public virtual ICollection<User> Users { get; set; }
    
        public int ID { get; set; }
        public string Name { get; set; }
    }
    

    Controller:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var badge = new Badge
            {
                ID = 1,
                Name = "badge"
            };
            var user = new User
            {
                Badge = badge
            };
            badge.Users = new[] { user }.ToList(); 
    
            return Json(badge, JsonRequestBehavior.AllowGet);
        }
    }
    

    If you remove the [ScriptIgnore] attribute from the Users property you will get a circular reference error.

    So I guess that your problem is somewhere else.

    But personally I would recommend you using view models instead of those [ScriptIgnore] attributes.

    So you simply define a view model that will contain only the properties you need for the given view:

    public class BadgeViewModel
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }
    

    and then in your controller action you map between the domain model and the view model and you pass the view model to the view:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            Badge badge = ...
            BasgeViewModel vm = new BasgeViewModel
            {
                Id = badge.Id,
                Name = badge.Name
            };
    
            return Json(vm, JsonRequestBehavior.AllowGet);
        }
    }
    

    and if you are sick of writing mapping code in your controllers you simply head over to your NuGet package console and type the following command:

    Install-Package AutoMapper
    

    in order to take full advantage of the excellent AutoMapper library.