I am exactly in the same case that this question:
How do I make JSON.NET ignore object relationships?
I see the proposed solution and I know I must use a Contract Revolver, and I also see the code of the Contract Resolver, but I do not know how to use it.
Should I use it in the WebApiConfig.vb?
Should I modify my Entity Model anyway?
It is a useful question👍 and I hope this help:
A)
If you have created your models manually (without Entity Framework), mark the relation properties as virtual first.
If your models were created by EF, It has already done it for you and each Relation Property is marked as virtual, as seen below:
Sample class:
public class PC
{
public int FileFolderId {get;set;}
public virtual ICollection<string> Libs { get; set; }
public virtual ICollection<string> Books { get; set; }
public virtual ICollection<string> Files { get; set; }
}
B)
Those relation properties can now be ignored by the JSON serializer by using the following ContractResolver for JSON.NET:
CustomResolver:
class CustomResolver : DefaultContractResolver
{
private readonly List<string> _namesOfVirtualPropsToKeep=new List<string>(new String[]{});
public CustomResolver(){}
public CustomResolver(IEnumerable<string> namesOfVirtualPropsToKeep)
{
this._namesOfVirtualPropsToKeep = namesOfVirtualPropsToKeep.Select(x=>x.ToLower()).ToList();
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
var propInfo = member as PropertyInfo;
if (propInfo != null)
{
if (propInfo.GetMethod.IsVirtual && !propInfo.GetMethod.IsFinal
&& !_namesOfVirtualPropsToKeep.Contains(propInfo.Name.ToLower()))
{
prop.ShouldSerialize = obj => false;
}
}
return prop;
}
}
C)
Finally, to serialize your model easily use the above ContractResolver. Set it up like this:
// -------------------------------------------------------------------
// Serializer settings
JsonSerializerSettings settings = new JsonSerializerSettings
{
// ContractResolver = new CustomResolver();
// OR:
ContractResolver = new CustomResolver(new []
{
nameof(PC.Libs), // keep Libs property among virtual properties
nameof(PC.Files) // keep Files property among virtual properties
}),
PreserveReferencesHandling = PreserveReferencesHandling.None,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Formatting = Formatting.Indented
};
// -------------------------------------------------------------------
// Do the serialization and output to the console
var json = JsonConvert.SerializeObject(new PC(), settings);
Console.WriteLine(json);
// -------------------------------------------------------------------
// We can see that "Books" filed is ignored in the output:
// {
// "FileFolderId": 0,
// "Libs": null,
// "Files": null
// }
Now, all the navigation (relation) properties [virtual properties] will be ignored automatically except you keep some of them by determine them in your code.😎
Live DEMO
Thanks from #BrianRogers for his answer here.
If you are using Newtonsoft.Json
Mark field with
Newtonsoft.Json.JsonIgnore
Instead of
System.Text.Json.Serialization.JsonIgnore
Related
I have implemented the custom contract resolver to remove the some properties from serialized Json. Most of the time below code works but occasionally this code fails. Sometime it loops through the list and skips item I want to exclude which causing to appear that properties which I wanted to exclude.
class TestClass
{
public static void Test()
{
var objectToSerialise = //Coming from some WebAPI calls
string[] stringToSkip = {"skip1", "skip2"};
var settings = new JsonSerializerSettings
{
ContractResolver = new MyContractResolver(stringToSkip)
};
var json = JsonConvert.SerializeObject(objectToSerialise, settings);
}
}
public class MyContractResolver : DefaultContractResolver {
private string[] _skipthis;
public MyContractResolver(string[] skipthis)
{
_skipthis = skipthis;
}
private JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization){
var property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = (prop) =>{
return !(_skipthis.Where(n => property.PropertyName.Contains(n)).Any());
};
return property;
}
}
Can someone please suggest why this code is failing silently intermittently with out throwing any kind of exception?
Note that skipthis array is not modified outside this. I want to skip the property when property name exist or substring of propertyname exist in skipthis array.
I have developed a custom validator Attribute class for checking Integer values in my model classes. But the problem is this class is not working. I have debugged my code but the breakpoint is not hit during debugging the code. Here is my code:
public class ValidateIntegerValueAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
int output;
var isInteger = int.TryParse(value.ToString(), out output);
if (!isInteger)
{
return new ValidationResult("Must be a Integer number");
}
}
return ValidationResult.Success;
}
}
I have also an Filter class for model validation globally in application request pipeline. Here is my code:
public class MyModelValidatorFilter: IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
if (context.ModelState.IsValid)
return;
var errors = new Dictionary<string, string[]>();
foreach (var err in actionContext.ModelState)
{
var itemErrors = new List<string>();
foreach (var error in err.Value.Errors){
itemErrors.Add(error.Exception.Message);
}
errors.Add(err.Key, itemErrors.ToArray());
}
actionContext.Result = new OkObjectResult(new MyResponse
{
Errors = errors
});
}
}
The model class with validation is below:
public class MyModelClass
{
[ValidateIntegerValue(ErrorMessage = "{0} must be a Integer Value")]
[Required(ErrorMessage = "{0} is required")]
public int Level { get; set; }
}
Can anyone please let me know why the attribute integer validation class is not working.
Model validation comes into play after the model is deserialized from the request. If the model contains integer field Level and you send value that could not be deserialized as integer (e.g. "abc"), then model will not be even deserialized. As result, validation attribute will also not be called - there is just no model for validation.
Taking this, there is no much sense in implementing such ValidateIntegerValueAttribute. Such validation is already performed by deserializer, JSON.Net in this case. You could verify this by checking model state in controller action. ModelState.IsValid will be set to false and ModelState errors bag will contain following error:
Newtonsoft.Json.JsonReaderException: Could not convert string to
integer: abc. Path 'Level', ...
One more thing to add: for correct work of Required validation attribute, you should make the underlying property nullable. Without this, the property will be left at its default value (0) after model deserializer. Model validation has no ability to distinguish between missed value and value equal to default one. So for correct work of Required attribute make the property nullable:
public class MyModelClass
{
[Required(ErrorMessage = "{0} is required")]
public int? Level { get; set; }
}
I am using Json.net in my MVC 4 program.
I have an object item of class Item.
I did:
string j = JsonConvert.SerializeObject(item);
Now I want to add an extra property, like "feeClass" : "A" into j.
How can I use Json.net to achieve this?
You have a few options.
The easiest way, as #Manvik suggested, is simply to add another property to your class and set its value prior to serializing.
If you don't want to do that, the next easiest way is to load your object into a JObject, append the new property value, then write out the JSON from there. Here is a simple example:
class Item
{
public int ID { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
Item item = new Item { ID = 1234, Name = "FooBar" };
JObject jo = JObject.FromObject(item);
jo.Add("feeClass", "A");
string json = jo.ToString();
Console.WriteLine(json);
}
}
Here is the output of the above:
{
"ID": 1234,
"Name": "FooBar",
"feeClass": "A"
}
Another possibility is to create a custom JsonConverter for your Item class and use that during serialization. A JsonConverter allows you to have complete control over what gets written during the serialization process for a particular class. You can add properties, suppress properties, or even write out a different structure if you want. For this particular situation, I think it is probably overkill, but it is another option.
Following is the cleanest way I could implement this
dynamic obj = JsonConvert.DeserializeObject(jsonstring);
obj.NewProperty = "value";
var payload = JsonConvert.SerializeObject(obj);
You could use ExpandoObject.
Deserialize to that, add your property, and serialize back.
Pseudocode:
Expando obj = JsonConvert.Deserializeobject<Expando>(jsonstring);
obj.AddeProp = "somevalue";
string addedPropString = JsonConvert.Serializeobject(obj);
I think the most efficient way to serialize a property that doesn't exist in the type is to use a custom contract resolver. This avoids littering your class with the property you don't want, and also avoids the performance hit of the extra serialization round trip that most of the other options on this page incur.
public class SpecialItemContractResolver : DefaultContractResolver {
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) {
var list = base.CreateProperties(type, memberSerialization);
if (type.Equals(typeof(Item))) {
var feeClassProperty = CreateFeeClassProperty();
list.Add(feeClassProperty);
}
return list;
}
private JsonProperty CreateFeeClassProperty() {
return new JsonProperty {
PropertyName = "feeClass",
PropertyType = typeof(string),
DeclaringType = typeof(Item),
ValueProvider = new FeeClassValueProvider(),
AttributeProvider = null,
Readable = true,
Writable = false,
ShouldSerialize = _ => true
};
}
private class FeeClassValueProvider : IValueProvider {
public object GetValue(object target) => "A";
public void SetValue(object target, object value) { }
}
}
To use this functionality:
// This could be put in a static readonly place so it's reused
var serializerSettings = new JsonSerializerSettings {
ContractResolver = new SpecialItemContractResolver()
};
// And then to serialize:
var item = new Item();
var json = JsonConvert.Serialize(item, serializerSettings);
The architecture: Win8 app + local Web API Self-Host share a common "Contracts" project.
The Web API returns very general contract types (IEnumerable etc.).
Within the Win8 app I want to convert these contracts to concrete MVVM compatible model objects which use ObservableCollection for example instead of IEnumerables.
I would have loved to use AutoMapper for this task but it is not compatible with the WinRT.
I used AutoMapper some time ago, but now I generally use a specific class to do this work so I can test it and implement "strange" logic. This class is responsible for the mapping in the 2 direction (if both are needed).
Sometimes, because I'm lazy ;-), I have used an implicit conversion operator to simplify the conversion, but I think that conceptually a constructor for the dto could be better:
public class ItemDto
{
public Int32 Id { get; set; }
public String Description { get; set; }
public static implicit operator ItemDto (Item item)
{
var dto = new ItemDto()
{
Id = item.Id,
Description = item.LongDescription
};
return dto;
}
In all these cases, I think that the possibility to test your mapping has a great value.
You can to use reflection ( System.Reflection) for mapper yours DTOs by yourself, in a loop by the properties and mapping using the portable CLR types.
Thank you for your suggestions.
I solved it in a non-generic fashion, for every model I do have a specific converter that does the job. What do you think?
using Project.Contracts;
using Project.Models;
namespace Project.Converters.Contracts
{
public static class ProductConverter
{
public static ProductContract ToContract(this Product model)
{
if (model == null)
{
return new ProductContract();
}
return new ProductContract
{
Id = model.Id,
Name = mode.Name,
Tags = model.Tags.ToContracts()
};
}
public static ICollection<ProductContract> ToContracts(this IEnumerable<Product> models)
{
if (models == null)
{
return new Collection<ProductContract>();
}
return models.Select(m => m.ToContract()).ToList();
}
public static Product ToModel(this ProductContract contract)
{
if (contract == null)
{
return new Product();
}
return new Product
{
Id = contract.Id,
Name = contract.Name,
Tags = contract.Tags.ToModels()
};
}
public static ObservableCollection<Product> ToModels(this IEnumerable<ProductContract> contracts)
{
if (contracts == null)
{
return new ObservableCollection<Product>();
}
return new ObservableCollection<Product>(contracts.Select(c => c.ToModel()));
}
}
}
I am having an issue with ASP.Net MVC3 (RC2). I'm finding that the new JSON model binding functionality, which is implicit in MVC3, does not want to deserialize to a property that has an enum type.
Here's a sample class and enum type:
public enum MyEnum { Nothing = 0, SomeValue = 5 }
public class MyClass
{
public MyEnum Value { get; set; }
public string OtherValue { get; set; }
}
Consider the following code, which successfully passes the unit test:
[TestMethod]
public void Test()
{
var jss = new JavaScriptSerializer();
var obj1 = new MyClass { Value = MyEnum.SomeValue };
var json = jss.Serialize(obj1);
var obj2 = jss.Deserialize<MyClass>(json);
Assert.AreEqual(obj1.Value, obj2.Value);
}
If I serialize obj1 above, but then post that data to an MVC3 controller (example below) with a single parameter of type MyClass, any other properties of the object deserialize properly, but any property that is an enum type deserializes to the default (zero) value.
[HttpPost]
public ActionResult TestAction(MyClass data)
{
return Content(data.Value.ToString()); // displays "Nothing"
}
I've downloaded the MVC source code from codeplex but I'm stumped as to where the actual code performing the deserialization occurs, which means I can't work out what the folks at Microsoft have used to perform the deserialization and thus determine if I'm doing something wrong or if there is a workaround.
Any suggestions would be appreciated.
I've found the answer. I hope this is fixed in MVC3 RTM, but essentially what happens is the object deserializes correctly internally via JsonValueProviderFactory, which uses JavaScriptSerializer to do the work. It uses DeserializeObject() so that it can pass the values back to the default model binder. The problem is that the default model binder won't convert/assign an int value when the property type is an enum.
There is a discussion of this at the ASP.Net forums here:
http://forums.asp.net/p/1622895/4180989.aspx
The solution discussed there is to override the default model binder like so:
public class EnumConverterModelBinder : DefaultModelBinder
{
protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
{
var propertyType = propertyDescriptor.PropertyType;
if(propertyType.IsEnum)
{
var providerValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if(null != providerValue)
{
var value = providerValue.RawValue;
if(null != value)
{
var valueType = value.GetType();
if(!valueType.IsEnum)
{
return Enum.ToObject(propertyType, value);
}
}
}
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
}
Then in Application_Start, add the following line:
ModelBinders.Binders.DefaultBinder = new EnumConverterModelBinder();
How are you calling this action? Have you tried:
$.post(
'/TestAction',
JSON.stringify({ OtherValue : 'foo', Value: 5 }),
function(result) {
alert('ok');
}
);