I have an interface with a property like this:
public interface IFoo {
// ...
[JsonIgnore]
string SecretProperty { get; }
// ...
}
I want the SecretProperty to be ignored when serializing all implementing classes. But it seems I have to define the JsonIgnore attribute on every implementation of the property. Is there a way to achieve this without having to add the JsonIgnore attribute to every implementation? I didn't find any serializer setting which helped me.
After a bit of searching, I found this question:
How to inherit the attribute from interface to object when serializing it using JSON.NET
I took the code by Jeff Sternal and added JsonIgnoreAttribute detection, so it looks like this:
class InterfaceContractResolver : DefaultContractResolver
{
public InterfaceContractResolver() : this(false) { }
public InterfaceContractResolver(bool shareCache) : base(shareCache) { }
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
var interfaces = member.DeclaringType.GetInterfaces();
foreach (var #interface in interfaces)
{
foreach (var interfaceProperty in #interface.GetProperties())
{
// This is weak: among other things, an implementation
// may be deliberately hiding an interface member
if (interfaceProperty.Name == member.Name && interfaceProperty.MemberType == member.MemberType)
{
if (interfaceProperty.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Any())
{
property.Ignored = true;
return property;
}
if (interfaceProperty.GetCustomAttributes(typeof(JsonPropertyAttribute), true).Any())
{
property.Ignored = false;
return property;
}
}
}
}
return property;
}
}
Using this InterfaceContractResolver in my JsonSerializerSettings, all properties which have a JsonIgnoreAttribute in any interface are ignored, too, even if they have a JsonPropertyAttribute (due to the order of the inner if blocks).
In more recent versions of Json.NET, applying [JsonIgnore] to interface properties now just works and successfully prevents them from being serialized for all implementing types, as long as the property is declared on the same class where the interface is declared. A custom contract resolver is no longer required.
For instance, if we define the following types:
public interface IFoo
{
[JsonIgnore]
string SecretProperty { get; set; }
string Include { get; set; }
}
public class Foo : IFoo
{
public string SecretProperty { get; set; }
public string Include { get; set; }
}
Then the following test passes in Json.NET 11 and 12 (and probably earlier versions also):
var root = new Foo
{
SecretProperty = "Ignore Me",
Include = "Include Me",
};
var json = JsonConvert.SerializeObject(root);
Assert.IsTrue(json == "{\"Include\":\"Include Me\"}");// Passes
Demo fiddles here and here.
I believe this was added in Json.NET 4.0.3 despite the fact that JsonIgnore was not mentioned explicitly in the release notes:
New feature - JsonObject and JsonProperty attributes can now be placed on an interface and used when serializing implementing objects.
(The implementation can be found in JsonTypeReflector.GetAttribute<T>(MemberInfo memberInfo).)
However, as noted by Vitaly, this does not work when the property is inherited from a base class of the class where the interface is declared. Demo fiddle here.
I have found it's simplest to create a DTO of only the properties I want and serialize that object to JSON. it creates many small, context specific objects but managing the code base is much easier and I don't have to think about what I'm serializing vs what I'm ignoring.
You should add [DataContract] in front of the class name.
It changes the default from including all properties, to including only explicitly marked properties. After that, add '[DataMember]' in front of each property you want to include in the JSON output.
Related
I have different configurations all inheriting from a base configuration that are customized in forms. I want all of these to be handled by a single action result.
[HttpPost]
public IActionResult Register(AbstractBaseConfig config)
{
...do some logic...
return View("../Home/Index");
}
However, this is not possible because you cannot base in abstract classes as a parameter to an action result. Is there any work around for this so I don't need a seperate action result for each configuration? (I still want each configuration to be it's own class, I only need access to the base class methods in the action result logic).
Basically you can't, and the reason is that MVC will try to do new AbstractBaseConfig() as part of the Data Binding process (which parses the URL or the Form Post and puts the results in a concrete object). And by definition, doing new AbstractBaseConfig() is impossible for an abstract class.
It also makes sense for other reasons, I will explain why.
You seem to expect that MVC can determine the class from the parameters that are being passed in. That is not how it works, in fact the opposite is true: the Action Method has to specify the exact class, and then the Binder will instantiate that exact class and try to bind its properties.
Suppose you had this:
public abstract class Thing { public int ID { get;set; } }
public class NamedThing : Thing { public string Name { get;set; } }
public class OtherThing : Thing { public string Name { get;set; } }
and suppose it would be allowed to use:
public IActionResult Register(Thing thing)
then what would you expect to be in thing after Data Binding: a Thing object with only the ID set? Or one of the other object types, with Name set, but how would MVC ever be able to know which class you meant?
So for all these reasons, this is not possible.
You could have a base class inherit the abstract class and all your classes inherit from that base class whilst having that base class as your parameter
Take for example
public abstract class ABase
{
public void stuff()
{
var stuff = string.Empty;
stuff = "hello";
}
public virtual void otherstuff()
{
var stuff = string.Empty;
stuff = "hello";
}
}
public class Base : ABase
{
//empty
}
public class Derived : Base
{
public void mystuff()
{
this.stuff();
}
public override void otherstuff()
{
// Custom code
}
}
public ActionResult Register(Base config)
{
}
the [ScriptIgnore] attribute works fine for me on the direct object that is getting serialized, but if I put it on a property of a related object (that is referenced from a property on the direct object), it doesn't apply?
i.e Json(user)
I.E
class user {
Badges badges
}
class Badge {
[ScriptIgnore]
SomeObject obj; //Causes circular reference error because scriptignore doesn't apply
}
Is there a way to get around this?
Is there a way to get around this?
I would recommend you using a view model exposing only the properties you need and passing the view model to the Json method.
If you don't want to follow the view model approach I recommend then the [ScriptIgnore] attribute should also work for you.
Example:
using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;
public class User
{
public IEnumerable<Badge> Badges { get; set; }
}
public class Badge
{
[ScriptIgnore]
public User User { get; set; }
}
public class Program
{
static void Main()
{
var user = new User();
var badge = new Badge { User = user };
user.Badges = new[] { badge };
var serializer = new JavaScriptSerializer();
Console.WriteLine(serializer.Serialize(user));
}
}
If you remove the [ScriptIgnore] attribute from the User property on the Badge class JSON serialization will fail due to circular reference error.
I'm using the NewtonSoft JSON.NET library for serializing the following class where DTOBase can hold derived instances.
public class Command
{
public DTOBase CommandDTO { get; set; }
}
Per this article you need to include the JsonProperty attribute so that the derived instances get deserialized properly
public class Command
{
[JsonProperty(TypeNameHandling = TypeNameHandling.All)]
public DTOBase CommandDTO { get; set; }
}
The question is whether there is any other way besides using an attribute to get the same result? I would prefer to not be coupled to the NewtonSoft library and json serialization in particular at the class level. Is there a way to specify some settings on the Serialize/Deserialize methods of the library at all to get the same result?
The TypeNameHandling property can be set on JsonSerializerSettings when you call JsonConvert.SerializeObject(value, settings).
If you only want the name included for derived objects set TypeNameHandling to TypeNameHandling.Auto.
When calling "JsonConvert.SerializeObject" I am passing in an object that implements an interface. It is the interface that defines the JsonProperty attributes to set the desired JSON object property name. However when I examine the JSON object that is produced it is using the actual .NET property name, rather than JsonPropertyAttribute value. This leads me to believe it is only reflecting over the implementation of the interface to find the JsonProperty attributes, rather than the interface itself. I have verified that if I place the JsonProperty attributes on the implementing class then everything works as expected, but this is not the desired behaviour. Is there any way to make JSON.NET pick up the JsonPropertyAttributes defined upon the interface as well as (or instead of) the interface.
public interface ISpecifyDataPageToGet
{
[JsonProperty("offset")]
int PageNumber { get; }
[JsonProperty("limit")]
int PageSize { get; }
}
public class PageInfo : ISpecifyDataPageToGet
{
public PageInfo(int pageNumber, int pageSize)
{
this.PageNumber = pageNumber;
this.PageSize = pageSize;
}
// I don't want to have to define JsonProperty attribute here
public int PageNumber { get; private set; }
// Or here
public int PageSize { get; private set; }
}
public void MakeCall(ISpecifyDataPageToGet requestMessage)
{
// I'm passing instance of interface in here, but it still only picks up
// attributes defined on class implementing interface.
JsonConvert.SerializeObject(requestMessage, Formatting.None, new JsonSerializerSettings());
...
...
}
UPDATE: Reported on Codeplex project site
This has now been fixed in the Json.NET codebase by James and is working.
See the codeplex issue report as well as the Json.NET 4.0 Release 3 release notes:
New feature - JsonObject and JsonProperty attributes can now be placed on an interface and used when serializing implementing objects.
I am trying to create a custom attribute in mvc to use it's parameters in a view as breadCrumb.
well, this is the code of the attribute
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class BreadCrumbAttribute : Attribute {
public BreadCrumbAttribute(string title, string parent, string url) {
this._title = title;
this._parent = parent;
this._url = url;
}
#region named parameters properties
private string _title;
public string Title {
get { return _title; }
}
private string _url;
public string Url {
get { return _url; }
}
private string _parent;
public string Parent {
get { return _parent; }
}
#endregion
#region positional parameters properties
public string Comments { get; set; }
#endregion
}
this is the call of the attribute
[BreadCrumbAttribute("tile", "parent name", "url")]
public ActionResult Index() {
//code goes here
}
this is a way of how I'd like to get the values. (this is a partial view)
System.Reflection.MemberInfo inf = typeof(ProductsController);
object[] attributes;
attributes = inf.GetCustomAttributes(typeof(BreadCrumbAttribute), false);
foreach (Object attribute in attributes) {
var bca = (BreadCrumbAttribute)attribute;
Response.Write(string.Format("{0}><a href={1}>{2}</a>", bca.Parent, bca.Url, bca.Title));
}
Unfortunately, the attribute didn't get call with the way I implement it. Although, If I add the attribute in Class instead of an Action method it worked.
How could I make it work?
Thanks
The problem is that you are using reflection to get the attributes for the class, so naturally it does not include attributes defined on the action method.
To get those, you should define an ActionFilterAttribute, and in the OnActionExecuting or OnActionExecuted method, you can use filterContext.ActionDescriptor.GetCustomAttributes() method (MSDN description here).
Note that with this solution, you will likely have two different types of attributes: The first one is the one you wrote, to define the breadcrumbs. The second is the one that looks at the attributes on the executing action and builds up the breadcrumb (and presumably adds it to the ViewModel or sticks it in HttpContext.Items or something).