Data Annotation Validation returns wrong result - c#

Here is my entire program. I expect it to return false but the result is true. Do I expect wrong result or I do something wrong here?
using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel.DataAnnotations;
public class Program
{
public static void Main()
{
var c = new Ax(){Id = 1000, Name = "A"};
//c.Name = null; // when un-comment this, result as expected
var context = new ValidationContext(c);
var isValid = Validator.TryValidateObject(c, context, null);
Console.WriteLine(isValid);
}
public class Ax
{
[Range(1,100)] // I expect this to cause failed validation
public int Id{get; set;}
[Required]
public string Name { get; set; }
}
}
Result: true

You are using this method:
Validator.TryValidateObject(Object, ValidationContext, ICollection<ValidationResult>)
This method evaluates each ValidationAttribute instance that is
attached to the object type. It also checks whether each property that
is marked with RequiredAttribute is provided. It does not recursively
validate the property values of the object.
You should use the other overload and pass true as third parameter:
Validator.TryValidateObject (Object, 
ValidationContext, 
ICollection<ValidationResult>, Boolean)

Related

Get raw value of property attribute using Reflection

I need to mark some properties as passwords so that they could be automatically screened. I found a standard attribute for that:
[PasswordPropertyText]
public string ThePassword { get; set; }
Following method checks if the attribute is there:
private static bool _isPassword(PropertyInfo p)
{
PasswordPropertyTextAttribute passProp = (PasswordPropertyTextAttribute)p.GetCustomAttribute(typeof(PasswordPropertyTextAttribute));
return (passProp != null); // Additional condition should go here
}
Now I would like to have my own logic here:
[PasswordPropertyText] should result in true.
[PasswordPropertyText(true)] should result in true.
[PasswordPropertyText(false)] should result in false.
but the default value of PasswordPropertyTextAttribute.Password is false when the argument is omitted.
Is there any way to get the raw attribute value?
Since attribute constructor information are stored as metadata as well you can get the required information by calling the GetCustomAttributesData method. Have a look at this simple example:
class Program
{
[PasswordPropertyText]
public string Password1 { get; set; }
[PasswordPropertyText(true)]
public string Password2 { get; set; }
[PasswordPropertyText(false)]
public string Password3 { get; set; }
static void Main(string[] args)
{
var props = typeof(Program).GetProperties();
foreach(var prop in props)
{
var attributeData = prop.GetCustomAttributesData().First(x => x.AttributeType == typeof(PasswordPropertyTextAttribute));
Console.WriteLine($"{prop.Name}: {(attributeData.ConstructorArguments.Cast<CustomAttributeTypedArgument?>().FirstOrDefault()?.Value ?? true)}");
}
Console.ReadLine();
}
}
Output:
Password1: True
Password2: True
Password3: False
What you're describing cannot be done using reflection.
Reflection is not looking at the code as it was written: it is looking at the model that results from that code. Since the default constructor for PasswordPropertyTextAttribute passes a false value to another constructor, the model that it produces is indistinguishable from using [PasswordPropertyText(false)].
If you want different behavior from the built-in attribute, I'd recommend creating your own attribute that has the behavior you're looking for instead.

Newtonsoft.Json: is it possible to throw error if property not present in output class? [duplicate]

I'm trying to deserialize some JSON objects using Json.NET. I've found however that when I deserialize an object that doesn't have the properties I'm looking for that no error is thrown up but a default value is returned for the properties when I access them. It's important that I'm able to detect when I've deserialized the wrong type of object. Example code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Json_Fail_Test
{
class Program
{
[JsonObject(MemberSerialization.OptOut)]
private class MyJsonObjView
{
[JsonProperty("MyJsonInt")]
public int MyJsonInt { get; set; }
}
const string correctData = #"
{
'MyJsonInt': 42
}";
const string wrongData = #"
{
'SomeOtherProperty': 'fbe8c20b'
}";
static void Main(string[] args)
{
var goodObj = JsonConvert.DeserializeObject<MyJsonObjView>(correctData);
System.Console.Out.WriteLine(goodObj.MyJsonInt.ToString());
var badObj = JsonConvert.DeserializeObject<MyJsonObjView>(wrongData);
System.Console.Out.WriteLine(badObj.MyJsonInt.ToString());
}
}
}
The output of this program is:
42
0
I would prefer an exception be thrown to failing silently. Short of that is there a way to detect if the serialization failed to find a parameter?
I know I can parse the data with a Json object and then check for the parameter with a key value lookup but the codebase I'm in uses the pattern above and I'd like keep that consistent if it's possible.
The Json.Net serializer has a MissingMemberHandling setting which you can set to Error. (The default is Ignore.) This will cause the serializer to throw a JsonSerializationException during deserialization whenever it encounters a JSON property for which there is no corresponding property in the target class.
static void Main(string[] args)
{
try
{
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MissingMemberHandling = MissingMemberHandling.Error;
var goodObj = JsonConvert.DeserializeObject<MyJsonObjView>(correctData, settings);
System.Console.Out.WriteLine(goodObj.MyJsonInt.ToString());
var badObj = JsonConvert.DeserializeObject<MyJsonObjView>(wrongData, settings);
System.Console.Out.WriteLine(badObj.MyJsonInt.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().Name + ": " + ex.Message);
}
}
Result:
42
JsonSerializationException: Could not find member 'SomeOtherProperty' on object
of type 'MyJsonObjView'. Path 'SomeOtherProperty', line 3, position 33.
See: MissingMemberHandling setting.
Just add [JsonProperty(Required = Required.Always)] to the required properties and it'll throw exception if the property is not there while deserializing.
[JsonProperty(Required = Required.Always)]
public int MyJsonInt { get; set; }
Put the following attribute on required properties:
[DataMember(IsRequired = true)]
If the member is not present, it will throw a Newtonsoft.Json.JsonSerializationException.
As Brian suggested below, you will also need this attribute on your class:
[DataContract]
As #dbc tells in comments:
At deserialization:
If your Model has a property that your JSON does not, and you want that to be an error, use [JsonProperty(Required = Required.Always)].
At serialization:
If your JSON has a property that your Model does not, and you want that to be an error, use MissingMemberHandling = MissingMemberHandling.Error.
also using [DataMember(IsRequired = true)] for error at deserialization is true when proeprty type is nullable.
Just define your members in your definition class with a question mark '?' int?:
private class MyJsonObjView
{
[JsonProperty("MyJsonInt")]
public int? MyJsonInt { get; set; }
}
When it is not initialized, it will just be null, otherwise it will be a valid value. This allows you to have settings optional and evaluate them on a per-setting basis.

Unable to make ShouldSerialize pattern work with XmlSerializer

I have been able to use ShouldSerializeProperty pattern with XmlSerializer to ignore a property of type ICollection<>. Below is the sample code. It works if I use XmlIgnore on ListOfTestClassB property but I want to achieve the same functionality utilizing ShouldSerialize pattern. If I run the code below I get the 'System.InvalidOperationException' saying:
Cannot serialize member ConsoleApplication3.TestClassA.ListOfTestClassB of type System.Collections.Generic.ICollection`1[[ConsoleApplication3.TestClassB, ConsoleApplication3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] because it is an interface.
Can anyone highlight what am I missing?
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication3
{
[Serializable]
public class TestClassA
{
public int A { get; set; }
public int B { get; set; }
public string C { get; set; }
public TestClassB TestClass { get; set; }
public virtual ICollection<TestClassB> ListOfTestClassB { get; set; }
public bool ShouldSerializeListOfTestClassB()
{
return false;
}
}
[Serializable]
public class TestClassB
{
public int Int32 { get; set; }
public string String { get; set; }
}
class Program
{
static object GetObject()
{
return new TestClassA { A = 1, B = 2, C = "test class a", TestClass = new TestClassB { Int32 = 11, String = "test class b"} };
}
static void Main(string[] args)
{
var result = new StringBuilder();
var entity = GetObject();
var ser = new XmlSerializer(entity.GetType());
var settings = new XmlWriterSettings { OmitXmlDeclaration = true };
using (var stream = new MemoryStream())
{
// make a copy of the entity - we do not want to serialize ZIP file !
var formatter = new BinaryFormatter();
formatter.Serialize(stream, entity);
stream.Position = 0;
entity = formatter.Deserialize(stream);
}
// serialize object
ser.Serialize(XmlWriter.Create(result, settings), entity);
Console.WriteLine(result.ToString());
Console.ReadLine();
}
}
}
This is where the check happens: http://referencesource.microsoft.com/#System.Xml/System/Xml/Serialization/Models.cs,249.
As you can see, it doesn't call the ShouldSerialize* method until after it's already identified fields/properties to serialize. So, your ListOfTestClassB has to be serializable, or it must be decorated with [XmlIgnore]. In order to be serializable your property must be of a concrete type that has the [Serializable] attribute applied.
There's a workaround if you can't modify the class. One of the overloads of the XmlSerializer.Serialize(...) method accepts an overrides object. I've created a simple example below:
[Serializable]
public class Foo
{
public IList<int> Numbers { get; set; }
public string TheNumber { get; set; }
}
class Program
{
private static void Main(string[] args)
{
var attributes = new XmlAttributes
{
XmlIgnore = true
};
var overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Foo), "Numbers", attributes);
var serializer = new XmlSerializer(typeof(Foo), overrides);
// the rest of this is for demo purposes.
// the code above is whats important
//
using (var ms = new MemoryStream())
using (var reader = new StreamReader(ms))
{
serializer.Serialize(ms, new Foo() { TheNumber = "5" });
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
Debug.WriteLine(reader.ReadToEnd());
}
}
}
This generates:
<?xml version="1.0"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TheNumber>5</TheNumber>
</Foo>
As you can see, I'm dynamically adding the XmlIgnore attribute to my Numbers element, and the serializer consequently ignores it. :) You should have no trouble adapting this to your own code.
Note: As noted by dbc, it's important to cache this serializer and re-use it, otherwise you're going to have a lot of memory leaks. You can keep a static reference to it, or use a hashtable to store different serializers for different types.
To increase performance, the XML serialization infrastructure dynamically generates assemblies to serialize and deserialize specified types. The infrastructure finds and reuses those assemblies. This behavior occurs only when using the following constructors:
XmlSerializer.XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type, String)
If you use any of the other constructors, multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance. The easiest solution is to use one of the previously mentioned two constructors. Otherwise, you must cache the assemblies in a Hashtable, as shown in the following example.
If you use XmlIgnore, then it will not care about that property at all. If you use ShouldSerialize, it doesn't know until runtime whether it should serialize that type, so it must be able to. In this case, the type you're trying to serialize must be a concrete class. Try using List<TestClassB>.

Buddy class for validation not working

I have the current piece of code :
using System;
using System.ComponentModel.DataAnnotations;
using Boo.Lang;
class Program
{
public partial class Foo
{
public string SomeField { get; set; }
}
[MetadataType(typeof(FooMetadata))]
public partial class Foo
{
}
public class FooMetadata
{
[Required(ErrorMessage = "Some Field is required")]
public string SomeField { get; set; }
}
static void Main()
{
var sample = new Foo { SomeField = null };
var context = new ValidationContext(sample);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(sample, context, results);
if (!isValid)
{
foreach (var validationResult in results)
{
Console.WriteLine(validationResult.ErrorMessage);
}
}
else
Console.WriteLine("sample is valid");
Console.ReadLine();
}
}
It says that sample is valid although it's not the case. Am I missing something ?
Validator.ValidateObject goes through all validation attributes and throws ValidationException for the first one that fails. I don't think one can get the list of all properties that failed this way.
The MSDN documentation is a bit confusing here, it says:
The ValidateObject method evaluates each ValidationAttribute attribute
that is associated with the object type. If validateAllProperties is
set to true, the method validates the property values of the object.
TryValidateObject method manual continues:
It also checks whether each property that is marked with
RequiredAttribute is provided.
I confirm that it always checks all the Required attributes, regardless of validateAllProperties. But if you have, for example, a Range validation attribute it will only check it when validateAllProperties is true (because this is when it validates the property value).
A bit confusing, but as you noticed, TryValidateObject shows all the failed properties.

Detect if deserialized object is missing a field with the JsonConvert class in Json.NET

I'm trying to deserialize some JSON objects using Json.NET. I've found however that when I deserialize an object that doesn't have the properties I'm looking for that no error is thrown up but a default value is returned for the properties when I access them. It's important that I'm able to detect when I've deserialized the wrong type of object. Example code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Json_Fail_Test
{
class Program
{
[JsonObject(MemberSerialization.OptOut)]
private class MyJsonObjView
{
[JsonProperty("MyJsonInt")]
public int MyJsonInt { get; set; }
}
const string correctData = #"
{
'MyJsonInt': 42
}";
const string wrongData = #"
{
'SomeOtherProperty': 'fbe8c20b'
}";
static void Main(string[] args)
{
var goodObj = JsonConvert.DeserializeObject<MyJsonObjView>(correctData);
System.Console.Out.WriteLine(goodObj.MyJsonInt.ToString());
var badObj = JsonConvert.DeserializeObject<MyJsonObjView>(wrongData);
System.Console.Out.WriteLine(badObj.MyJsonInt.ToString());
}
}
}
The output of this program is:
42
0
I would prefer an exception be thrown to failing silently. Short of that is there a way to detect if the serialization failed to find a parameter?
I know I can parse the data with a Json object and then check for the parameter with a key value lookup but the codebase I'm in uses the pattern above and I'd like keep that consistent if it's possible.
The Json.Net serializer has a MissingMemberHandling setting which you can set to Error. (The default is Ignore.) This will cause the serializer to throw a JsonSerializationException during deserialization whenever it encounters a JSON property for which there is no corresponding property in the target class.
static void Main(string[] args)
{
try
{
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MissingMemberHandling = MissingMemberHandling.Error;
var goodObj = JsonConvert.DeserializeObject<MyJsonObjView>(correctData, settings);
System.Console.Out.WriteLine(goodObj.MyJsonInt.ToString());
var badObj = JsonConvert.DeserializeObject<MyJsonObjView>(wrongData, settings);
System.Console.Out.WriteLine(badObj.MyJsonInt.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().Name + ": " + ex.Message);
}
}
Result:
42
JsonSerializationException: Could not find member 'SomeOtherProperty' on object
of type 'MyJsonObjView'. Path 'SomeOtherProperty', line 3, position 33.
See: MissingMemberHandling setting.
Just add [JsonProperty(Required = Required.Always)] to the required properties and it'll throw exception if the property is not there while deserializing.
[JsonProperty(Required = Required.Always)]
public int MyJsonInt { get; set; }
Put the following attribute on required properties:
[DataMember(IsRequired = true)]
If the member is not present, it will throw a Newtonsoft.Json.JsonSerializationException.
As Brian suggested below, you will also need this attribute on your class:
[DataContract]
As #dbc tells in comments:
At deserialization:
If your Model has a property that your JSON does not, and you want that to be an error, use [JsonProperty(Required = Required.Always)].
At serialization:
If your JSON has a property that your Model does not, and you want that to be an error, use MissingMemberHandling = MissingMemberHandling.Error.
also using [DataMember(IsRequired = true)] for error at deserialization is true when proeprty type is nullable.
Just define your members in your definition class with a question mark '?' int?:
private class MyJsonObjView
{
[JsonProperty("MyJsonInt")]
public int? MyJsonInt { get; set; }
}
When it is not initialized, it will just be null, otherwise it will be a valid value. This allows you to have settings optional and evaluate them on a per-setting basis.

Categories