C# JsonConverter can convert properties depending on the class - c#

I want to write a JsonConverter it can convert a determinate property type but only depending on the class they are defined.
I am registering the JsonConverter globally via Web API:
var config = GlobalConfiguration.Configuration;
var jsonSettings = config.Formatters.JsonFormatter.SerializerSettings;
jsonSettings.Converters.Add(new SomeConverter());
The Converter can convert for example objects of type MyType.
public override bool CanConvert(Type objectType)
{
return typeof(MyType).IsAssignableFrom(objectType);
}
But now I want to control want the converter should be used or not.
I thought something like that:
[IgnoreSomeConverter]
public class ClassA
{
public MyType PropMyType {get;set;}
}
public class ClassB
{
public MyType PropMyType {get;set;}
}
So I would like that the SomeConverter serialized and deserialized the properties MyType only when the property is defined in a class not decorated with the CustomAttribute IgnoreSomeConverter. In the example I want to use the converter for ClassB but not for ClassA. Any idea to get that?

Newtonsoft.JSON package contains DefaultContractResolver class, register it on your
serializer settings, create a derived class from it, this you can adopt to class or a class property.
public class ShouldSerializeContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = i => false;
property.Ignored = true;
return property;
}
}
And register it.
var serializer = new JsonSerializerSettings { ContractResolver = new ShouldSerializeContractResolver() };
Function has MemberInfo argument from which you can attributes, etc.

Related

Is there a way to have Json.NET honor the System.Text.Json JsonPropertyName attribute

I have a C# type that represents a deserialized C# payload. But it's deserialized in one place by System.Text.Json. In another place, it's Json.NET.
So right now, I have to attribute the properties using both [JsonProperty] (for Json.NET) and [JsonPropertyName] (for System.Text.Json).
Is there a way to tell Json.NET recognize the JsonPropertyName attribute so I don't have to annotate each property twice?
You can create custom contract resolver which will search for JsonPropertyName attribute and use value from it. Example one can look something like that:
public class ContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (member.GetCustomAttribute<JsonPropertyNameAttribute>() is {} stj)
{
property.PropertyName = stj.Name;
return property;
}
return property;
}
}
And usage:
class MyClass
{
[JsonProperty("P1")]
public int MyProperty { get; set; }
[JsonPropertyName("P2")]
public int MyProperty2 { get; set; }
}
var settings = new JsonSerializerSettings
{
ContractResolver = new ContractResolver()
};
Console.WriteLine(JsonConvert.SerializeObject(new MyClass(), settings)); // prints {"P1":0,"P2":0}
Console.WriteLine(JsonConvert.DeserializeObject<MyClass>("{'P1':1,'P2':2}", settings).MyProperty2); // prints 2

Force serialization of property that has [JsonIgnore] attribute

Is it possible to somehow force serialization of x property?
public class Cake
{
[JsonIgnore]
// possibly some attribute like [ReadOnlyJsonIgnore] or [JsonIgnoreReadOnly] would be nice
public int x { get; set; }
}
But keep [JsonIgnore] for deserialization only? What I want to achieve is some form of read only properties. So they can be serialized but can't be deserialized, are simply ignored like they aren't there.
Okay, what I did to make this "force" work(a bit hackish):
I created custom JsonConverter that use custom IContractResolver that ignores [JsonIgnore] only for writing in WriteJson.
public class CustomJsonConverter : JsonConverter
{
...
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// somehow using serializer passed to this method doesn't seem to work
var token = JToken.FromObject(value, new JsonSerializer()
{
ContractResolver = new DontIgnoreContractResolver()
});
token.WriteTo(writer);
}
}
public class DontIgnoreContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var jsonProperty = base.CreateProperty(member, memberSerialization);
if (jsonProperty.Ignored)
{
jsonProperty.Ignored = false;
}
return jsonProperty;
}
}
but for ReadJson it uses default contract resolver and ignores the property. But yeah, that's possible.

Building a custom JSON serializer that isn't invoked by default

I'm using Newtownsoft JSON.Net and want to add custom attributes, then have a JSONConverter that deals with these custom attributes. Currently the way to do this is [JsonConverter(typeof(CustomJsonConverter))]. But I don't want this converter to always be called, only when I pass it into JsonConvert.SerializeObject(...). This is because this class is being used by two different paths, one of which needs to modify the JSON based on the properties and the other doesn't.
In other words, I only want these attributes to be considered when I explicitly tell my code to consider them. They should be ignored when Newtonsoft is doing its default serialization. How can I achieve this?
Example:
class Foo {
[CustomAttributeToAddMyExtraProperty]
public int Bar;
[JsonProperty('default')]
public int Baz;
}
If I just use the default Newtonsoft JSON.Net serialize, I should get
{
"Bar":value1,
"default":value2
}
But if I pass in my custom converter explicitly, I should get this:
{
"Bar":value1,
"default":value2,
"MyExtraProperty":value3
}
Notice that the JSON.Net attributes are always used.
UPDATE
I have tried this:
namespace Project1
{
class Class1
{
static void Main()
{
Class2 foo = new Class2();
Console.WriteLine(JsonConvert.SerializeObject(foo, new JsonSerializerSettings() { ContractResolver = new BlahResolver() }));
Console.Read();
}
}
class Class2
{
[Blah]
public int one = 1;
[JsonProperty]
public int two = 2;
[Blah]
public string three = "3";
}
internal class BlahAttribute : Attribute
{
}
class BlahResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
var attr = property.AttributeProvider.GetAttributes(typeof(BlahAttribute), true);
if (attr.Count == 1)
{
property.Converter = new BlahConverter();
}
return property;
}
}
class BlahConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsValueType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return existingValue;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken token = JToken.Parse("{ \"foo\":\"a\", \"bar\":34 }");
token.WriteTo(writer);
}
}
}
Output:
{"one":{"foo":"a","bar":34},"two":2,"three":{"foo":"a","bar":34}}
I'm able to successfully identify properties with my custom attribute and redirect them to my custom converter. The issue now is the converter is adding properties as if one is a complex type. I want to add the properties in the top level JSON, not as properties of one.
Output I want:
{"one":1, "foo":"a","bar":34,"two":2,"three":3,"foo":"a","bar":34}
I realize that this has multiple properties with the same name. I don't know if that's valid or not, but eventually the object value will be included in the name (ex. one_foo, one_bar), so you can disregard that.
With your update, you're very close to getting what you need to work for this scenario. At this point, your only issue is in the WriteJson method.
All you need to do at this point is simply:
writer.WritePropertyName("propertyName");
writer.WriteValue("propertyValue");
If you need to use a different serializer depending on the current execution path, you are correct that the attribute route will not work
Instead pass in the currently desired converter during the serialization process:
string json = JsonConvert.SerializeObject(employee, Formatting.Indented, new MyCustomConverter(typeof(MyType)));
Source

How to omit get-only properties only when [JsonProperty] attribute is not set on it?

I use Json.Net in my project.
I need to find a solution where get-only properties do not serialize only if attribute [JsonProperty] is not set.
public class Example
{
[JsonProperty]
public string ThisPropertyHasToBeSerialized {get;}
public string ThisPropertyHasToBeSkipped {get;}
}
I found a partial answer to it at:
Is there a way to ignore get-only properties in Json.NET without using JsonIgnore attributes?
But I want to leave an opportunity for get-only properties to be serialized in case it is needed.
I am thinking of implementing it in CreateProperty function like this
public class CustomContractResolver : DefaultContractResolver
{
public static readonly CustomContractResolver Instance = new CustomContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if(property.Writable||????????????)return property;
else ?????
}
}
Is there a way to check if json Attribute was set on a property ([JsonProperty]) but not [JsonIgnore]?
Thanks.
You can implement your resolver like this:
public class CustomContractResolver : DefaultContractResolver
{
public static readonly CustomContractResolver Instance = new CustomContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
// Ignore get-only properties that do not have a [JsonProperty] attribute
if (!property.Writable && member.GetCustomAttribute<JsonPropertyAttribute>() == null)
property.Ignored = true;
return property;
}
}
Fiddle: https://dotnetfiddle.net/4oi04N

Using JSON.net, how do I prevent serializing properties of a derived class, when used in a base class context?

Given a data model:
[DataContract]
public class Parent
{
[DataMember]
public IEnumerable<ChildId> Children { get; set; }
}
[DataContract]
public class ChildId
{
[DataMember]
public string Id { get; set; }
}
[DataContract]
public class ChildDetail : ChildId
{
[DataMember]
public string Name { get; set; }
}
For implementation convenience reasons, there are times when the ChildId objects on the Parent are in fact ChildDetail objects. When I use JSON.net to serialize the Parent, they are written out with all of the ChildDetail properties.
Is there any way to instruct JSON.net (or any other JSON serializer, I'm not far enough into the project to be committed to one) to ignore derived class properties when serializing as a base class?
EDIT: It is important that when I serialize the derived class directly that I'm able to produce all the properties. I only want to inhibit the polymorphism in the Parent object.
I use a custom Contract Resolver to limit which of my properties to serialize. This might point you in the right direction.
e.g.
/// <summary>
/// json.net serializes ALL properties of a class by default
/// this class will tell json.net to only serialize properties if they MATCH
/// the list of valid columns passed through the querystring to criteria object
/// </summary>
public class CriteriaContractResolver<T> : DefaultContractResolver
{
List<string> _properties;
public CriteriaContractResolver(List<string> properties)
{
_properties = properties
}
protected override IList<JsonProperty> CreateProperties(
JsonObjectContract contract)
{
IList<JsonProperty> filtered = new List<JsonProperty>();
foreach (JsonProperty p in base.CreateProperties(contract))
if(_properties.Contains(p.PropertyName))
filtered.Add(p);
return filtered;
}
}
In the override IList function, you could use reflection to populate the list with only the parent properties perhaps.
Contract resolver is applied to your json.net serializer. This example is from an asp.net mvc app.
JsonNetResult result = new JsonNetResult();
result.Formatting = Formatting.Indented;
result.SerializerSettings.ContractResolver =
new CriteriaContractResolver<T>(Criteria);
I had the exact same problem and looked up how to build the ContractResolver I was actually looking for and that better answer this question. This only serializes the Properties of the Type T you actually want to serialize, but with this example you can also easily build similar approaches:
public class TypeOnlyContractResolver<T> : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = instance => property.DeclaringType == typeof (T);
return property;
}
}
Having encountered a similar problem, this is the ContractResolver I came up with:
public class StrictTypeContractResolver : DefaultContractResolver
{
private readonly Type _targetType;
public StrictTypeContractResolver( Type targetType ) => _targetType = targetType;
protected override IList<JsonProperty> CreateProperties( Type type, MemberSerialization memberSerialization )
=> base.CreateProperties
(
_targetType.IsAssignableFrom( type ) ? _targetType : type,
memberSerialization
);
}
It cuts off only the properties of targetType's descendants, without affecting the properties of its base classes or of other types that targetType's properties might reference. Which, depending on your needs, may or may not be an improvement over the other answers provided here at the time.
Check out the answers in this similar thread, particularly the IgnorableSerializerContractResolver in my answer and the nicer lambda version
Usage:
var jsonResolver = new IgnorableSerializerContractResolver();
// ignore single property
jsonResolver.Ignore(typeof(Company), "WebSites");
// ignore single datatype
jsonResolver.Ignore(typeof(System.Data.Objects.DataClasses.EntityObject));
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };
I have not used JSON.Net in particular so not positive this will help you. If JSON.Net derives itself from the .Net serialization system then you should be able to add the [NonSerialized] attribute to your properties you do now wish to be serialized in the base class. When you call the serialize methods on the base class, serialization should then skip those elements.
Haven't compared performance implications, but this is a working solution as well, and works with nested/referenced objects as well.
Derived d = new Derived();
string jsonStringD = JsonConvert.SerializeObject(d);
Base b = new Base();
JsonConvert.PopulateObject(jsonStringD, b);
string jsonStringB = JsonConvert.SerializeObject(b);

Categories