How to Override a Default JsonConverter (specified in an attribute) - c#

I would like the following Author type to have a default JsonConverter, and be able to override it at runtime.
[JsonConverter(typeof(BaseJsonConverter))]
public class Author
{
// The ID of an author entity in the application.
public int ID { set; get; }
// The ID of an Author entity in its source.
public string SourceID { set; set; }
}
I used the following code to override the default converter (i.e., BaseJsonConverter).
public class AlternativeConverter : BaseJsonConverter
{ // the serializer implementation is removed for clarity. }
// Deserialize using AlternativeConverter:
var author = JsonConvert.DeserializeObject<Author>(jsonString, new AlternativeConverter());
Question
Using the above call, the AlternativeConverter is first constructed; however, then an instance of BaseJsonConverter is initialized and used for deserialization. So, the AlternativeConverter is never used.
Executable example: https://dotnetfiddle.net/l0bgYO
Use case
The application is to convert different JSON objects, obtained from different sources, to a common C# type. Commonly data comes from a source for that we define the default converter (i.e., BaseJsonConverter), and for data coming from other sources, we define different converters per each.
Background
I am aware of methods such as this one, and indeed I am using similar method partially. With ref to that article, I need to have different _propertyMappings depending on the source of input, because in my application attribute to property mapping is not one-to-one. For instance, I have the following JSON objects:
{
"id":123
}
// and
{
"id":"456"
}
where the first JSON object should be deserialized to:
author.ID = 123
author.SourceID = null
and the second JSON object should be deserialized as:
author.ID = 0
author.SourceID = "456"

You can use a custom ContractResolver to override a [JsonConverter] attribute programmatically. To solve your problem you could make a custom resolver like this:
public class CustomResolver : DefaultContractResolver
{
private Dictionary<Type, JsonConverter> Converters { get; set; }
public CustomResolver(Dictionary<Type, JsonConverter> converters)
{
Converters = converters;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
if (Converters.TryGetValue(objectType, out JsonConverter converter))
{
contract.Converter = converter;
}
return contract;
}
}
Then, when you wanted to use the AlternativeConverter in place of the BaseJsonConverter, you could use the custom resolver like this:
// map the `Author` type to the `AlternativeConverter`
var converters = new Dictionary<Type, JsonConverter>()
{
{ typeof(Author), new AlternativeConverter() }
};
// Create a resolver with the converter mapping and add it to the serializer settings
var settings = new JsonSerializerSettings
{
ContractResolver = new CustomResolver(converters)
};
// Use the settings when deserializing
var author = JsonConvert.DeserializeObject<Author>(jsonString, settings);
Demo Fiddle: https://dotnetfiddle.net/cu0igV
Of course, if all you're really doing with these converters is remapping properties to different names, you could just use a ContractResolver for that in the first place and get rid of the converters altogether. See Json.NET deserialize or serialize json string and map properties to different property names defined at runtime for more information on that approach.

I think you should try to use different JsonSerializerSettings instances for different data sources, with different Converters collections. And remove JsonConverter attributes from your classes.

Related

Some fields missing from API response in postman But It is there in result of controller - asp .net core 6.0 [duplicate]

Fellow programmers,
I've encountered a strange behavior in Newtonsoft.Json.
When I'm trying to serialize an object looking like this:
public class DMSDocWorkflowI
{
[JsonProperty("DMSDocWorkflowIResult")]
public bool DMSDocWorkflowIResult { get; set; }
[JsonProperty("DMSDocWorkflowIResultSpecified")]
public bool DMSDocWorkflowIResultSpecified { get; set; }
}
Using this simple call with no custom converters / binders / contract resolvers:
var testObject = new DMSDocWorkflowI();
var json = JsonConvert.SerializeObject(testObject, Formatting.Indented);
or even with JToken.FromObject(...) I always get only one property:
{
"DMSDocWorkflowIResultSpecified": false
}
When I attach the trace writer, it catches only this:
[0]: "2016-08-30T11:06:27.779 Info Started serializing *****DMSDocWorkflowI. Path ''."
[1]: "2016-08-30T11:06:27.779 Verbose IsSpecified result for property 'DMSDocWorkflowIResult' on *****DMSDocWorkflowI: False. Path ''."
[2]: "2016-08-30T11:06:27.779 Info Finished serializing *****.DMSDocWorkflowI. Path ''."
[3]: "2016-08-30T11:06:27.780 Verbose Serialized JSON: \r\n{\r\n \"DMSDocWorkflowIResultSpecified\": false\r\n}"
So it seems Newtonsoft.Json treats this "Specified" property somewhat magically.
Can I turn this off?
I need both these properties in resulting JSON with exactly these names.
This behavior is mentioned, very briefly, in the Json.NET 4.0.1 release notes: New feature - Added XmlSerializer style Specified property support. The XmlSerializer functionality is in turn described in MinOccurs Attribute Binding Support:
[For optional fields] Xsd.exe generates a public field of type bool whose name is the element field's name with Specified appended. For example, if the element field's name is startDate, the bool field's name becomes startDateSpecified. When serializing an object to XML, the XmlSerializer class checks the value of the bool field to determine whether to write the element.
I feel as though this functionality should be documented here, but is not. You might want to open a documentation issue with Newtonsoft.
Since you don't want this behavior, if you are using Json.NET 11.0.1 or later, you can disable it for all classes by instantiating your own DefaultContractResolver and settting DefaultContractResolver.IgnoreIsSpecifiedMembers = true:
public static class JsonContractResolvers
{
// Newtonsoft recommends caching and reusing contract resolvers for best performance:
// https://www.newtonsoft.com/json/help/html/Performance.htm#ReuseContractResolver
// But be sure not to modify IgnoreIsSpecifiedMembers after the contract resolver is first used to generate a contract.
public static readonly DefaultContractResolver IgnoreIsSpecifiedMembersResolver =
new DefaultContractResolver { IgnoreIsSpecifiedMembers = true };
}
Then pass it to JsonConvert as follows:
var settings = new JsonSerializerSettings { ContractResolver = JsonContractResolvers.IgnoreIsSpecifiedMembersResolver };
var json = JsonConvert.SerializeObject(testObject, Formatting.Indented, settings);
Or to create a JToken do:
var jToken = JToken.FromObject(testObject, JsonSerializer.CreateDefault(settings));
If you are using an earlier version, you will need to create and cache a custom contract resolver:
public static class JsonContractResolvers
{
public static readonly DefaultContractResolver IgnoreIsSpecifiedMembersResolver =
new IgnoreSpecifiedContractResolver();
}
internal class IgnoreSpecifiedContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.GetIsSpecified = null;
property.SetIsSpecified = null;
return property;
}
}

Xamarin Forms - Observable Collection to CUSTOM JSON

I have an observable collection that contains a list of products that is binded to ListView.
However I want to export this Observable Collection as a JSON file AND only specific entries so I can submit it through the API.
For example.
The full observable collection contains
Product ID
Product Name
Product Price
Product Qty
But I want to extract the JSON file to only:
Product ID
Product Qty
Here's my code:
public static ObservableCollection<FBProduct> fbproducts = new ObservableCollection<FBProduct>();
Here's my JSON deserialiser
shoppingcartjson = JsonConvert.SerializeObject(ShoppingCart.fbproducts);
How can I only extract only ProductID and ProductQTY from that ObservableCollection like so:
"line_items": [{"product_id":79631,"quantity":1}],
It's simple in your FBProduct class use the JsonIgnore attribute!
For instance:
public class FBProduct
{
[JsonIgnore]
public double Name { get; set; }
.
.
Also, add the following using statement:
using Newtonsoft.Json;
Good luck!
Feel free to get back if you have questions.
If you want to always ignore a property when serialising your FBProduct, then go ahead and use FreakyAli's answer. I'll give a quick explainer as to how to ignore properties only sometimes.
Sometimes you want to ignore some properties, while other times you want the full class without ignoring any properties. But by placing a [JsonIgnore] attribute on a property you will ignore it always, which isn't great. So instead, Newtonsoft offers a way to ignore properties conditionally, using what they call a contract resolver. You can implement your own contract resolvers to be able to programmatically ignore properties sometimes (as well as do everything else you could using their attributes).
Here is how you would go about implementing a contract resolver that conditionally ignores some properties:
public class IgnorePropertyContractResolver : DefaultContractResolver
{
// Holds our information for which properties to ignore on which types
private readonly Dictionary<Type, HashSet<string>> _ignores;
public IgnorePropertyContractResolver()
{
_ignores = new Dictionary<Type, HashSet<string>>();
}
public void IgnoreProperty(Type type, params string[] propertyNames)
{
// If we don't know the type to ignore properties on, initialize the HashSet
if (_ignores.ContainsKey(type))
_ignores[type] = new HashSet<string>();
foreach (var prop in propertyNames)
_ignores[type].Add(prop);
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
// Create the property as normal
var property = base.CreateProperty(member, memberSerialization);
// If we're ignoring the property
if (IsIgnored(property.DeclaringType, property.PropertyName))
{
// Don't serialize and ignore
property.ShouldSerialize = i => false;
property.Ignored = true;
}
return property;
}
private bool IsIgnored(Type type, string propertyName)
{
// If we're not ignoring any property on the type, return false
if (!_ignores.ContainsKey(type))
return false;
// If we are ignoring some properties on the type, return if we're ignoring the given property
return _ignores[type].Contains(propertyName);
}
}
We then use this custom contract resolver as follwing:
var fbProduct = new FBProduct();
var resolver = new IgnorePropertyContractResolver();
resolver.IgnoreProperty(typeof(FBProduct),
nameof(FBProduct.ProductID),
nameof(FBProduct.Name),
nameof(FBProduct.Price),
nameof(FBProduct.Qty)
);
var serialized = JsonConvert.SerializeObject(
fbProduct,
Formatting.None, // You can choose any formatting you want
new JsonSerializerSettings
{
ContractResolver = resolver
}
);

Selectively escape HTML in strings during deserialization

I'm looking to write a JsonConverter which escapes HTML in strings, unless the [AllowHtml] attribute has been applied;
private class ObjectWithStrings
{
// will be HTML-escaped
public string Name { get; set; }
// won't be escaped
[AllowHtml]
public string Unsafe { get; set; }
}
So I'm trying to write a JsonConverter with a custom ReadJson property;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var s = (string)reader.Value;
if (s == null)
{
return null;
}
// here I need to get a PropertyInfo so I can call GetCustomAttribute<AllowHtmlAttribute>();
var encoded = System.Web.Security.AntiXss.AntiXssEncoder.HtmlEncode(s, useNamedEntities: true);
return encoded;
}
The gap I've got is that I can't see if Json.Net will let me know the property I'm reading into. Consequently, I can't figure out how to get the property's custom attributes.
Is there a way to find out what property I'm serialising into, or a different pattern recommended for this kind of thing?
EDIT: I failed to write a clear question; I've attempted to write a JsonConverter which deserialises strings, -- see the implementation above of CanConvert(). I suspect that choice is the start of my problem; I may need to deserialise objects with string properties, and do a standard deserialize except when deserialising particular properties.
From within a custom JsonConverter, you can find the name of the JSON property being deserialized by picking it out of the Path property from the JsonReader.
string propertyName = reader.Path.Split('.').Last();
However, this will not solve your overall problem. Assuming the name of the JSON property matches your target class property, you'd still need a way to get the parent object type so you can get the custom attributes from it. Unfortunately, this information is not available to you inside a converter. A converter is intended to be responsible only for the object type it says it can convert (string in your case), and that object's child properties (none in this case, since string is a primitive). So, to make it work, the converter would need to be written to operate on the parent class, and would then need to handle all the string properties of that class. Since your goal seems to be to apply the HTML encoding behavior to all strings in all classes, then you would need a generic converter that handles all non-primitive types, which could get pretty messy, depending on the breadth of what you're trying to deserialize.
Fortunately, there is a better way. Instead of using a JsonConverter, you can use a custom IContractResolver in combination with a IValueProvider to solve this. A ContractResolver is much better suited to problems like this where you want to apply a certain behavior broadly.
Below is an example of the code you would need. The CustomResolver class extends the DefaultContractResolver provided by Json.Net. The CreateProperties() method inspects the JsonProperty objects created by the base resolver and attaches an instance of the inner HtmlEncodingValueProvider class to any string properties which do not have the [AllowHtml] attribute applied. Each value provider later handles the actual encoding of its target string property via the SetValue() method.
public class CustomResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
// Find all string properties that do not have an [AllowHtml] attribute applied
// and attach an HtmlEncodingValueProvider instance to them
foreach (JsonProperty prop in props.Where(p => p.PropertyType == typeof(string)))
{
PropertyInfo pi = type.GetProperty(prop.UnderlyingName);
if (pi != null && pi.GetCustomAttribute(typeof(AllowHtmlAttribute), true) == null)
{
prop.ValueProvider = new HtmlEncodingValueProvider(pi);
}
}
return props;
}
protected class HtmlEncodingValueProvider : IValueProvider
{
PropertyInfo targetProperty;
public HtmlEncodingValueProvider(PropertyInfo targetProperty)
{
this.targetProperty = targetProperty;
}
// SetValue gets called by Json.Net during deserialization.
// The value parameter has the original value read from the JSON;
// target is the object on which to set the value.
public void SetValue(object target, object value)
{
var encoded = System.Web.Security.AntiXss.AntiXssEncoder.HtmlEncode((string)value, useNamedEntities: true);
targetProperty.SetValue(target, encoded);
}
// GetValue is called by Json.Net during serialization.
// The target parameter has the object from which to read the string;
// the return value is the string that gets written to the JSON
public object GetValue(object target)
{
// if you need special handling for serialization, add it here
return targetProperty.GetValue(target);
}
}
}
To use the resolver, create a new JsonSerializerSettings instance, then set its ContractResolver property to a new instance of the custom resolver and pass the settings to the JsonConvert.DeserializeObject() method.
Here is a short demo:
class Program
{
static void Main(string[] args)
{
string json = #"
{
""Name"" : ""<b>Foo Bar</b>"",
""Description"" : ""<p>Bada Boom Bada Bing</p>"",
}";
JsonSerializerSettings settings = new JsonSerializerSettings
{
ContractResolver = new CustomResolver()
};
Foo foo = JsonConvert.DeserializeObject<Foo>(json, settings);
Console.WriteLine("Name: " + foo.Name);
Console.WriteLine("Desc: " + foo.Description);
}
}
class Foo
{
public string Name { get; set; }
[AllowHtml]
public string Description { get; set; }
}
class AllowHtmlAttribute : Attribute { }
Here is the output. Notice that the Name property gets HTML encoded while the Description property does not.
Name: <b>Foo Bar</b>
Desc: <p>Bada Boom Bada Bing</p>
Fiddle: https://dotnetfiddle.net/cAg4NC

Control Deserializing Json Properties via Attributes? [duplicate]

I have some data in a C# DataSet object. I can serialize it right now using a Json.net converter like this
DataSet data = new DataSet();
// do some work here to populate 'data'
string output = JsonConvert.SerializeObject(data);
However, this uses the property names from data when printing to the .json file. I would like to change the property names to be something different (say, change 'foo' to 'bar').
In the Json.net documentation, under 'Serializing and Deserializing JSON' → 'Serialization Attributes' it says "JsonPropertyAttribute... allows the name to be customized". But there is no example. Does anyone know how to use a JsonPropertyAttribute to change the property name to something else?
(Direct link to documentation)
Json.net's documentation seems to be sparse. If you have a great example I'll try to get it added to the official documentation.
Thanks!
You could decorate the property you wish controlling its name with the [JsonProperty] attribute which allows you to specify a different name:
using Newtonsoft.Json;
// ...
[JsonProperty(PropertyName = "FooBar")]
public string Foo { get; set; }
Documentation: Serialization Attributes
If you don't have access to the classes to change the properties, or don't want to always use the same rename property, renaming can also be done by creating a custom resolver.
For example, if you have a class called MyCustomObject, that has a property called LongPropertyName, you can use a custom resolver like this…
public class CustomDataContractResolver : DefaultContractResolver
{
public static readonly CustomDataContractResolver Instance = new CustomDataContractResolver ();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(MyCustomObject))
{
if (property.PropertyName.Equals("LongPropertyName", StringComparison.OrdinalIgnoreCase))
{
property.PropertyName = "Short";
}
}
return property;
}
}
Then call for serialization and supply the resolver:
var result = JsonConvert.SerializeObject(myCustomObjectInstance,
new JsonSerializerSettings { ContractResolver = CustomDataContractResolver.Instance });
And the result will be shortened to {"Short":"prop value"} instead of {"LongPropertyName":"prop value"}
More info on custom resolvers here
There is still another way to do it, which is using a particular NamingStrategy, which can be applied to a class or a property by decorating them with [JSonObject] or [JsonProperty].
There are predefined naming strategies like CamelCaseNamingStrategy, but you can implement your own ones.
The implementation of different naming strategies can be found here: https://github.com/JamesNK/Newtonsoft.Json/tree/master/Src/Newtonsoft.Json/Serialization
You can directly use
[JsonProperty(Name = "access_token")]
public string AccessToken { get; set; }
or
[JsonProperty("access_token")]
public string AccessToken { get; set; }
and serialize using Newthonsoft.Json library will be detect how change it

c# JavaScriptConverter - how to deserialize custom property?

I've got a class which has been serialized into JSON, and which I'm trying to deserialize into an object.
e.g.
public class ContentItemViewModel
{
public string CssClass { get; set; }
public MyCustomClass PropertyB { get; set; }
}
the simple property (CssClass) will deserialize with:
var contentItemViewModels = ser.Deserialize<ContentItemViewModel>(contentItems);
But PropertyB gets an error...
We added a JavaScriptConverter:
ser.RegisterConverters(new List<JavaScriptConverter>{ publishedStatusResolver});
But when we added 'MyCustomClass' as a 'SupportedType', the Deserialize method was never called. However when we have ContentItemViewModel as the SupportedType, then Deserialize is called.
We've got a current solution which looks something like this:
class ContentItemViewModelConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var cssClass = GetString(dictionary, "cssClass"); //I'm ommitting the GetString method in this example...
var propertyB= GetString(dictionary, "propertyB");
return new ContentItemViewModel{ CssClass = cssClass ,
PropertyB = new MyCustomClass(propertyB)}
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new Exception("Only does the Deserialize");
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new List<Type>
{
typeof(ContentItemViewModel)
};
}
}
}
But we'd prefer a simpler solution of only deserializing MyCustomClass, as there are a number of other fields which are on the ViewModel, and it seems a waste to have to edit this converter every time we change/add a property....
Is there a way to Deserialize JUST PropertyB of type MyCustomClass?
Thanks for your help!
Have you considered using DatacontractJsonSerializer
[DataContract]
public class MyCustomClass
{
[DataMember]
public string foobar { get; set; }
}
[DataContract]
public class ContentItemViewModel
{
[DataMember]
public string CssClass { get; set; }
[DataMember]
public MyCustomClass PropertyB { get; set; }
}
class Program
{
static void Main(string[] args)
{
ContentItemViewModel model = new ContentItemViewModel();
model.CssClass = "StackOver";
model.PropertyB = new MyCustomClass();
model.PropertyB.foobar = "Flow";
//Create a stream to serialize the object to.
MemoryStream ms = new MemoryStream();
// Serializer the User object to the stream.
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ContentItemViewModel));
ser.WriteObject(ms, model);
byte[] json = ms.ToArray();
ms.Close();
string s= Encoding.UTF8.GetString(json, 0, json.Length);
Console.ReadLine();
}
}
Add all possible classes to DatacontractJsonSerializer.KnownTypes if MyCustomClass has derivations.
For whatever it may be worth after all this time, but I stumbled over the same problem and the solution is that the Deserializer hasn't got a clue about the classes you are deserializing unless you give him the necessary information.
On the top level, it knows the type from the type parameter of Deserialize<>(). That's why your converter for ContentItemViewModel works. For nested objects, it needs __type properties and a JavaScriptTypeResolver.
var ser = new JavaScriptSerializer(new SimpleTypeResolver());
ser.RegisterConverters(myconverters);
MyClass myObject = new MyClass();
string json = ser.Serialize(myObject);
// set a breakpoint here to see what has happened
ser.Deserialize<MyClass>(json);
A TypeResolver adds a __type property to each serialized object. You can write a custom type resolver that uses short names. In this sample, I use the SimpleTypeResolver from .net that "simply" stores the fully qualified type name as __type. When deserializing, the JavaScriptDeserializer finds __type and asks the TypeResolver for the correct type. Then it knows a type and can call a registered JavaScriptConverter.Deserialize method.
Without a TypeResolver, objects are deserialized to a Dictionary because JavaScriptSerializer doesn't have any type information.
If you can't provide a __type property in your json string, I think you'll need to deserialize to Dictionary first and then add a "guessing-step" that interprets the fields to find the right type. Then, you can use the ConvertToType method of JavaScriptSerializer to copy the dictionary into the object's fields and properties.
If you need to use the JavaScriptSerializer that is provides by ASP.NET and can't create your own, consider this section from the .ctor help of JavaScriptSerializer:
The instance of JavaScriptSerializer that is used by the asynchronous communication layer for invoking Web services from client script uses a special type resolver. This type resolver restricts the types that can be deserialized to those defined in the Web service’s method signature, or the ones that have the GenerateScriptTypeAttribute applied. You cannot modify this built-in type resolver programmatically.
Perhaps the GenerateScriptType Attribute can help you. But I don't know what kind of __type Properties are be needed here.

Categories

Resources