I'd like to serialize an Exception using a custom resolver.
Here's an example custom resolver - which should serialize only specified properties:
public class IncludeSpecifiedPropsResolver : DefaultContractResolver
{
string[] propsToSerialize = null;
public IncludeSpecifiedPropsResolver(params string[] propsToSerialize)
{
this.propsToSerialize = propsToSerialize;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var allProps = base.CreateProperties(type, memberSerialization);
if (propsToSerialize == null || propsToSerialize.Length == 0)
{
return allProps;
}
return allProps.Where(p => propsToSerialize.Contains(p.PropertyName)).ToList();
}
}
Example usage:
string test = JsonConvert.SerializeObject(new Exception("Something went wrong"), new JsonSerializerSettings()
{
ContractResolver = new IncludeSpecifiedPropsResolver("Message")
});
However, CreateProperties is ignored.
What else is missing so that the custom resolver would work as expected?
The problem here is that Exception implements the ISerializable interface, which has special handling in the DefaultContractResolver: the code path does not go through CreateProperties(). You can override this behavior by setting the IgnoreSerializableInterface property to true in the constructor of your resolver. If you do this, your code will work as intended.
public IncludeSpecifiedPropsResolver(params string[] propsToSerialize)
{
this.propsToSerialize = propsToSerialize;
IgnoreSerializableInterface = true;
}
Working demo: https://dotnetfiddle.net/DNhwaH
Related
I have an object that needs to be serialized in such a way that both null and "whitespace" (empty or just spaces) values are not serialized. I don't control the object itself and therefore can't set attributes, but I know that all the properties are strings. Setting NullValueHandling to Ignore obviously only gets me part of the way to the solution.
It "seems" (as best I understand) like what I need to do is create a custom DefaultContractResolver but I haven't come up with a solution that works. Here are a couple failed attempts, for reference, that throw no exceptions but have no obvious effect on the serialization either:
public class NoNullWhiteSpaceResolver : DefaultContractResolver
{
public static readonly NoNullWhiteSpaceResolver Instance = new NoNullWhiteSpaceResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
/* this doesn't work either
if (property.ValueProvider.GetValue(member) == null ||
(property.PropertyType == typeof(string) &&
string.IsNullOrWhiteSpace((string)property.ValueProvider.GetValue(member))))
{
property.ShouldSerialize = i => false;
}*/
if (property.PropertyType == typeof(string))
{
property.ShouldSerialize =
instance =>
{
try
{
string s = (string) instance;
bool shouldSkip = string.IsNullOrWhiteSpace(s);
return !string.IsNullOrWhiteSpace(s);
}
catch
{
return true;
}
};
}
return property;
}
}
I'm implementing the resolver by
string str = JsonConvert.SerializeObject(obj, new JsonSerializerSettings
{
Formatting = Formatting.None;
ContractResolver = new NoNullWhiteSpaceResolver();
});
Maybe I'm going about this backward but I appreciate any insights people have. I've worked around the issue by using an extension method/reflection to iterate over the properties of the object and setting the value to null if it's "nullorwhitespace" and then using the standard NullValueHandling but I'm hoping I can find a way to configure all of this in the serialization.
This seems to work:
public class NoNullWhiteSpaceResolver : DefaultContractResolver {
public static readonly NoNullWhiteSpaceResolver Instance = new NoNullWhiteSpaceResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.PropertyType == typeof(string)) {
property.ShouldSerialize =
instance => {
try {
var rawValue = property.ValueProvider.GetValue(instance);
if (rawValue == null) {
return false;
}
string stringValue = property.ValueProvider.GetValue(instance).ToString();
return !string.IsNullOrWhiteSpace(stringValue);
}
catch {
return true;
}
};
}
return property;
}
}
Using this test class:
public class TestClass {
public string WhiteSpace => " ";
public string Null = null;
public string Empty = string.Empty;
public string Value = "value";
}
This is the output:
{"Value":"value"}
A)
I have a DLL library include a class like the bellow code:
public class Thing
{
// OTHER_PROPERTIES
//..........................
[JsonProperty(Required = Required.Always)]
public bool IsBook { get; set; }
}
Assumption: I have not access to the source of the Thing class.
B)
And I have a JSON like this:
{
OTHER_PROPERTIES
}
Note: It has not a pair of name-value for IsBook
Assumption: I wont change the JSON to include IsBook in it.
C)
If I run JsonConvert.DeserializeObject<Thing>(json); then I get the following error:
Error: Required property not found in json ....
Question:
How can I write a custom code to solve the above error by observing the assumptions of the problem?
(please read the above assumptions again before writing your answer.)
You can use custom contract resolver to achieve that. For example:
public class Thing
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty(Required = Required.Always)]
public bool IsBook { get; set; }
}
class NeverRequiredContractResolver : DefaultContractResolver {
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
var prop = base.CreateProperty(member, memberSerialization);
prop.Required = Required.Default;
return prop;
}
}
And then:
var test = JsonConvert.DeserializeObject<Thing>("{\"Name\":\"some name\"}",
new JsonSerializerSettings {
ContractResolver = new NeverRequiredContractResolver()
});
Of course you can assign resolver globally and not pass it to every DeserializeObject:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
ContractResolver = new NeverRequiredContractResolver()
};
var test = JsonConvert.DeserializeObject<Thing>("{\"Name\":\"some name\"}");
you can make only specific properties of specific classes non-required (and not all of them like in example above):
class CustomPropertyContractResolver : DefaultContractResolver {
private readonly Action<MemberInfo, JsonProperty> _propFixup;
public CustomPropertyContractResolver(Action<MemberInfo, JsonProperty> propFixup) {
_propFixup = propFixup;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
var prop = base.CreateProperty(member, memberSerialization);
_propFixup?.Invoke(member , prop);
return prop;
}
}
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
ContractResolver = new CustomPropertyContractResolver((member, prop) => {
if (member.DeclaringType == typeof(Thing) && member.Name == "IsBook") {
prop.Required = Required.Default;
}
})
};
var test = JsonConvert.DeserializeObject<Thing>("{\"Name\":\"some name\"}");
and overall adjust example above however you wish for your specific use case.
Sometimes I need to suppress output of "$type" properties by Json.NET even when specified by JsonPropertyAttribute.ItemTypeNameHandling. How can this be done?
My root class looks like below:
public class DomainResource
{
[JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)]
public List<Extension> Extensions { get; set; }
}
And in addition I have a class hierarchy for Extension such as the following:
public class Extension
{
readonly string url;
public string Url { get { return url; } }
public Extension(string url)
{
this.url = url;
}
}
public class IntegerExtension : Extension
{
public IntegerExtension(string url) : base(url) { }
[JsonProperty("ValueInteger")]
public int Value { get; set; }
}
I want to ignore ItemTypeNameHandling in certain scenarios during serialization, but I am not able to find a way to do that.
I tried setting JsonSerializerSettings with TypeNameHandling.None as input for jsonconvert when I do not want "$type" properties using the code below:
public static string SerializeObject(object value)
{
JsonSerializerSettings jsonSettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.None,
};
jsonSettings.Converters.Add(new StringEnumConverter
{
CamelCaseText = true
});
return JsonConvert.SerializeObject(value, Formatting.None, jsonSettings);
}
And then use it as follows:
var res = new DomainResource();
res.Extensions = new List<Extension>();
res.Extensions.Add(new IntegerExtension("ewwer"){Value = 3});
var x = CustomJsonConvert.SerializeObject(res);
My desired JSON is:
{"extensions":[{"valueInteger":3,"url":"ewwer"}]}
But instead it contains "$type" properties as shown below:
{"extensions":[{"$type":"DicomtoJsonConverter.IntegerExtension,
DicomtoJsonConverter","valueInteger":3,"url":"ewwer"}]}
How can I suppress output of "$type" properties without changing DomainResource class?
You can use a custom ContractResolver to suppress output of type information even when specified by JsonPropertyAttribute.TypeNameHandling, JsonPropertyAttribute.ItemTypeNameHandling or JsonContainerAttribute.ItemTypeNameHandling. First, define the following contract resolver:
public class NoTypeNameHandlingContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
// Suppress JsonPropertyAttribute.TypeNameHandling
property.TypeNameHandling = null;
// Suppress JsonPropertyAttribute.ItemTypeNameHandling
property.ItemTypeNameHandling = null;
return property;
}
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
if (contract is JsonContainerContract)
{
// Suppress JsonContainerAttribute.ItemTypeNameHandling
((JsonContainerContract)contract).ItemTypeNameHandling = null;
}
return contract;
}
}
Then, modify CustomJsonConvert.SerializeObject() as follows:
public static class CustomJsonConvert
{
// You may want to cache the contract resolver for best performance, see
// https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information
static readonly JsonSerializerSettings jsonSettings;
static CustomJsonConvert()
{
jsonSettings = new JsonSerializerSettings
{
ContractResolver = new NoTypeNameHandlingContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy
{
// These are the settings used by CamelCasePropertyNamesContractResolver by default.
// Change them if this is not what you want.
OverrideSpecifiedNames = true,
ProcessDictionaryKeys = true,
},
},
NullValueHandling = NullValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.None,
Converters = { new StringEnumConverter { CamelCaseText = true } },
};
}
public static string SerializeObject(object value)
{
return JsonConvert.SerializeObject(value, Formatting.None, jsonSettings);
}
}
If you are using a version of Json.NET that predates 9.0.1 you will need to subclass CamelCasePropertyNamesContractResolver rather than subclassing DefaultContractResolver since NamingStrategy was introduced in that release.
This question already has answers here:
Can I optionally turn off the JsonIgnore attribute at runtime?
(3 answers)
Closed 6 years ago.
Is there a way I can ignore Json.NET's [JsonIgnore] attribute on a class that I don't have permission to modify/extend?
public sealed class CannotModify
{
public int Keep { get; set; }
// I want to ignore this attribute (and acknowledge the property)
[JsonIgnore]
public int Ignore { get; set; }
}
I need all properties in this class to be serialized/deserialized. I've tried subclassing Json.NET's DefaultContractResolver class and overriding what looks to be the relevant method:
public class JsonIgnoreAttributeIgnorerContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
// Serialize all the properties
property.ShouldSerialize = _ => true;
return property;
}
}
but the attribute on the original class seems to always win:
public static void Serialize()
{
string serialized = JsonConvert.SerializeObject(
new CannotModify { Keep = 1, Ignore = 2 },
new JsonSerializerSettings { ContractResolver = new JsonIgnoreAttributeIgnorerContractResolver() });
// Actual: {"Keep":1}
// Desired: {"Keep":1,"Ignore":2}
}
I dug deeper, and found an interface called IAttributeProvider that can be set (it had a value of "Ignore" for the Ignore property, so that was a clue this might be something that needs changing):
...
property.ShouldSerialize = _ => true;
property.AttributeProvider = new IgnoreAllAttributesProvider();
...
public class IgnoreAllAttributesProvider : IAttributeProvider
{
public IList<Attribute> GetAttributes(bool inherit)
{
throw new NotImplementedException();
}
public IList<Attribute> GetAttributes(Type attributeType, bool inherit)
{
throw new NotImplementedException();
}
}
But the code isn't ever hit.
You were on the right track, you only missed the property.Ignored serialization option.
Change your contract to the following
public class JsonIgnoreAttributeIgnorerContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.Ignored = false; // Here is the magic
return property;
}
}
I have an API that returns a big list of car features.... all are either bool or ints... and basically I only want to display the ones that return true values or >0 for the ints.
I am using JSON.net so that I san use the ShouldSerialize() property to determine if I should serialize the property based upon its value and my code looks like this:
public class Features
{
public bool ABS { get; set; }
public bool ShouldSerializeABS()
{
// don't serialize the ABS property if ABS is false
return (ABS != false);
}
public bool Immobiliser { get; set; }
public bool ShouldSerializeImmobiliser ()
{
// don't serialize the Immobiliser property if Immobiliser is false
return (Immobiliser != false);
}
public int BHP { get; set; }
public bool ShouldSerializeBHP ()
{
// don't serialize the BHP property if BHP is false
return (BHP != 0);
}
//..... etc
}
This works great and gives me the results I am after, however I was just wondering if there is a way to re-factor this so that my class does not become cluttered with all the ShouldSerialize() properties?
I have been looking into CopyConditional properties with IContractResolver on http://james.newtonking.com/projects/json/help/index.html?topic=html/ConditionalProperties.htm and looks like it might be possible to use IContractResolver for such a purpose, but I still seem to end up with lots of code that does not seem to re-factor out
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(Features) && property.PropertyName == "ABS")
{
property.ShouldSerialize =
instance =>
{
Features e = (Features)instance;
return e.ABS != false;
};
}
if (property.DeclaringType == typeof(Features) && property.PropertyName == "Immobiliser")
{
property.ShouldSerialize =
instance =>
{
Features e = (Features)instance;
return e.Immobiliser != false;
};
}
return property;
}
}
and this method using the ShouldSerializeContractResolver does not seem to remove the property from the class if it is false... any help is greatly appreciated
It sounds like what you are trying to accomplish by writing all these ShouldSerialize() methods can be accomplished by just changing the DefaultValueHandling setting on the serializer to Ignore. This will cause any values that are equal to their default values (false for bool, 0 for int) not to be serialized.
JsonSerializerSettings jsonSettings = new JsonSerializerSettings();
jsonSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
string json = JsonConvert.SerializeObject(yourObject, jsonSettings);
If you're using Web API, then you can access the settings of the Json.NET serializer via the Register method of the WebApiConfig class (in the App_Start folder).
JsonSerializerSettings settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.DefaultValueHandling = DefaultValueHandling.Ignore;