JSON different serialization from deserialization in .NET [duplicate] - c#

We have some configuration files which were generated by serializing C# objects with Json.net.
We'd like to migrate one property of the serialised class away from being a simple enum property into a class property.
One easy way to do this, would be to leave the old enum property on the class, and arrange for Json.net to read this property when we load the config, but not to save it again when we next serialize the object. We'll deal with generating the new class from the old enum separately.
Is there any simple way to mark (e.g. with attributes) a property of a C# object, so that Json.net will ignore it ONLY when serializing, but attend to it when deserializing?

There are actually several fairly simple approaches you can use to achieve the result you want.
Let's assume, for example, that you have your classes currently defined like this:
class Config
{
public Fizz ObsoleteSetting { get; set; }
public Bang ReplacementSetting { get; set; }
}
enum Fizz { Alpha, Beta, Gamma }
class Bang
{
public string Value { get; set; }
}
And you want to do this:
string json = #"{ ""ObsoleteSetting"" : ""Gamma"" }";
// deserialize
Config config = JsonConvert.DeserializeObject<Config>(json);
// migrate
config.ReplacementSetting =
new Bang { Value = config.ObsoleteSetting.ToString() };
// serialize
json = JsonConvert.SerializeObject(config);
Console.WriteLine(json);
To get this:
{"ReplacementSetting":{"Value":"Gamma"}}
Approach 1: Add a ShouldSerialize method
Json.NET has the ability to conditionally serialize properties by looking for corresponding ShouldSerialize methods in the class.
To use this feature, add a boolean ShouldSerializeBlah() method to your class where Blah is replaced with the name of the property that you do not want to serialize. Make the implementation of this method always return false.
class Config
{
public Fizz ObsoleteSetting { get; set; }
public Bang ReplacementSetting { get; set; }
public bool ShouldSerializeObsoleteSetting()
{
return false;
}
}
Note: if you like this approach but you don't want to muddy up the public interface of your class by introducing a ShouldSerialize method, you can use an IContractResolver to do the same thing programmatically. See Conditional Property Serialization in the documentation.
Approach 2: Manipulate the JSON with JObjects
Instead of using JsonConvert.SerializeObject to do the serialization, load the config object into a JObject, then simply remove the unwanted property from the JSON before writing it out. It's just a couple of extra lines of code.
JObject jo = JObject.FromObject(config);
// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();
json = jo.ToString();
Approach 3: Clever (ab)use of attributes
Apply a [JsonIgnore] attribute to the property that you do not want to be serialized.
Add an alternate, private property setter to the class with the same type as the original property. Make the implementation of that property set the original property.
Apply a [JsonProperty] attribute to the alternate setter, giving it the same JSON name as the original property.
Here is the revised Config class:
class Config
{
[JsonIgnore]
public Fizz ObsoleteSetting { get; set; }
[JsonProperty("ObsoleteSetting")]
private Fizz ObsoleteSettingAlternateSetter
{
// get is intentionally omitted here
set { ObsoleteSetting = value; }
}
public Bang ReplacementSetting { get; set; }
}

For any situation where it's acceptable to have your deserialization-only property be marked internal, there's a remarkably simple solution that doesn't depend on attributes at all. Simply mark the property as internal get, but public set:
public class JsonTest {
public string SomeProperty { internal get; set; }
}
This results in correct deserialization using default settings/resolvers/etc., but the property is stripped from serialized output.

I like sticking with attributes on this one, here is the method I use when needing to deserialize a property but not serialize it or vice versa.
STEP 1 - Create the custom attribute
public class JsonIgnoreSerializationAttribute : Attribute { }
STEP 2 - Create a custom Contract Reslover
class JsonPropertiesResolver : DefaultContractResolver
{
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
//Return properties that do NOT have the JsonIgnoreSerializationAttribute
return objectType.GetProperties()
.Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute)))
.ToList<MemberInfo>();
}
}
STEP 3 - Add attribute where serialization is not needed but deserialization is
[JsonIgnoreSerialization]
public string Prop1 { get; set; } //Will be skipped when serialized
[JsonIgnoreSerialization]
public string Prop2 { get; set; } //Also will be skipped when serialized
public string Prop3 { get; set; } //Will not be skipped when serialized
STEP 4 - Use it
var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });
Hope this helps! Also it's worth noting that this will also ignore the properties when Deserialization happens, when I am derserializing I just use the converter in the conventional way.
JsonConvert.DeserializeObject<MyType>(myString);

Use setter property:
[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { _ignoreOnSerializing = value; } }
[JsonIgnore]
private string _ignoreOnSerializing;
[JsonIgnore]
public string IgnoreOnSerializing
{
get { return this._ignoreOnSerializing; }
set { this._ignoreOnSerializing = value; }
}
Hope this help.

After i spent a quite long time searching how to flag a class property to be De-Serializable and NOT Serializable i found that there's no such thing to do that at all; so i came up with a solution that combines two different libraries or serialization techniques (System.Runtime.Serialization.Json & Newtonsoft.Json) and it worked for me like the following:
flag all your class and sub-classes as "DataContract".
flag all the properties of your class and sub-classes as "DataMember".
flag all the properties of your class and sub-classes as "JsonProperty" except those you want them not to be serialized.
now flag the properties the you do NOT want it to be serialized as "JsonIgnore".
then Serialize using "Newtonsoft.Json.JsonConvert.SerializeObject" and De-Serialize using "System.Runtime.Serialization.Json.DataContractJsonSerializer".
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
namespace LUM_Win.model
{
[DataContract]
public class User
{
public User() { }
public User(String JSONObject)
{
MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(JSONObject));
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(User));
User user = (User)dataContractJsonSerializer.ReadObject(stream);
this.ID = user.ID;
this.Country = user.Country;
this.FirstName = user.FirstName;
this.LastName = user.LastName;
this.Nickname = user.Nickname;
this.PhoneNumber = user.PhoneNumber;
this.DisplayPicture = user.DisplayPicture;
this.IsRegistred = user.IsRegistred;
this.IsConfirmed = user.IsConfirmed;
this.VerificationCode = user.VerificationCode;
this.Meetings = user.Meetings;
}
[DataMember(Name = "_id")]
[JsonProperty(PropertyName = "_id")]
public String ID { get; set; }
[DataMember(Name = "country")]
[JsonProperty(PropertyName = "country")]
public String Country { get; set; }
[DataMember(Name = "firstname")]
[JsonProperty(PropertyName = "firstname")]
public String FirstName { get; set; }
[DataMember(Name = "lastname")]
[JsonProperty(PropertyName = "lastname")]
public String LastName { get; set; }
[DataMember(Name = "nickname")]
[JsonProperty(PropertyName = "nickname")]
public String Nickname { get; set; }
[DataMember(Name = "number")]
[JsonProperty(PropertyName = "number")]
public String PhoneNumber { get; set; }
[DataMember(Name = "thumbnail")]
[JsonProperty(PropertyName = "thumbnail")]
public String DisplayPicture { get; set; }
[DataMember(Name = "registered")]
[JsonProperty(PropertyName = "registered")]
public bool IsRegistred { get; set; }
[DataMember(Name = "confirmed")]
[JsonProperty(PropertyName = "confirmed")]
public bool IsConfirmed { get; set; }
[JsonIgnore]
[DataMember(Name = "verification_code")]
public String VerificationCode { get; set; }
[JsonIgnore]
[DataMember(Name = "meeting_ids")]
public List<Meeting> Meetings { get; set; }
public String toJSONString()
{
return JsonConvert.SerializeObject(this, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
}
}
}
Hope that helps ...

Depending on where in the application this takes place and if it's just one property, one manual way you can do this is by setting the property value to null and then on the model you can specify that the property be ignored if the value is null:
[JsonProperty(NullValueHandling = NullValue.Ignore)]
public string MyProperty { get; set; }
If you are working on an ASP.NET Core web app, you can globally set this for all properties in all models by setting this in your Startup.cs file:
public void ConfigureServices(IServiceCollection services) {
// other configuration here
services.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}

with reference to #ThoHo's solution, using the setter is actually all that is needed, with no additional tags.
For me I previously had a single reference Id, that I wanted to load and add to the new collection of reference Ids. By changing the definition of the reference Id to only contain a setter method, which added the value to the new collection. Json can't write the value back if the Property doesn't have a get; method.
// Old property that I want to read from Json, but never write again. No getter.
public Guid RefId { set { RefIds.Add(value); } }
// New property that will be in use from now on. Both setter and getter.
public ICollection<Guid> RefIds { get; set; }
This class is now backwards compatible with the previous version and only saves the RefIds for the new versions.

To build upon Tho Ho's answer, this can also be used for fields.
[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { IgnoreOnSerializing = value; } }
[JsonIgnore]
public string IgnoreOnSerializing;

If you use JsonConvert,IgnoreDataMemberAttribute is ok.My standard library not refrence Newton.Json,and I use [IgnoreDataMember] to control object serialize.
From Newton.net help document.

Is there any simple way to mark (e.g. with attributes) a property of a C# object, so that Json.net will ignore it ONLY when serializing, but attend to it when deserializing?
The easiest way I've found as of this writing is to include this logic in your IContractResolver.
Sample code from above link copied here for posterity:
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public bool ShouldSerializeManager()
{
// don't serialize the Manager property if an employee is their own manager
return (Manager != this);
}
}
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
{
property.ShouldSerialize =
instance =>
{
Employee e = (Employee)instance;
return e.Manager != e;
};
}
return property;
}
}
All of the answers are good but this approach seemed like the cleanest way. I actually implemented this by looking for an attribute on the property for SkipSerialize and SkipDeserialize so you can just mark up any class you control. Great question!

Jraco11's answer is very neat. In case, if you want to use the same IContractResolver both for serialization and deserialization, then you can use the following:
public class JsonPropertiesResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (member.IsDefined(typeof(JsonIgnoreSerializationAttribute)))
{
property.ShouldSerialize = instance => false;
}
return property;
}
}

thats will do the trick, create a property with set only
example 1:
https://dotnetfiddle.net/IxMXcG
[JsonProperty("disabled-protections")]
public JArray DisabledProtections { set => IsPartialResult = (value != null && value.HasValues); }
public bool IsPartialResult { get; private set; }
example 2:
private JArray _disabledProtections;
[JsonProperty("disabled-protections")]
public JArray DisabledProtections { set => _disabledProtections = value; }
public bool IsPartialResult => _disabledProtections != null && _disabledProtections.HasValues;

Use [JsonIgnore] attribute in the public property of the model class.

Related

Can I use Partial Classes to determine how data models get Json Serialized in .Net Core 2.0 API [duplicate]

[Table("LegalEntity")]
[ModelMetadataType(typeof(LegalEntityMeta))]
public class LegalEntity : Entity<long>
{
}
public class LegalEntityMeta
{
[JsonProperty(PropertyName = "LegalEntityId")]
public long Id { get; set; }
[JsonProperty(PropertyName = "LegalEntityName")]
public string Name { get; set; }
}
In the Startup.cs ....
services
.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
})
.AddAutoMapper(typeof(Startup))
.AddMvcCore()
.AddJsonFormatters()
.AddApiExplorer();
My expectation is to see json with attributes legalEntityId and legalEntityName yet the json produced has id and name as attributes.
Can someone pleas help me with how to change the json attributes?
Thanks
Anand
Json.NET currently has no support for Microsoft.AspNetCore.Mvc.ModelMetadataTypeAttribute. In Issue #1349: Add support for ModelMetadataType for dotnetcore like supported MetadataTypeAttribute in previous versions a request to implement support for it was declined.
Json.NET does support System.ComponentModel.DataAnnotations.MetadataTypeAttribute, albeit with some limitations described in this answer, however even if this attribute were present in .Net core (not sure it is) it would not help you, because you are trying to use the metadata type of a derived class to rename the properties in a base type, which is not an intended usage for metadata type information. I.e. the following works out of the box (in full .Net):
[System.ComponentModel.DataAnnotations.MetadataType(typeof(EntityMeta))]
public class Entity<T>
{
public T Id { get; set; }
public string Name { get; set; }
}
public class EntityMeta
{
[JsonProperty(PropertyName = "LegalEntityId")]
public long Id { get; set; }
[JsonProperty(PropertyName = "LegalEntityName")]
public string Name { get; set; }
}
But the following does not:
[System.ComponentModel.DataAnnotations.MetadataType(typeof(LegalEntityMeta))]
public class LegalEntity : Entity<long>
{
}
public class LegalEntityMeta
{
[JsonProperty(PropertyName = "LegalEntityId")]
public long Id { get; set; }
[JsonProperty(PropertyName = "LegalEntityName")]
public string Name { get; set; }
}
Why doesn't Json.NET allow derived type metadata information to modify base type contracts? You would have to ask Newtonsoft, but guesses include:
Json.NET is a contract-based serializer where each type specifies its contract through attributes. It's not intended that one type could rewrite the contract of a second type.
DataContractJsonSerializer and DataContractSerializer work the same way.
Doing so would violate the Liskov substitution principle.
So, what are your options?
You could serialize a DTO in place of your LegalEntity, and use something like automapper to map between then:
public class LegalEntityDTO
{
[JsonProperty(PropertyName = "LegalEntityId")]
public long Id { get; set; }
[JsonProperty(PropertyName = "LegalEntityName")]
public string Name { get; set; }
}
You could create a custom JsonConverter for LegalEntity with the necessary logic.
You could create a custom contract resolver with the necessary logic, similar to the one here, for instance the following:
using System.Reflection;
public class ModelMetadataTypeAttributeContractResolver : DefaultContractResolver
{
public ModelMetadataTypeAttributeContractResolver()
{
// Default from https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonSerializerSettingsProvider.cs
this.NamingStrategy = new CamelCaseNamingStrategy();
}
const string ModelMetadataTypeAttributeName = "Microsoft.AspNetCore.Mvc.ModelMetadataTypeAttribute";
const string ModelMetadataTypeAttributeProperty = "MetadataType";
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
var propertyOverrides = GetModelMetadataTypes(type)
.SelectMany(t => t.GetProperties())
.ToLookup(p => p.Name, p => p);
foreach (var property in properties)
{
var metaProperty = propertyOverrides[property.UnderlyingName].FirstOrDefault();
if (metaProperty != null)
{
var jsonPropertyAttribute = metaProperty.GetCustomAttributes<JsonPropertyAttribute>().FirstOrDefault();
if (jsonPropertyAttribute != null)
{
property.PropertyName = jsonPropertyAttribute.PropertyName;
// Copy other attributes over if desired.
}
}
}
return properties;
}
static Type GetModelMetadataType(Attribute attribute)
{
var type = attribute.GetType();
if (type.FullName == ModelMetadataTypeAttributeName)
{
var property = type.GetProperty(ModelMetadataTypeAttributeProperty);
if (property != null && property.CanRead)
{
return property.GetValue(attribute, null) as Type;
}
}
return null;
}
static Type[] GetModelMetadataTypes(Type type)
{
var query = from t in type.BaseTypesAndSelf()
from a in t.GetCustomAttributes(false).Cast<System.Attribute>()
let metaType = GetModelMetadataType(a)
where metaType != null
select metaType;
return query.ToArray();
}
}
public static partial class TypeExtensions
{
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
}
Sample .Net fiddle.
To serialize directly, do:
var settings = new JsonSerializerSettings
{
ContractResolver = new ModelMetadataTypeAttributeContractResolver(),
};
var json = JsonConvert.SerializeObject(entity, Formatting.Indented, settings);
To install the contract resolver into Asp.Net Core see here.
Note I wrote this using full .Net 4.5.1 so it is just a prototype. .Net Core uses a different reflection API, however if you install System.Reflection.TypeExtensions as described here I believe it should work.
Switch to Newtonsoft.Json will help:
Add nuget package Microsoft.AspNetCore.Mvc.Newtonsoft.Json
In Startup.cs -> ConfigureServices (Read https://www.ryadel.com/en/use-json-net-instead-of-system-text-json-in-asp-net-core-3-mvc-projects/ for more information)
services.AddControllers().AddNewtonsoftJson();
Replace using System.Text.Json.Serialization and use MetadataType instead of ModelMetadataType:
using Newtonsoft.Json;
namespace YourDbDataNamespace
{
[MetadataType(typeof(UserMetadata))]
public partial class User {}
public class UserMetadata
{
[JsonProperty(PropertyName = "LegalEntityId")]
int Id { get; set; }
[JsonIgnore]
public string PasswordHash { get; set; }
}
}

Excluding properties on domain model objects from JSON

I'm using Newtonsoft JSON to serialize a graph of objects which are defined in a satellite assembly. I have a circular reference which is causing the recursion limit in JSON to be exceeded and adding extra bloat to an already data heavy POST.
The documented way would be to add a JsonIgnore attribute to the property on my data object, but that would require a Newtonsoft.Json reference in my domain project. Is there any other way of excluding a property from serialization?
Alternative 1
You should be able to use [DataContract] together with [DataMember].
[DataContract]
public class MyDomainModel
{
[DataMember]
public string PublicString { get; set; }
public string HiddenString { get; set; }
public MyDomainModel InfiniteReference { get; set; }
}
It requires a reference to System.Runtime.Serialization.
Example:
[HttpGet]
public MyDomainModel GetModels()
{
var model = new MyDomainModel
{
HiddenString = "Hidden",
PublicString = "Public",
};
model.InfiniteReference = model;
return model;
}
Output:
{
"publicString": "Public"
}
Alternative 2
I have a circular reference [...]
You could use ReferenceLoopHandling, which simply cuts the circular reference. But this might not be want you want, when you mentioned other extra bloat.
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
You can apply the [IgnoreDataMember] attribute on the model properties, which references System.Runtime.Serialization. All other properties will be serialized by default.
e.g.
public class MyViewModel
{
public string SerializedProperty { get; set; }
[IgnoreDataMember]
public string IgnoredProperty { get; set; }
}
Had a similar issue. I solved it in an entirely overwrought and overengineered fashion with a custom attribute and contract-resolver, when serializing, so I didn't need to reference any external libs (with the exception of System) in my model.
Declare your own attribute, such as
[AttributeUsage(AttributeTargets.Property)]
public sealed class IgnoreMeAttribute : Attribute
{
}
Decorate your model
[IgnoreMe]
public string IgnoreMePlease { get; set; }
Add a
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.AttributeProvider.GetAttributes(typeof (IgnoreMeAttribute), false).Any())
property.ShouldSerialize = instance => false;
return property;
}
}
and use it
var result = JsonConvert.SerializeObject(value, Formatting.None, new JsonSerializerSettings { ContractResolver = ShouldSerializeContractResolver.Instance });

Default value for missing properties with JSON.net

I'm using Json.net to serialize objects to database.
I added a new property to the class (which is missing in the json in the database) and I want the new property to have a default value when missing in the json.
I tried DefaultValue attribute but it's doesn't work. I'm using private setters and constructor to deserialize the json, so setting the value of the property in the constructor will not work as there is a parameter with the value.
Following is an example:
class Cat
{
public Cat(string name, int age)
{
Name = name;
Age = age;
}
public string Name { get; private set; }
[DefaultValue(5)]
public int Age { get; private set; }
}
static void Main(string[] args)
{
string json = "{\"name\":\"mmmm\"}";
Cat cat = JsonConvert.DeserializeObject<Cat>(json);
Console.WriteLine("{0} {1}", cat.Name, cat.Age);
}
I expect the age to be 5 but it is zero.
Any suggestions?
I found the answer, just need to add the following attribute as well:
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
In your example:
class Cat
{
public Cat(string name, int age)
{
Name = name;
Age = age;
}
public string Name { get; private set; }
[DefaultValue(5)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public int Age { get; private set; }
}
static void Main(string[] args)
{
string json = "{\"name\":\"mmmm\"}";
Cat cat = JsonConvert.DeserializeObject<Cat>(json);
Console.WriteLine("{0} {1}", cat.Name, cat.Age);
}
See Json.Net Reference
You can also have a default value as:
class Cat
{
public string Name { get; set; }
public int Age { get; set; } = 1 ; // one is the default value. If json property does not exist when deserializing the value will be one.
}
Add [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] ,Change your Age property from
[DefaultValue(5)]
public int Age { get; private set; }
to
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[DefaultValue(5)]
public string Age { get; private set; }
I had same issue as someone already described where probably existing constructor made DefaultValueHandling not working. I played a bit around and found out that it can be bypassed with NullValueHandling.
[DefaultValue(5)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate, NullValueHandling = NullValueHandling.Ignore)]
public int Age { get; set; }
As the question's title is phrased more broadly (than just handling a single default value for one single missing JSON property), here's a more generic solution for handling missing JSON properties on a global level:
To handle all missing JSON properties in more complex models, a ContractResolver is likely needed. (other means are inconsistent with missing properties or null value handling in general)
The idea is to use DefaultContractResolver.CreateProperty() to "hook" into the creation of JSON properties and specify desired behavior, which will run on all members (also nested ones) relevant during the (de-)serialization process:
public class MyContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
// do smth with the property (settings) here
return prop;
}
}
Here is an example for how this can be used to default-initialize all collection types to empty collections instead of null, when they are missing (what initially brought me to this question):
public class EmptyCollectionJsonResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
// default initialize collections to empty instead of null
if (IsCollection(prop.PropertyType))
{
prop.DefaultValue = Activator.CreateInstance(prop.PropertyType); // creates empty collection
prop.DefaultValueHandling = DefaultValueHandling.Populate; // force population on null / missing parsed members
}
return prop;
}
// helper function to determine if a type is a collection
private static readonly Type TColl = typeof(ICollection<>);
private static bool IsCollection(Type t)
{
return t.IsGenericType
&& TColl.IsAssignableFrom(t.GetGenericTypeDefinition()) || t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == TColl);
}
}

How can I "un-JsonIgnore" an attribute in a derived class?

I am using Newtonsoft's JsonSerializer to serialise some classes.
As I wanted to omit one field of my class in the serialisation process, I declared it as follow:
[JsonIgnore]
public int ParentId { get; set; }
This worked, but I am now facing a new problem : In a derived class, I would like this field to appear (and do so only in this specific derived class).
I have been looking through the documentation and on the Internet for a way to override this setting in child classes (I guess I need something like [JsonStopIgnore], but I couldn't find anything close).
Is there any way for me to force JsonSerializer to pick up again this attribute ?
Is it possible to explicitly mark an attribute as [JsonIgnore], but only in base class ?
The only way to "override" the behavior of the [JsonIgnore] attribute is to use a contract resolver, as #Yuval Itzchakov nicely explained in his answer.
However, there is another possible solution that might work for you: instead of using a [JsonIgnore] attribute, you could implement a ShouldSerializeParentId() method in your classes to control whether the ParentId property gets serialized. In the base class, make this method return false; then, override the method in the derived class to return true. (This feature is known as conditional property serialization in Json.Net.)
public class Base
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual bool ShouldSerializeParentId()
{
return false;
}
}
public class Derived : Base
{
public override bool ShouldSerializeParentId()
{
return true;
}
}
Fiddle: https://dotnetfiddle.net/65sCSz
You can do this by creating a custom DefaultContractResolver and overriding its CreateProperty method.
For example, given a Foo base and a derived Bar:
public class Foo
{
[JsonIgnore]
public string Name { get; set; }
public int Age { get; set; }
}
public class Bar : Foo
{ }
You can create the following contract resolver:
public class MyTypeContractResolver<T> : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member,
MemberSerialization
memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.Ignored = false;
property.ShouldSerialize = propInstance => property.DeclaringType != typeof (T);
return property;
}
}
This will set all properties to Ignored = false, and then analyze them by the given predicate:
propInstance => property.DeclaringType != typeof (T);
Which in our case means "you should serialize only if they are not of type Foo" (since Foo is the DeclaryingType).
And then when you want to deserialize, you pass an instance of the contract resolver to JsonSerializerSettings:
var bar = new Bar();
var result = JsonConvert.SerializeObject(bar,
new JsonSerializerSettings {ContractResolver = new MyTypeContractResolver<Bar>()});
I solved the same problem by using the new keyword on the property of the derived class.
public class Foo
{
[JsonIgnore]
public int ParentId { get; set; }
}
public class Bar: Foo
{
[JsonProperty("ParentId")]
public new int ParentId { get; set; }
}
You can probably simply overwrite ParentId in the derived class.
public new int ParentId
{
get { return base.ParentId; }
set { base.ParentId = value; }
}
I solved the same problem with a ghost property :
public class Foo
{
[JsonIgnore]
public int ParentId { get; set; }
[NotMapped]
public int FooParent { get; set; }
}
When I want to show this property quite always hidden, I populate it, other times it is null :
Foos.ForEach(x => x.FooParent = ParentId);

Making a property deserialize but not serialize with json.net

We have some configuration files which were generated by serializing C# objects with Json.net.
We'd like to migrate one property of the serialised class away from being a simple enum property into a class property.
One easy way to do this, would be to leave the old enum property on the class, and arrange for Json.net to read this property when we load the config, but not to save it again when we next serialize the object. We'll deal with generating the new class from the old enum separately.
Is there any simple way to mark (e.g. with attributes) a property of a C# object, so that Json.net will ignore it ONLY when serializing, but attend to it when deserializing?
There are actually several fairly simple approaches you can use to achieve the result you want.
Let's assume, for example, that you have your classes currently defined like this:
class Config
{
public Fizz ObsoleteSetting { get; set; }
public Bang ReplacementSetting { get; set; }
}
enum Fizz { Alpha, Beta, Gamma }
class Bang
{
public string Value { get; set; }
}
And you want to do this:
string json = #"{ ""ObsoleteSetting"" : ""Gamma"" }";
// deserialize
Config config = JsonConvert.DeserializeObject<Config>(json);
// migrate
config.ReplacementSetting =
new Bang { Value = config.ObsoleteSetting.ToString() };
// serialize
json = JsonConvert.SerializeObject(config);
Console.WriteLine(json);
To get this:
{"ReplacementSetting":{"Value":"Gamma"}}
Approach 1: Add a ShouldSerialize method
Json.NET has the ability to conditionally serialize properties by looking for corresponding ShouldSerialize methods in the class.
To use this feature, add a boolean ShouldSerializeBlah() method to your class where Blah is replaced with the name of the property that you do not want to serialize. Make the implementation of this method always return false.
class Config
{
public Fizz ObsoleteSetting { get; set; }
public Bang ReplacementSetting { get; set; }
public bool ShouldSerializeObsoleteSetting()
{
return false;
}
}
Note: if you like this approach but you don't want to muddy up the public interface of your class by introducing a ShouldSerialize method, you can use an IContractResolver to do the same thing programmatically. See Conditional Property Serialization in the documentation.
Approach 2: Manipulate the JSON with JObjects
Instead of using JsonConvert.SerializeObject to do the serialization, load the config object into a JObject, then simply remove the unwanted property from the JSON before writing it out. It's just a couple of extra lines of code.
JObject jo = JObject.FromObject(config);
// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();
json = jo.ToString();
Approach 3: Clever (ab)use of attributes
Apply a [JsonIgnore] attribute to the property that you do not want to be serialized.
Add an alternate, private property setter to the class with the same type as the original property. Make the implementation of that property set the original property.
Apply a [JsonProperty] attribute to the alternate setter, giving it the same JSON name as the original property.
Here is the revised Config class:
class Config
{
[JsonIgnore]
public Fizz ObsoleteSetting { get; set; }
[JsonProperty("ObsoleteSetting")]
private Fizz ObsoleteSettingAlternateSetter
{
// get is intentionally omitted here
set { ObsoleteSetting = value; }
}
public Bang ReplacementSetting { get; set; }
}
For any situation where it's acceptable to have your deserialization-only property be marked internal, there's a remarkably simple solution that doesn't depend on attributes at all. Simply mark the property as internal get, but public set:
public class JsonTest {
public string SomeProperty { internal get; set; }
}
This results in correct deserialization using default settings/resolvers/etc., but the property is stripped from serialized output.
I like sticking with attributes on this one, here is the method I use when needing to deserialize a property but not serialize it or vice versa.
STEP 1 - Create the custom attribute
public class JsonIgnoreSerializationAttribute : Attribute { }
STEP 2 - Create a custom Contract Reslover
class JsonPropertiesResolver : DefaultContractResolver
{
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
//Return properties that do NOT have the JsonIgnoreSerializationAttribute
return objectType.GetProperties()
.Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute)))
.ToList<MemberInfo>();
}
}
STEP 3 - Add attribute where serialization is not needed but deserialization is
[JsonIgnoreSerialization]
public string Prop1 { get; set; } //Will be skipped when serialized
[JsonIgnoreSerialization]
public string Prop2 { get; set; } //Also will be skipped when serialized
public string Prop3 { get; set; } //Will not be skipped when serialized
STEP 4 - Use it
var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });
Hope this helps! Also it's worth noting that this will also ignore the properties when Deserialization happens, when I am derserializing I just use the converter in the conventional way.
JsonConvert.DeserializeObject<MyType>(myString);
Use setter property:
[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { _ignoreOnSerializing = value; } }
[JsonIgnore]
private string _ignoreOnSerializing;
[JsonIgnore]
public string IgnoreOnSerializing
{
get { return this._ignoreOnSerializing; }
set { this._ignoreOnSerializing = value; }
}
Hope this help.
After i spent a quite long time searching how to flag a class property to be De-Serializable and NOT Serializable i found that there's no such thing to do that at all; so i came up with a solution that combines two different libraries or serialization techniques (System.Runtime.Serialization.Json & Newtonsoft.Json) and it worked for me like the following:
flag all your class and sub-classes as "DataContract".
flag all the properties of your class and sub-classes as "DataMember".
flag all the properties of your class and sub-classes as "JsonProperty" except those you want them not to be serialized.
now flag the properties the you do NOT want it to be serialized as "JsonIgnore".
then Serialize using "Newtonsoft.Json.JsonConvert.SerializeObject" and De-Serialize using "System.Runtime.Serialization.Json.DataContractJsonSerializer".
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
namespace LUM_Win.model
{
[DataContract]
public class User
{
public User() { }
public User(String JSONObject)
{
MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(JSONObject));
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(User));
User user = (User)dataContractJsonSerializer.ReadObject(stream);
this.ID = user.ID;
this.Country = user.Country;
this.FirstName = user.FirstName;
this.LastName = user.LastName;
this.Nickname = user.Nickname;
this.PhoneNumber = user.PhoneNumber;
this.DisplayPicture = user.DisplayPicture;
this.IsRegistred = user.IsRegistred;
this.IsConfirmed = user.IsConfirmed;
this.VerificationCode = user.VerificationCode;
this.Meetings = user.Meetings;
}
[DataMember(Name = "_id")]
[JsonProperty(PropertyName = "_id")]
public String ID { get; set; }
[DataMember(Name = "country")]
[JsonProperty(PropertyName = "country")]
public String Country { get; set; }
[DataMember(Name = "firstname")]
[JsonProperty(PropertyName = "firstname")]
public String FirstName { get; set; }
[DataMember(Name = "lastname")]
[JsonProperty(PropertyName = "lastname")]
public String LastName { get; set; }
[DataMember(Name = "nickname")]
[JsonProperty(PropertyName = "nickname")]
public String Nickname { get; set; }
[DataMember(Name = "number")]
[JsonProperty(PropertyName = "number")]
public String PhoneNumber { get; set; }
[DataMember(Name = "thumbnail")]
[JsonProperty(PropertyName = "thumbnail")]
public String DisplayPicture { get; set; }
[DataMember(Name = "registered")]
[JsonProperty(PropertyName = "registered")]
public bool IsRegistred { get; set; }
[DataMember(Name = "confirmed")]
[JsonProperty(PropertyName = "confirmed")]
public bool IsConfirmed { get; set; }
[JsonIgnore]
[DataMember(Name = "verification_code")]
public String VerificationCode { get; set; }
[JsonIgnore]
[DataMember(Name = "meeting_ids")]
public List<Meeting> Meetings { get; set; }
public String toJSONString()
{
return JsonConvert.SerializeObject(this, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
}
}
}
Hope that helps ...
Depending on where in the application this takes place and if it's just one property, one manual way you can do this is by setting the property value to null and then on the model you can specify that the property be ignored if the value is null:
[JsonProperty(NullValueHandling = NullValue.Ignore)]
public string MyProperty { get; set; }
If you are working on an ASP.NET Core web app, you can globally set this for all properties in all models by setting this in your Startup.cs file:
public void ConfigureServices(IServiceCollection services) {
// other configuration here
services.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}
with reference to #ThoHo's solution, using the setter is actually all that is needed, with no additional tags.
For me I previously had a single reference Id, that I wanted to load and add to the new collection of reference Ids. By changing the definition of the reference Id to only contain a setter method, which added the value to the new collection. Json can't write the value back if the Property doesn't have a get; method.
// Old property that I want to read from Json, but never write again. No getter.
public Guid RefId { set { RefIds.Add(value); } }
// New property that will be in use from now on. Both setter and getter.
public ICollection<Guid> RefIds { get; set; }
This class is now backwards compatible with the previous version and only saves the RefIds for the new versions.
To build upon Tho Ho's answer, this can also be used for fields.
[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { IgnoreOnSerializing = value; } }
[JsonIgnore]
public string IgnoreOnSerializing;
If you use JsonConvert,IgnoreDataMemberAttribute is ok.My standard library not refrence Newton.Json,and I use [IgnoreDataMember] to control object serialize.
From Newton.net help document.
Is there any simple way to mark (e.g. with attributes) a property of a C# object, so that Json.net will ignore it ONLY when serializing, but attend to it when deserializing?
The easiest way I've found as of this writing is to include this logic in your IContractResolver.
Sample code from above link copied here for posterity:
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public bool ShouldSerializeManager()
{
// don't serialize the Manager property if an employee is their own manager
return (Manager != this);
}
}
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
{
property.ShouldSerialize =
instance =>
{
Employee e = (Employee)instance;
return e.Manager != e;
};
}
return property;
}
}
All of the answers are good but this approach seemed like the cleanest way. I actually implemented this by looking for an attribute on the property for SkipSerialize and SkipDeserialize so you can just mark up any class you control. Great question!
Jraco11's answer is very neat. In case, if you want to use the same IContractResolver both for serialization and deserialization, then you can use the following:
public class JsonPropertiesResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (member.IsDefined(typeof(JsonIgnoreSerializationAttribute)))
{
property.ShouldSerialize = instance => false;
}
return property;
}
}
thats will do the trick, create a property with set only
example 1:
https://dotnetfiddle.net/IxMXcG
[JsonProperty("disabled-protections")]
public JArray DisabledProtections { set => IsPartialResult = (value != null && value.HasValues); }
public bool IsPartialResult { get; private set; }
example 2:
private JArray _disabledProtections;
[JsonProperty("disabled-protections")]
public JArray DisabledProtections { set => _disabledProtections = value; }
public bool IsPartialResult => _disabledProtections != null && _disabledProtections.HasValues;
Use [JsonIgnore] attribute in the public property of the model class.

Categories