Making a property deserialize but not serialize with json.net
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; }
}
Serialize Property, but Do Not Deserialize Property in Json.Net
Simplest method would be to mark the real property as [JsonIgnore]
and create a get-only proxy property:
/// <summary>
/// Gets or sets the country code, and province or state code delimited by a vertical pipe: <c>US|MI</c>
/// </summary>
[JsonIgnore]
public string CountryProvinceState
{
get
{
return string.Format("{0}|{1}", this.CountryCode, this.ProvinceState);
}
set
{
if (!string.IsNullOrWhiteSpace(value) && value.Contains("|"))
{
string[] valueParts = value.Split('|');
if (valueParts.Length == 2)
{
this.CountryCode = valueParts[0];
this.ProvinceState = valueParts[1];
}
}
}
}
[JsonProperty("countryProvinceState")]
string ReadCountryProvinceState
{
get { return CountryProvinceState; }
}
The proxy property can be private if you desire.
Update
If you have to do this for lots of properties in lots of classes, it might be easier to create your own ContractResolver
that checks for a custom attribute. If found, the attribute would signal that the property is get-only:
[System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple = false)]
public class GetOnlyJsonPropertyAttribute : Attribute
{
}
public class GetOnlyContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property != null && property.Writable)
{
var attributes = property.AttributeProvider.GetAttributes(typeof(GetOnlyJsonPropertyAttribute), true);
if (attributes != null && attributes.Count > 0)
property.Writable = false;
}
return property;
}
}
Then use it like:
[JsonProperty("countryProvinceState")]
[GetOnlyJsonProperty]
public string CountryProvinceState { get; set; }
And then:
var settings = new JsonSerializerSettings { ContractResolver = new GetOnlyContractResolver() };
var address = JsonConvert.DeserializeObject<Address>(jsonString, settings);
Using JsonProperty for Deserializing but NOT serializing
You could use a set-only property that points to the 'good' property.
public class Item
{
public int Id { get; set; }
[JsonProperty("pkID")]
public int BackwardCompatibleId { set => Id = value; }
}
// test
var x = new Item { Id = 88 };
var json = JsonConvert.SerializeObject(x); // {"Id":88}
var clone = JsonConvert.DeserializeObject<Item>("{\"pkId\":99}");
Can I instruct Json.NET to deserialize, but not serialize, specific properties?
You should be able to just use the ShouldSerialize* methods as shown in the question. These only impact serialization, not deserialization.
How to deserialize a property on JSON from one type but serialize to another type with the same name?
Thanks to @Fildor for leading me in the right direction.
The answer was deceptively simple, add a type converter and decorate the new property with the converter.
public class Something {
[JsonConverter(typeof(FeeConverter))]
public Fee Fee { get; set;}
}
public class Fee {
public decimal? Amount { get; set; }
public int Type { get; set; }
}
public class FeeConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Float || reader.TokenType == JsonToken.Integer)
{
var property = JValue.Load(reader);
return new Fee() { Amount = property.Value<decimal>() };
}
return serializer.Deserialize(reader, objectType);
}
public override bool CanWrite => false;
public override bool CanConvert(Type objectType) => false;
}
Json.NET does not deserialize property with custom getter and immutable type
The culprit is this line in your ClassWithCustomProperty
class:
get => value = (value ?? new ImmutableValue(0));
Json.Net's default behavior is to reuse existing objects rather than replace them. So when it is time to deserialize the ImmutableValue
on ClassWithCustomProperty
, the serializer first checks whether there is an existing value. The accessor (above) finds that there isn't, so it creates one with value 0
, which is returned to the serializer. The serializer then attempts to reuse this existing object, but since it is readonly, there is no way to set the value 42
on it. So the value remains zero.
There is an ObjectCreationHandling
setting which can be used to change this behavior. If you set it to Replace
, it will work the way you want. Try like this:
var settings = new JsonSerializerSettings
{
ObjectCreationHandling = ObjectCreationHandling.Replace
};
var obj = JsonConvert.DeserializeObject<ClassWithCustomProperty>(json, settings);
Fiddle: https://dotnetfiddle.net/UVDMsL
Json.NET Deserialize Property Without Deserializing Parent Object Above It
I suggest two approaches that are very explicit and easy to follow for the next developer looking at the code.
Two classes
creating a intermediate dto class that is used for deserialisation and then creating the business logic object from that intermediate object.
var withAttributes = Deserialise<ListingDto>();
var flatObject = new Listing(withAttributes);
One class
You could provide accessors at the top level which dip into the subclasses.
public class Listing
{
public AttributesDto Attributes {get; set}
...
public string Url => Attributes.Url; // Maybe '?.Url'
}
Related Topics
How to Autheticate to Ibm Mq C# with Tls-Certificate
Can You Develop Linux Applications with Xamarin
Why Filesystemwatcher Doesn't Work in Linux Container Watching Windows Volume
.Net Core MAChine Key Alternative for Webfarm
C# Execute a Terminal Command in Linux
Mono High Resolution Timer (On Linux)
Change Current Linux User in a C# Application Running with Mono
Does Mono Support System.Drawing and System.Drawing.Printing
Mono Shared Library Under Linux Location
How to Create a Navigation Menu in Dotnet Application
How to Use Visual Studio Code to Develop Unity3D Projects in Ubuntu
Embedding JavaScript Engine into .Net
Difference Between Equals/Equals and == Operator
Most Elegant Way to Generate Prime Numbers
Show a Form Without Stealing Focus
How to Specify the Exit Code of a Console Application in .Net