Conditional serialization/deserialization of object properties with json.net [duplicate] - c#

This question already has answers here:
Serialize Property, but Do Not Deserialize Property in Json.Net
(2 answers)
Closed 6 years ago.
I have a scenario with a class defined as below:
class MyObject
{
public DataDictionary MyObjectData { get; set; }
public bool ShouldSerializeMyObjectData() { return true; }
public bool ShouldDeserializeMyObjectData() { return false; }
}
When I attempt to serialize/deserialize that class with JSON.net, it takes the ShouldSerialize into account, but not the ShouldDeserialize.
According to the documentation, both should work the same way I guess. Is there something particular I should know? More generally, how should I deal with scenarios where I want to serialize a property but not deserialize it?
I'm using Json.NET 8.0 if that matters.
Thanks for your help.

The short answer to your question is, automatically checking for ShouldDeserialize{PropertyName}() is not currently implemented even though ShouldSerialize{PropertyName}() is. A longer answer and workaround follow.
The class JsonProperty is used internally by Json.NET to define a contract for how to map a JSON property to a .NET member or constructor parameter. It has two predicate properties, ShouldSerialize and ShouldDeserialize that, when non-null, prevent a property from being serialized and deserialized, respectively. Initializing each JsonProperty is the job of the ContractResolver. For each property {PropertyName}, Json.NET's default contract resolver automatically checks for the presence of a public bool ShouldSerialize{PropertyName}() method. If such a method exists, it adds a call to it in the ShouldSerialize predicate, thereby suppressing serialization when the method returns false. This was implemented because controlling property serialization via a method ShouldSerialize{PropertyName}() is a standard pattern supported by, e.g., XmlSerializer. For more background see the relevant Json.NET release notes.
For example, in the following class, serialization of MyObjectData will be suppressed unless MyObjectData.Count > 0:
class MyObject
{
public DataDictionary MyObjectData { get; set; }
public bool ShouldSerializeMyObjectData() { return MyObjectData != null && MyObjectData.Count > 0; }
}
JsonProperty.ShouldDeserialize, however, it is never set by the default contract resolver. This may be due to the fact that there is no standard pattern for deserialization equivalent to ShouldSerialize{PropertyName}() and so Newtonsoft never had any requests to implement such a pattern. Nevertheless, as you have noticed, infrastructure to support such a pattern exists, and so applications can create custom contract resolvers that do just that. In fact, Json.NET has an example of such a contract resolver in its own test suite:
public class ShouldDeserializeContractResolver : DefaultContractResolver
{
public static new readonly ShouldDeserializeContractResolver Instance = new ShouldDeserializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
MethodInfo shouldDeserializeMethodInfo = member.DeclaringType.GetMethod("ShouldDeserialize" + member.Name);
if (shouldDeserializeMethodInfo != null)
{
property.ShouldDeserialize = o => { return (bool)shouldDeserializeMethodInfo.Invoke(o, null); };
}
return property;
}
}
public class ShouldDeserializeTestClass
{
[JsonExtensionData]
public IDictionary<string, JToken> ExtensionData { get; set; }
public bool HasName { get; set; }
public string Name { get; set; }
public bool ShouldDeserializeName()
{
return HasName;
}
}
If you want to conditionally suppress deserialization of properties even when present in the JSON, you may use this contract resolver.
Notes:
If you do use a custom contract resolver, you should cache and reuse it for best performance.
JsonProperty.ShouldDeserialize is called before the property value is deserialized. If it returns true, the property is skipped, with no ability to examine the contents of the property. Thus it cannot be used to implement custom filtering based on that value.
A JSON object is defined by the JSON standard as an unordered set of name/value pairs. Thus a ShouldDeserialize method that assumes that other properties have already been read in may be brittle.
Instead, if you want to skip deserialization of one property based on the value of another, consider using an [OnDeserialized] callback and clearing the unwanted value there, after all properties have been deserialized.

Related

Ignore [DataMember(Name = "xx")] at json serialisation [duplicate]

We are running into a situation on an MVC3 project with both the Microsoft JSON serializers and JSON.NET.
Everybody knows DateTime's are basically broken in Microsoft's serializers, so we switched to JSON.NET to avoid this issue. That works great, except that some of the classes we are trying to serialize are POCOs with DataContract/DataMember attributes. They are defined in an assembly that is referenced in multiple places. Additionally, they have some other display properties that are not marked as DataMembers for efficiency. For instance, a Customer
[DataContract]
public class Customer
{
[DataMember]
public string FirstName { get; set;}
[DataMember]
public string LastName { get; set;}
public string FullName
{
get
{ return FirstName + " " + LastName; }
}
}
When this customer is passed over WCF the client side can reference that assembly and use the FullName just fine, but when serialized with JSON.NET it sees that FullName isn't a [DataMember] and doesn't serialize it. Is there an option to pass to JSON.NET to tell it to ignore the fact that a class has [DataContract] attribute applied?
Note:
Using the JavaScriptSerializer in .NET works fine for the FullName property, but DateTimes are broken. I need JSON.NET to ignore the fact that this class has DataContract/DataMember attributes and just do standard public field serialization like it would if they weren't there.
Simply use Json.Net's OptOut attribute. It will take precedence over DataContract.
[DataContract]
[JsonObject(MemberSerialization.OptOut)]
As Amry said you can uses your own IContractResolver.
Unfortunately the solution provided by Amry didn't work for me, below is the solution that I managed to get working:
public class AllPropertiesResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
//property.HasMemberAttribute = true;
property.Ignored = false;
//property.ShouldSerialize = instance =>
//{
// return true;
//};
return property;
}
}
There are a few lines commented, these wern't required to make my solution work, but you never know!
This has the same usage as Amry's solution:
var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings {
ContractResolver = new AllPropertiesResolver()
});
Hope this helps!
I was having a problem almost related to what you're having, and managed to find a solution by going through Json.NET's codes. So it may not be the best solution, but it works for me.
To do this, you need to implement your own IContractResolver. An over-simplified implementation of that to include all parameters and ignores all attributes (not just DataContract but other built-in Json.NET's rules as well, so whatever options you set that should originally affect the members selection is now being overidden by this code):
class AllPropertiesResolver : DefaultContractResolver
{
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
return objectType.GetProperties()
.Where(p => p.GetIndexParameters().Length == 0)
.Cast<MemberInfo>()
.ToList();
}
}
And here comes the code usage example:
var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings {
ContractResolver = new AllPropertiesResolver()
});
In accordance to the Json.NET documentation [DataMember] attributes are ignored if the properties are also annotated with Json.NET specific attributes (such as [JsonProperty]).See the Serialization Attributes documentation for details:
Json.NET attributes take presidence over standard .NET serialization attributes, e.g. if both JsonPropertyAttribute and DataMemberAttribute are present on a property and both customize the name, the name from JsonPropertyAttribute will be used.
The documentation only covers the name property, but for my experience the [JsonProperty] attribute also fully shadows settings done by the [DataMember] attribute. So, if it is feasible for your case, also add Json.NET attributes to the properties for which the [DataMember] annotation should be ignored.
If you want to ignore the presence of DataContractAttribute for all types without having to add additional attributes, then a custom contract resolver is the correct solution. However, as of Json.NET 9.0.1 Amry's resolver no longer works. Doolali's resolver works but it has the additional side effect of serializing all public properties including those marked with [JsonIgnore]. If you require a contract resolver that only ignores the presence of DataContractAttribute but otherwise behaves like the default contract resolver, the following can be used:
public class IgnoreDataContractContractResolver : DefaultContractResolver
{
static MemberSerialization RemoveDataContractAttributeMemberSerialization(Type type, MemberSerialization memberSerialization)
{
if (memberSerialization == MemberSerialization.OptIn)
{
type = Nullable.GetUnderlyingType(type) ?? type;
// Json.NET interprets DataContractAttribute as inherited despite the fact it is marked with Inherited = false
// https://json.codeplex.com/discussions/357850
// https://stackoverflow.com/questions/8555089/datacontract-and-inheritance
// https://github.com/JamesNK/Newtonsoft.Json/issues/603
// Thus we need to manually climb the type hierarchy to see if one is present.
var dataContractAttribute = type.BaseTypesAndSelf().Select(t => t.GetCustomAttribute<DataContractAttribute>()).FirstOrDefault(a => a != null);
var jsonObjectAttribute = type.GetCustomAttribute<JsonObjectAttribute>();
if (dataContractAttribute != null && jsonObjectAttribute == null)
memberSerialization = MemberSerialization.OptOut;
}
return memberSerialization;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, RemoveDataContractAttributeMemberSerialization(type, memberSerialization));
return properties;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
contract.MemberSerialization = RemoveDataContractAttributeMemberSerialization(objectType, contract.MemberSerialization);
return contract;
}
}
public static class TypeExtensions
{
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
}
You may want to cache the contract resolver for best performance.
Have you tried this?
IgnoreDataMemberAttribute

Modify/Add property via custom IContractResolver

I have a class:
public class MyClass
{
public MyEnum Foo{ get; set; }
}
During serialization i'd like to change the output from
{
"Foo": 1
}
to
{
"Foo": "EnumName"
}
I've tried creating an IValueProvider but hit dead ends every way I go. (My scenario is a bit more complicated than stated; I need to find a way to do this entirely within the IContractResolver.)
You could create a custom ContractResolver inheriting from DefaultContractResolver that automatically applies StringEnumConverter to every contract for an enum or nullable enum:
public class StringEnumContractResolver : DefaultContractResolver
{
readonly StringEnumConverter converter;
public StringEnumContractResolver() : this(true, false) { }
public StringEnumContractResolver(bool allowIntegerValue, bool camelCaseText)
{
this.converter = new StringEnumConverter { AllowIntegerValues = allowIntegerValue, CamelCaseText = camelCaseText };
}
protected override JsonPrimitiveContract CreatePrimitiveContract(Type objectType)
{
var contract = base.CreatePrimitiveContract(objectType);
var type = Nullable.GetUnderlyingType(contract.UnderlyingType) ?? contract.UnderlyingType;
if (type.IsEnum && contract.Converter == null)
contract.Converter = converter;
return contract;
}
}
Notes:
If the enum type already has a JsonConverter applied, that is kept in preference to the default StringEnumConverter.
Adding the converter to the JsonPrimitiveContract for the enum itself, rather than to every JsonProperty for members that return the enum, ensures that the converter is applied to enums in collections and dictionaries.
The IValueProvider merely provides methods to get and set values and thus is less convenient to this purpose than the converter. You would need to perform a nested serialization and deserialization of the enum value as a JSON string inside it, but it isn't designed for this and so doesn't have access to the JSON reader, writer, or serializer. In addition, there is no value provider for dictionary values or collection items that are enums.
You may want to cache the contract resolver for best performance as explained here.
Sample .Net fiddle.

How to prevent Json.NET from using use default values for missing constructor parameters while still using default values for properties? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
Is there a way to tell JSON.net that when it attempts to deserialize using a constructor (if there is no default constructor), that it should NOT assign default value to constructor parameters and that it should only call a constructor if every constructor parameter is represented in the JSON string? This same serializer SHOULD use default values when calling property/field setters, the rule is only scoped to constructors. None of the enum values here seem to be appropriate: http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_DefaultValueHandling.htm
The solution should NOT rely on applying any attributes to the types being deserialized.
for example, the json string "{}" will deserialize to an object of type Dog by setting the Dog's age to 0 (the default value for an int). I'd like to a generalized, not-attribute-based solution to prevent this from happening. In this case, {"age":4} would work because age is specified in the JSON string and corresponds to the constructor parameter.
public class Dog
{
public Dog(int age)
{
this.Age = age;
}
public int Age { get; }
}
However, if Dog is specified as such, then "{}" should deserialize to a Dog with Age == 0, because the Dog is not being created using a constructor.
public class Dog
{
public int Age { get; set; }
}
As to "why would you want to do this"... Objects with constructors are typically qualitatively different than POCOs as it relates to their properties. Using a constructor to store property values instead of settable properties on a POCO typically means that you want to validate/constrain the property values. So it's reasonable not to allow deserialization with default values in the presence of constructor(s).
When Json.NET encounters an object without a parameterless constructor but with a parameterized constructor, it will call that constructor to create the object, matching the JSON property names to the constructor arguments by name using reflection via a case-insensitive best match algorithm. I.e. a property whose name also appears in the constructor will be set via the constructor call, not the set method (even if there is one).
Thus, you can mark a constructor argument as required by marking the equivalent property as required:
public class Dog
{
public Dog(int age)
{
this.Age = age;
}
[JsonProperty(Required = Required.Always)]
public int Age { get; }
}
Now JsonConvert.DeserializeObject<Dog>(jsonString) will throw when the "age" property is missing.
Since this is something you always want, you can create a custom contract resolver inheriting from DefaultContractResolver or CamelCasePropertyNamesContractResolver that marks properties passed to the constructor as required automatically, without the need for attributes:
public class ConstructorParametersRequiredContractResolver : DefaultContractResolver
{
protected override JsonProperty CreatePropertyFromConstructorParameter(JsonProperty matchingMemberProperty, ParameterInfo parameterInfo)
{
var property = base.CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo);
if (property != null && matchingMemberProperty != null)
{
var required = matchingMemberProperty.Required;
// If the member is already explicitly marked with some Required attribute, don't override it.
// In Json.NET 12.0.2 and later you can use matchingMemberProperty.IsRequiredSpecified to check to see if Required is explicitly specified.
// if (!matchingMemberProperty.IsRequiredSpecified)
if (required == Required.Default)
{
if (matchingMemberProperty.PropertyType != null && (matchingMemberProperty.PropertyType.IsValueType && Nullable.GetUnderlyingType(matchingMemberProperty.PropertyType) == null))
{
required = Required.Always;
}
else
{
required = Required.AllowNull;
}
// It turns out to be necessary to mark the original matchingMemberProperty as required.
property.Required = matchingMemberProperty.Required = required;
}
}
return property;
}
}
Then construct an instance of the resolver:
static IContractResolver requiredResolver = new ConstructorParametersRequiredContractResolver();
And use it as follows:
var settings = new JsonSerializerSettings { ContractResolver = requiredResolver };
JsonConvert.DeserializeObject<T>(jsonString, settings)
Now deserialization will throw if the "age" property is missing from the JSON.
Notes:
This only works if there is a corresponding property. There doesn't appear to be a straightforward way to mark a constructor parameter with no corresponding property as required.
Newtonsoft recommends that you cache and reuse the contract resolver for best performance.
Demo fiddle here.

Is it possible to set custom (de)serializers for open generic types in ServiceStack.Text?

I have a type like this:
class Foo<T>
{
public string Text { get; set; }
public T Nested { get; set; }
public static string ToJson(Foo<T> foo) { [...] }
}
ToJson serializes a Foo<Bar> instance to JSON in a way that is impossible to achieve by tweaking JsConfig. Also, ToJson relies on ServiceStack.Text to serialize Nested, which can be an instance of Foo<Baz>.
Unfortunately, the way JsConfig is implemented implies that there will be a JsConfig<T> set of static variables for Foo<Bar> and other for Foo<Baz>. Also, AFAIK, ServiceStack.Text offers no way to configure JSON serialization for open generic types (i.e.: something like JsConfig.Add(typeof(Foo<>), config)). I tried to solve this issue by creating this static constructor for Foo<T>:
static Foo() {
JsConfig<Foo<T>>.RawSerializeFn = ToJson;
}
This doesn't work all the time. It depends on the order the static constructors are invoked by the runtime. Apparently, ServiceStack.Text caches serializers functions and sometimes is doing it before the static constructor is called depending on the order operations are invoked in the API, so:
var outer = new Foo<Baz> { Text = "text" };
outer.ToJson(); // OK, because Nested is null
var inner = new Foo<Bar>();
inner.ToJson(); // OK, because JsConfig<Foo<Bar>>.RawSerializeFn is Foo<T>.ToJson
outer.Nested = inner;
outer.ToJson(); // NOT OK, because SS.Text uses the default serializer for Foo<T>, not Foo<T>.ToJson
I can't set all the serializers in JsConfig<Foo<T>> beforehand because T can be virtually any type, even other generic types.
Is it possible to define custom serialization routines for open generic types (that can be nested) in ServiceStack.Text?
I solved for this in my own way with a wrapper and a custom deserializer. I created a base type for all of my abstract types. That base type tells the system which type it is:
public class SetSettingItemBase
{
public string Key { get; set; }
public string ValueType { get; set; }
}
So the base is essentially the metadata -- the setting key + the value type. The object DTO, then, simply extends it by adding the actual value:
public class SetSettingItem : SetSettingItemBase
{
public object Value { get; set; }
}
Note that it's just an object. This is the DTO, not my actual object. I can cast it later, or convert it to a real/generic type after serialization.
My custom serialization then is:
JsConfig<SetSettingItem>.RawDeserializeFn = str =>
{
var baseSettings = str.FromJson<SetSettingItemBase>();
var ret = baseSettings.MapTo<SetSettingItem>();
if(true) // actual condition removed here... unimportant to the example
{
var dataType = Constants.KnownSettingsTypes[baseSettings.ValueType];
var method = typeof(JsonExtensions).GetMethod("JsonTo").MakeGenericMethod(dataType);
var key = "Value";
var parsed = JsonObject.Parse(str);
if(parsed.Object(key) == null)
key = "value";
ret.Value = method.Invoke(null, new object[] { parsed, key });
}
return ret;
};
This method first deserializes to the simple base. So the Value passed in from the DTO is ignored when deserializing baseSettings. I then call MapTo to prepare the actual SetSettingItem DTO. MapTo is just a wrapper around AutoMapper. You could just as easily use SS's built in mapper here.
For security, I have a set list of types that I allow as settings. Example:
KnownSettingsTypes.Add("string", typeof(string));
KnownSettingsTypes.Add("int", typeof(int));
KnownSettingsTypes.Add("nullableint", typeof(int?));
KnownSettingsTypes.Add("nullablepercentage", typeof(double?));
KnownSettingsTypes.Add("feegrid", typeof(FeeGrid));
After that, I use reflection to get the JsonTo method, passing in the generic type parameter dynamically from the KnownSettingsTypes dictionary.
And then finishing it all up, I parse the object using the generic JsonObject.Parse method and then looking for the Value or value (depending on case sensitivity) and explicitly convert that JsonObject using the dynamic method I created earlier.
The end result is I can pass in settings of all different types here as a part of my DTOs.
This served my purposes for the time being, but looking at the example I could see it improving in two ways:
After parsing, I could convert my SetSettingItem to a SettingItem<T> so I could use it as a strongly-typed object in my code. Remember, this example is just for the DTOs to get it across the wire.
Instead of requiring the person to pass in the type for me to check against, I could check against the setting Key to know which type it is supposed to be and parse accordingly. In my example, even if I check against the master list of settings and their types, I'd still probably require them to pass in the type just as a precaution and throw an exception if they didn't match.

How to deserialize class without calling a constructor?

I'm using Json.NET in my WCF data service.
Here's my class (simplified):
[DataContract]
public class Component
{
public Component()
{
// I'm doing some magic here.
}
}
How can I deserialize that class without invoking a constructor using JsonConvert.DeserializeObject?
Sorry if not clear, feel free to ask questions.
A constructor is always invoked. I usually have two constructors. One for serialization (the default constructor) and one for all "regular" code:
[DataContract]
public class Component
{
// for JSON.NET
protected Component()
{
}
public Component(allMandatoryFieldsHere)
{
// I'm doing some magic here.
}
}
In that way I can also make sure that the dev specify all information which are required.
However, I do not really recommend that you use anything but DTO's when transfering information since it's otherwise possible to circumvent the encapsulation of your objects (anyone could initialize any field with any value). Well. If you use anything but anemic models.
Using FormatterServices.GetSafeUninitializedObject is imho therefore an ugly workaround, since no one can tell that you create all objects in an unintialized way. Constructor initialization is there for a reason. It's better that the classes can tell that it's OK to not call the real constructor by providing a "serialization" constructor as I suggested.
You could create a class that inherits from CustomCreationConverter
and use FormatterServices.GetSafeUninitializedObject to create your
object. It skips calling the constructor.
More about CustomCreationConverter here.
Placing
[JsonObject(MemberSerialization.Fields)] on a class will make Json.NET use
FormatterServices.GetSafeUninitializedObject by default (although
Fields mode will also serialize public/private fields rather than
public properties which you may not want).
Move the logic you don't want run outside of the default constructor.
Others already mentioned the second constructor, but using 2 attributes: [JsonConstructor] and [Obsolete] you can do much better than leaving it up to humans to remember which one to call.
public ChatMessage()
{
MessageID = ApplicationState.GetNextChatMessageID(); // An expensive call that uses up an otherwise free ID from a limited set and does disk access in the process.
}
[JsonConstructor] // This forces JsonSerializer to call it instead of the default.
[Obsolete("Call the default constructor. This is only for JSONserializer", true)] // To make sure that calling this from your code directly will generate a compiler error. JSONserializer can still call it because it does it via reflection.
public ChatMessage(bool DO_NOT_CALL_THIS)
{
}
[JsonConstructor] forces JsonSerializer to call it instead of the default.
[Obsolete("...", true)] Makes sure that calling this from your code directly will generate a compiler error. JSONserializer can still call it because it does it via reflection.
The best option to avoid constructor calls on deserialization is to create special contract resolver that overrides creator function for all classes without constructor marked with JsonConstructor attribute. This way you still can force JSON.NET to call constructor if you really need it, but all other classes will be created much like in standard DataContract serializers in .NET. Here is the code:
/// <summary>
/// Special contract resolver to create objects bypassing constructor call.
/// </summary>
public class NoConstructorCreationContractResolver : DefaultContractResolver
{
/// <summary>
/// Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> for the given type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// A <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> for the given type.
/// </returns>
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
// prepare contract using default resolver
var objectContract = base.CreateObjectContract(objectType);
// if type has constructor marked with JsonConstructor attribute or can't be instantiated, return default contract
if (objectContract.OverrideCreator != null || objectContract.CreatedType.IsInterface || objectContract.CreatedType.IsAbstract)
return objectContract;
// prepare function to check that specified constructor parameter corresponds to non writable property on a type
Func<JsonProperty, bool> isParameterForNonWritableProperty =
parameter =>
{
var propertyForParameter =
objectContract.Properties.FirstOrDefault(property => property.PropertyName == parameter.PropertyName);
if (propertyForParameter == null)
return false;
return !propertyForParameter.Writable;
};
// if type has parameterized constructor and any of constructor parameters corresponds to non writable property, return default contract
// this is needed to handle special cases for types that can be initialized only via constructor, i.e. Tuple<>
if (objectContract.CreatorParameters.Any(parameter => isParameterForNonWritableProperty(parameter)))
return objectContract;
// override default creation method to create object without constructor call
objectContract.DefaultCreatorNonPublic = false;
objectContract.DefaultCreator = () => FormatterServices.GetSafeUninitializedObject(objectContract.CreatedType);
return objectContract;
}
}
All you need is simply set this contract resolver in serializer settings before deserialization.
Update on readonly fields and get-only properties
A readonly field can be deserialized if it has a JsonProperty attribute applied.
A get-only property can also be deserialized if it's backing field has a JsonProperty attribute applied.
Since C# 7.3 it's possible to apply attributes to compiler-generated backing fields of auto-properties.
Leveraging this we could do something like this:
public class SampleClass
{
[JsonProperty("Field1")]
private readonly string _field1 = "Field1Value";
[JsonProperty("Property1")]
private string _property1BackingField = "Property1Value";
[JsonIgnore]
public string Property1 => _property1BackingField;
[field: JsonProperty("Property2")]
[JsonIgnore]
public string Property2 { get; } = "Property2Value";
}
const string json = "{\"Field1\":\"NEW-Field1Value\",\"Property1\":\"NEW-Property1Value\",\"Property2\":\"NEW-Property2Value\"}";
var serializerSettings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
// this settings is needed to work with compiler-generated backing fields
SerializeCompilerGeneratedMembers = true
}
};
var deserializedSample = JsonConvert.DeserializeObject<SampleClass>(json, serializerSettings);
If you need to do this in older C# version or have no control over attributes, you could override CreateProperty() method of DefaultContractResolver with something like this:
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var jsonProperty = base.CreateProperty(member, memberSerialization);
if (jsonProperty.Writable == false)
{
// this uses compiler implementation details and may not work for all cases
// better to use smarter approach like in BackingFieldResolver from Mono.Reflection library
var fieldInfo = jsonProperty.DeclaringType.GetField($"<{jsonProperty.PropertyName}>k__BackingField",
BindingFlags.NonPublic | BindingFlags.Instance);
if (fieldInfo != null)
{
jsonProperty.ValueProvider = new ReflectionValueProvider(fieldInfo);
jsonProperty.Writable = true;
}
}
return jsonProperty;
}

Categories

Resources