JsonConvert.DeserializeObject w/ DynamicObject and TypeCreationConverter - c#

I have a class EntityBase that derives from DynamicObject without an empty default constructor.
// this is not the actual type but a mock to test the behavior with
public class EntityBase : DynamicObject
{
public string EntityName { get; private set; }
private readonly Dictionary<string, object> values = new Dictionary<string, object>();
public EntityBase(string entityName)
{
this.EntityName = entityName;
}
public virtual object this[string fieldname]
{
get
{
if (this.values.ContainsKey(fieldname))
return this.values[fieldname];
return null;
}
set
{
if (this.values.ContainsKey(fieldname))
this.values[fieldname] = value;
else
this.values.Add(fieldname, value);
}
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return this.values.Keys.ToList();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = this[binder.Name];
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
this[binder.Name] = value;
return true;
}
}
the JSON I'd like to deserialize looks like this:
{'Name': 'my first story', 'ToldByUserId': 255 }
EntityBase has neither the Name nor the ToldByUserId property. They should be added to the DynamicObject.
If I let DeserializeObject create the object like this everything works as expected:
var story = JsonConvert.DeserializeObject<EntityBase>(JSON);
but since I don't have an empty default constructor and can't change the class I went for a CustomCreationConverter :
public class StoryCreator : CustomCreationConverter<EntityBase>
{
public override EntityBase Create(Type objectType)
{
return new EntityBase("Story");
}
}
but
var stroy = JsonConvert.DeserializeObject<EntityBase>(JSON, new StoryCreator());
throws
Cannot populate JSON object onto type 'DynamicObjectJson.EntityBase'.
Path 'Name', line 1, position 8.
It seems that the DeserializeObject calls PopulateObject on the object that was created by the CustomCreationConverter. When I try to do this manually the error stays the same
JsonConvert.PopulateObject(JSON, new EntityBase("Story"));
I further assume that PopulateObject does not check if the target type derives from DynamicObject and therefore does not fall back to TrySetMember.
Note that I don't have influence on the EntityBase type definition, it's from an external library and cannot be changed.
Any insights would be highly appreciated!
Edit: added an example: https://dotnetfiddle.net/EGOCFU

You seem to have stumbled on a couple of bugs or limitations in Json.NET's support for deserializing dynamic objects (defined as those for which a JsonDynamicContract is generated):
Support for parameterized constructors is not present. Even if one is marked with [JsonConstructor] it will not get used.
Here the necessary logic to pre-load all the properties seems to be entirely missing from JsonSerializerInternalReader.CreateDynamic(). Compare with JsonSerializerInternalReader.CreateNewObject() which indicates what would be required.
Since the logic looks fairly elaborate this might be a limitation rather than a bug. And actually there is closed issue #47 about this indicating that it's not implemented:
There would be a fair bit of work to add this feature. You are welcome to submit a pull request if you do add it.
Json.NET has no ability to populate a preexisting dynamic object. Unlike for regular objects (those for which a JsonObjectContract is generated), logic for construction and population is contained entirely in the previously-mentioned JsonSerializerInternalReader.CreateDynamic().
I don't see why this couldn't be implemented with a fairly simple code restructuring. You might submit an issue asking for this. If this were implemented, your StoryCreator would work as-is.
In the absence of either #1 or #2, it's possible to create a custom JsonConverter whose logic is modeled roughly on JsonSerializerInternalReader.CreateDynamic() which calls a specified creation method then populates both dynamic and non-dynamic properties, like so:
public class EntityBaseConverter : ParameterizedDynamicObjectConverterBase<EntityBase>
{
public override EntityBase CreateObject(JObject jObj, Type objectType, JsonSerializer serializer, ICollection<string> usedParameters)
{
var entityName = jObj.GetValue("EntityName", StringComparison.OrdinalIgnoreCase);
if (entityName != null)
{
usedParameters.Add(((JProperty)entityName.Parent).Name);
}
var entityNameString = entityName == null ? "" : entityName.ToString();
if (objectType == typeof(EntityBase))
{
return new EntityBase(entityName == null ? "" : entityName.ToString());
}
else
{
return (EntityBase)Activator.CreateInstance(objectType, new object [] { entityNameString });
}
}
}
public abstract class ParameterizedDynamicObjectConverterBase<T> : JsonConverter where T : DynamicObject
{
public override bool CanConvert(Type objectType) { return typeof(T).IsAssignableFrom(objectType); } // Or possibly return objectType == typeof(T);
public abstract T CreateObject(JObject jObj, Type objectType, JsonSerializer serializer, ICollection<string> usedParameters);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Logic adapted from JsonSerializerInternalReader.CreateDynamic()
// https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs#L1751
// By James Newton-King https://github.com/JamesNK
var contract = (JsonDynamicContract)serializer.ContractResolver.ResolveContract(objectType);
if (reader.TokenType == JsonToken.Null)
return null;
var jObj = JObject.Load(reader);
var used = new HashSet<string>();
var obj = CreateObject(jObj, objectType, serializer, used);
foreach (var jProperty in jObj.Properties())
{
var memberName = jProperty.Name;
if (used.Contains(memberName))
continue;
// first attempt to find a settable property, otherwise fall back to a dynamic set without type
JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName);
if (property != null && property.Writable && !property.Ignored)
{
var propertyValue = jProperty.Value.ToObject(property.PropertyType, serializer);
property.ValueProvider.SetValue(obj, propertyValue);
}
else
{
object propertyValue;
if (jProperty.Value.Type == JTokenType.Null)
propertyValue = null;
else if (jProperty.Value is JValue)
// Primitive
propertyValue = ((JValue)jProperty.Value).Value;
else
propertyValue = jProperty.Value.ToObject<IDynamicMetaObjectProvider>(serializer);
// Unfortunately the following is not public!
// contract.TrySetMember(obj, memberName, propertyValue);
// So we have to duplicate the logic of what Json.NET has already done.
CallSiteCache.SetValue(memberName, obj, propertyValue);
}
}
return obj;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
internal static class CallSiteCache
{
// Adapted from the answer to
// https://stackoverflow.com/questions/12057516/c-sharp-dynamicobject-dynamic-properties
// by jbtule, https://stackoverflow.com/users/637783/jbtule
// And also
// https://github.com/mgravell/fast-member/blob/master/FastMember/CallSiteCache.cs
// by Marc Gravell, https://github.com/mgravell
private static readonly Dictionary<string, CallSite<Func<CallSite, object, object, object>>> setters
= new Dictionary<string, CallSite<Func<CallSite, object, object, object>>>();
public static void SetValue(string propertyName, object target, object value)
{
CallSite<Func<CallSite, object, object, object>> site;
lock (setters)
{
if (!setters.TryGetValue(propertyName, out site))
{
var binder = Binder.SetMember(CSharpBinderFlags.None,
propertyName, typeof(CallSiteCache),
new List<CSharpArgumentInfo>{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)});
setters[propertyName] = site = CallSite<Func<CallSite, object, object, object>>.Create(binder);
}
}
site.Target(site, target, value);
}
}
Then use it like:
var settings = new JsonSerializerSettings
{
Converters = { new EntityBaseConverter() },
};
var stroy = JsonConvert.DeserializeObject<EntityBase>(JSON, settings);
Since it seems like EntityBase may be a base class for multiple derived classes, I wrote the converter to work for all derived types of EntityBase with the assumption that they all have a parameterized constructor with the same signature.
Note I am taking the EntityName from the JSON. If you would prefer to hardcode it to "Story" you could do that, but you should still add the actual name of the EntityName property to the usedParameters collection to prevent a dynamic property with the same name from getting created.
Sample working .Net fiddle here.

Related

How to customize value setting during deserialization with contract resolver and value provider

I am failing at a task to encrypt certain fields when serializing into JSON and decrypt those fields during deserialization into a specific C# class.
I reduced the problem to its most basic issue, which is that I cannot customize the deserialization of specific fields by manipulating the value, and I don't know the reason. I am using a custom contract resolver and a custom value provider for each field. I can see that the GetValue function is executed but the SetValue is never executed.
The code sample:
class Program
{
static void Main(string[] args)
{
var text = "This is text";
var number = 1;
var anotherText = "This is another text";
var anotherNumber = 2;
var sampleInner = new SampleInner(anotherText, anotherNumber);
var sample = new SampleMessage(text, number, sampleInner);
var myCustomContractResolver = new MyCustomContractResolver();
var jsonSettings = GetJsonSettings(myCustomContractResolver);
Console.WriteLine("Serializing..");
var json = JsonConvert.SerializeObject(sample, jsonSettings);
Console.WriteLine(json);
Console.WriteLine("Deserializing..");
var sampleDeserialized = JsonConvert.DeserializeObject(json, typeof(SampleMessage), jsonSettings);
Console.WriteLine(sampleDeserialized);
Console.ReadLine();
}
private static JsonSerializerSettings GetJsonSettings(IContractResolver contractResolver)
{
var jsonSettings =
new JsonSerializerSettings
{
ContractResolver = contractResolver
};
return jsonSettings;
}
}
the custom contract resolver:
public class MyCustomContractResolver
: DefaultContractResolver
{
public MyCustomContractResolver()
{
NamingStrategy = new CamelCaseNamingStrategy();
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var jsonProperties = base.CreateProperties(type, memberSerialization);
foreach (var jsonProperty in jsonProperties)
{
var propertyInfo = type.GetProperty(jsonProperty.UnderlyingName);
var defaultValueProvider = jsonProperty.ValueProvider;
jsonProperty.ValueProvider = new MyValueProvider(defaultValueProvider);
}
return jsonProperties;
}
}
and the custom value provider whose SetValue is never executed during deserialization:
public class MyValueProvider
: IValueProvider
{
private readonly IValueProvider _valueProvider;
public MyValueProvider(IValueProvider valueProvider)
{
_valueProvider = valueProvider;
}
public void SetValue(object target, object value)
{
//This is not executed during deserialization. Why?
_valueProvider.SetValue(target, value);
Console.WriteLine($"Value set: {value}");
}
public object GetValue(object target)
{
var value = _valueProvider.GetValue(target);
Console.WriteLine($"Value get: {value}");
return value;
}
}
Here is the sample code to reproduce it in case it's needed.
Hopefully somebody can let me know what am I missing :)
UPDATE 1: The object I serialize/deserialize is immutable (no public setters) and that's a requirement because I need to support such objects. As a comment points out, then it makes sense there is no SetValue executed
UPDATE 2: Thanks to #dbc for the brilliant answer no I know a good workaround for deserialization into immutable object. The final version code after the accepted answer.
UPDATE 3: The chosen answer is absolutely correct given the question. However, after investigating further I decided to go with a slightly different approach that works for both immutable and mutable classes in case somebody is in a similar situation. Instead using value providers I use now a combination of contract resolver and json converter so that with the contract resolver I can decide how to serialize/deserialize based on the type, and with the json converter I can access the value during serialization/deserialization and manipulate as desired.
Basically, on my contract resolver I override the method to create properties (where I can access my original Type properties) and selectively I specify which json converter to use.
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var jsonProperties = base.CreateProperties(type, memberSerialization);
//Filter here based on type, attribute or whatever and if want to customize a specific property type:
foreach (var jsonProperty in jsonProperties)
{
jsonProperty.Converter = new MyJsonConverter();
}
return jsonProperties;
}
and in the MyJsonConverter we can choose what to do when writing into json or when reading from json:
public class MyJsonConverter
: JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//I can do whatever with value
var finalValue = $"{value}-edited";
writer.WriteValue(finalValue);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// I can do whatever with the value
var value = (string)reader.Value;
var newValue = "whatever";
return newValue;
}
public override bool CanWrite => true;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return true;
}
}
The reason that IValueProvider.SetValue is not called for the properties of SampleInner is that SampleInner is immutable and so there are no set methods to be called. Instead, the JSON properties are matched to arguments of the type's single parameterized constructor by name (modulo case), deserialized to the type of the matched argument, and then passed into the constructor as explained here.
Even if you were to make the properties mutable the setter will not be called for properties already passed into the constructor, as Json.NET makes the (reasonable) assumption that passing a property value into the constructor is sufficient to set the property's value.
So, what are your options?
Firstly, you could make your types mutable with a default constructor. The setters and constructor could be private as long as they are marked with appropriate attributes:
public class SampleInner
{
[JsonProperty] // Adding this attribute informs Json.NET that the private setter can be called.
public string AnotherText { get; private set; }
[JsonProperty]
public int AnotherNumber { get; private set; }
[JsonConstructor] // Adding this attribute informs Json.NET that this private constructor can be called
private SampleInner() { }
public SampleInner(string anotherText, int anotherNumber)
{
this.AnotherText = anotherText;
this.AnotherNumber = anotherNumber;
}
}
Now that there are setters to be called, your MyValueProvider.SetValue() will get invoked. Demo fiddle #1 here.
Secondly, if you cannot so modify your types, you could wrap the constructor method called in some decorator that does the necessary pre-processing, however this is made difficult by the fact that JsonObjectContract.ParameterizedCreator is nonpublic. Thus you cannot get direct access to the parameterized constructor that Json.NET has chosen, in order to decorate it. You can, however, determine its arguments, which are specified by JsonObjectContract.CreatorParameters. When this collection is populated then either OverrideCreator is set or the (secret) ParameterizedCreator is set. This allows the necessary logic to be inserted as follows:
public class MyCustomContractResolver : DefaultContractResolver
{
public MyCustomContractResolver() { NamingStrategy = new CamelCaseNamingStrategy(); }
static ObjectConstructor<Object> GetParameterizedConstructor(JsonObjectContract contract)
{
if (contract.OverrideCreator != null)
return contract.OverrideCreator;
// Here we assume that JsonSerializerSettings.ConstructorHandling == ConstructorHandling.Default
// If you would prefer AllowNonPublicDefaultConstructor then you need to remove the check on contract.DefaultCreatorNonPublic
if (contract.CreatorParameters.Count > 0 && (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic))
{
// OK, Json.NET has a parameterized constructor stashed away in JsonObjectContract.ParameterizedCreator
// https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs#L100
// But, annoyingly, this value is internal so we cannot get it!
// But because CreatorParameters.Count > 0 and OverrideCreator == null we can infer that such a constructor exists, and so call it using Activator.CreateInstance
return (args) => Activator.CreateInstance(contract.CreatedType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, args, CultureInfo.InvariantCulture);
}
return null;
}
static ObjectConstructor<Object> CustomizeConstructor(JsonObjectContract contract, ObjectConstructor<Object> constructor)
{
if (constructor == null)
return null;
return (args) =>
{
// Add here your customization logic.
// You can match creator parameters to properties by property name if needed.
foreach (var pair in args.Zip(contract.CreatorParameters, (a, p) => new { Value = a, Parameter = p }))
{
// Get the corresponding property in case you need to, e.g., check its attributes:
var property = contract.Properties[pair.Parameter.PropertyName];
if (property == null)
Console.WriteLine("Argument {0}: Value {1}", pair.Parameter.PropertyName, pair.Value);
else
Console.WriteLine("Argument {0} (corresponding to JsonProperty {1}): Value {2}", pair.Parameter.PropertyName, property, pair.Value);
}
return constructor(args);
};
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
contract.OverrideCreator = CustomizeConstructor(contract, GetParameterizedConstructor(contract));
return contract;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var jsonProperties = base.CreateProperties(type, memberSerialization);
foreach (var jsonProperty in jsonProperties)
{
var defaultValueProvider = jsonProperty.ValueProvider;
jsonProperty.ValueProvider = new MyValueProvider(defaultValueProvider);
}
return jsonProperties;
}
}
Notes:
If a default constructor exists but is nonpublic, the above contract resolver assumes that it is not used. If you would prefer nonpublic default constructors to be used, you will need to set JsonSerializerSettings.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor and also modify the code in GetParameterizedConstructor() above to remove the check on contract.DefaultCreatorNonPublic:
if (contract.CreatorParameters.Count > 0 && contract.DefaultCreator == null)
It would be reasonable to request an enhancement to allow access to, and customization of, JsonObjectContract.ParameterizedCreator.
(I suppose you could try to access JsonObjectContract.ParameterizedCreator directly via reflection...)
Demo fiddle #2 here.

Skip Serializing the the Property Name for a JSON Object using Newtonsoft.Json

I have the below simple class
class MyType {
string JsonData { get; set;}
string OtherProperty { get; set; }
}
where JsonData contains formatted JSON that I want to plug into the serialized class output:
"Prop1":"Value1",
"Prop2":"Value2",
"Prop3":"Value3"
and I want to have it serialized as
{
<JsonData>,
"OtherProperty": ""
}
I have tried using a custom JsonConverter for that field, but I cannot find a way to remove the serialization of the property name:
{
"JsonData": <JsonData>,
"OtherProperty": ""
}
So basically I just need to have "JsonData": removed from the output (preferably without using string.Replace())
You can do this with a custom JsonConverter, but the converter has to be made to handle the whole MyType class as opposed to just the JsonData property. As you've seen, there is no way to omit the property name from within a converter that handles just the property.
Here is how I might write the converter:
class MyTypeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(MyType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
MyType mt = (MyType)value;
JObject jo = new JObject();
if (!string.IsNullOrWhiteSpace(mt.JsonData))
{
// JsonData is assumed to contain pre-formatted, comma-separated, JSON properties
jo.Add(JObject.Parse("{" + mt.JsonData + "}").Properties());
}
foreach (PropertyInfo prop in typeof(MyType).GetProperties()
.Where(p => p.CanRead && p.Name != "JsonData"))
{
object val = prop.GetValue(mt, null);
jo.Add(prop.Name, val != null ? JToken.FromObject(val, serializer) : JValue.CreateNull());
}
jo.WriteTo(writer);
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, just annotate your class with a [JsonConverter] attribute like this:
[JsonConverter(typeof(MyTypeConverter))]
class MyType
{
...
}
Demo fiddle: https://dotnetfiddle.net/bu0NgC
Having presented the above, I should point out that this might not be the best solution, depending on where the pre-formatted JSON properties are coming from. If they are coming from a third party out of your control, then the design you have along with the converter is probably adequate. However, if you are creating the pre-formatted JSON properties yourself somewhere along the line as a way of having dynamic properties in your MyType class, then there is definitely a better way to do it.
Instead of using a string, you can declare JsonData as a Dictionary<string, object> and mark it with the special [JsonExtensionData] attribute:
class MyType
{
public MyType()
{
JsonData = new Dictionary<string, object>();
}
[JsonExtensionData]
public Dictionary<string, object> JsonData { get; set;}
public string OtherProperty { get; set; }
}
Then, just add your dynamic properties to the dictionary as key-value pairs:
MyType mt = new MyType();
mt.JsonData.Add("Prop1", "Value1");
...
When you serialize MyType, the dictionary KVPs will be automatically added as properties of the JSON object just like OtherProperty. No converter needed!
Fiddle: https://dotnetfiddle.net/f8gCVA

How can I remove quotes from serilaized string?

I need to produce a JSON document, that will be parsed by a SSI mechanism on a device. The document will actually be a json serialized dictionary. For the sake of simplicity, let's say, that it should look like this:
var x = new Dictionary<string,object>
{
["A"]=new {x = "<!-- ?A.x -->"},
["B"]=new {x = "<!-- ?B.x -->"}
};
JsonConvert.SerializeObject(x).Dump();
Which produces in LinqPad:
{"A":{"x":"<!-- ?A.x -->"},"B":{"x":"<!-- ?B.x -->"}}
But actually those "x" fields are numbers, and when fetched from the device, they will contain numbers. So I would need to serialize this dictionary without quotes around a field value that is string on C# side:
{"A":{"x":<!-- ?A.x -->},"B":{"x":<!-- ?B.x -->}}
How can I force Newtonsoft Json.NET serializer not to add quotes to the value of specific fields (not all) during serialization?
Thank you.
One way to do it is by introducing new JsonConverter (sample). To separate the functionality of "raw serialization", you could introduce new type that would just wrap a string value, eg.
public class RawJson
{
public string Value { get; private set; }
public RawJson(string value)
{
Value = value;
}
}
Then you just check for this type in converter's CanConvert() and in WriteJson() you can just write
writer.WriteRawValue(((RawJson)value).Value);
And below is the actual solution, based on #kiziu's suggestion to use custom converter. But without custom type. As the converter can be added with the attribute to members too, and not only to classes or the converter itself, I can use it on the property I need. The above LinqPad scratch updated:
internal class RawJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteRawValue((string)value);
}
}
class myClass
{
[JsonConverter(typeof(RawJsonConverter))]
public string x;
}
void Main()
{
var x = new Dictionary<string,object>
{
["A"]=new myClass {x = "<!-- ?A.x -->"},
["B"]=new myClass {x = "<!-- ?B.x -->"}
};
JsonConvert.SerializeObject(x).Dump();
}
And the result is, as expected:
{"A":{"x":<!-- ?A.x -->},"B":{"x":<!-- ?B.x -->}}

JSON.NET deserializes an object implementing IDictionary<TKey, TValue> as dictionary, how to force regular class serialization?

Let's say I've defined an interface:
public interface IExtensibleObject : IDictionary<string, object>
{
// Some members here, but it doesn't matter for this question
}
And I've designed a class which implements the whole interface:
public class Customer : IExtensibleObject
{
public Guid Id { get; set; }
// IExtensibleObject implemented members
}
When I try to deserialize a JSON string to Customer, JSON.NET will access the IDictionary<TKey, TValue> indexer to set Id property (i.e. instance["Id"] = value):
Customer customer = JsonConvert.DeserializeObject<Customer>(#"{""Id"":""bcf66a92-00ea-4124-afa7-a6c200ae5886""}");
Is there some built-in way of avoiding the whole behavior?. I need to deserialize the whole object as a regular object even when it implements IDictionary<TKey, TValue>.
You can do it with custom contract resolver. Default contract resolver checks if class implements IDictionary and if so - serializes\deserializes it as such. You can change that:
class CustomResolver : DefaultContractResolver {
protected override JsonContract CreateContract(Type objectType) {
// if type implements your interface - serialize it as object
if (typeof(IExtensibleObject).IsAssignableFrom(objectType)) {
return base.CreateObjectContract(objectType);
}
return base.CreateContract(objectType);
}
}
And then just:
var settings = new JsonSerializerSettings(); // or change default settings
settings.ContractResolver = new CustomResolver();
Customer customer = JsonConvert.DeserializeObject<Customer>(#"{""Id"":""bcf66a92-00ea-4124-afa7-a6c200ae5886""}", settings);
You'll find an answer to this here : Duplicate Question
The only way I know is the same as answered in the link - you'll have to implement an custom JsonConverter
public class CustomerConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof (Customer);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var eobj = (Customer) value;
var temp = new Dictionary<string, object>(eobj);
temp.Add("Id", eobj.Id);
serializer.Serialize(writer, temp);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
var temp = serializer.Deserialize<Dictionary<string, object>>(reader);
var eobj = new Customer();
foreach (var key in temp.Keys)
{
if (key == "Id")
eobj.Id = (Guid) temp[key];
else
eobj.Add(key, temp[key]);
}
return eobj;
}
}

Serialize only derived type using Newtonsoft Json.NET

Is there any JsonSerializerSettings available for serializing only the derived type.
for example consider i have below two class. When I am serializing Employee object the the result json should only contains the properties of employee not the person class.
public class Person
{
public string Name { get; set; }
}
public class Employee : Person
{
public DateTime JoiningDate { get; set; }
public string EmployeeId { get; set;}
}
Questions like those usually reflect an issue with model design, however, one way to do what you want to do is to get rid of the inheritance; you could try something like converting your object to dynamic and then serialize the dynamic object :
class MyJson
{
public static string SerializeObject<T>(T obj, bool ignoreBase)
{
if (!ignoreBase)
{
return JsonConvert.SerializeObject(obj);
}
var myType = typeof(T);
var props = myType.GetProperties().Where(p => p.DeclaringType == myType).ToList();
var x = new ExpandoObject() as IDictionary<string, Object>;
props.ForEach(p => x.Add(p.Name, p.GetValue(obj, null)));
return JsonConvert.SerializeObject(x);
}
}
call it like
MyJson.SerializeObject<Employee>(e, true)
This way you can use it for any type and filter the properties to serialize however you wish in the helper class. For example, you can check the property attributes and decided if it should be added to the dynamic object.
You can use a custom JsonConverter for that purpose. Please see below for a basic version for your purpose.
public class PersonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
if (objectType == typeof(Employee))
return true;
return false;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return "";
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject o = (JObject)t;
o.Remove("Name"); //currently the property name is hardcoded. You could enhance this to remove any property you like
o.WriteTo(writer);
}
}
}
After creating your JsonConverter you can use that during deserialization like below,
var des = JsonConvert.SerializeObject(e, new PersonConverter());
//e is the Person instance we want to convert and PersonConverter is the custom converter
//we use of serialization
Please see this link for more information on this

Categories

Resources