ObjectValidation-CountryValidator 1.2.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package ObjectValidation-CountryValidator --version 1.2.1                
NuGet\Install-Package ObjectValidation-CountryValidator -Version 1.2.1                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ObjectValidation-CountryValidator" Version="1.2.1" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add ObjectValidation-CountryValidator --version 1.2.1                
#r "nuget: ObjectValidation-CountryValidator, 1.2.1"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install ObjectValidation-CountryValidator as a Cake Addin
#addin nuget:?package=ObjectValidation-CountryValidator&version=1.2.1

// Install ObjectValidation-CountryValidator as a Cake Tool
#tool nuget:?package=ObjectValidation-CountryValidator&version=1.2.1                

ObjectValidation

This library contains some object validation helper:

  • (Try)ValidateObject extension for validating any object type
  • NoValidationAttribute for excluding types or properties from validation
  • ObjectValidationException for handling validation errors
  • Validation events
  • CountLimitAttribute for limiting dictionary, list, array, collection and enumerable lengths
  • ValidatableObject for implementing automatic validated types
  • Deep dictionary and list key/value validation
  • ICountable and ILongCountable interfaces for count limitation
  • ItemNullableAttribute for (non-)nullable dictionary or list item validation
  • SWIFT validation attributes for ISO 13616 IBAN and ISO 9362 BIC (SWIFT codes)
  • ABA RTN validation attributes (MICR and fraction formats are supported)
  • IP address validation attribute for IPv4 and IPv6
  • Country ISO 3166-1 alpha-2 code validation
  • Currency ISO 4217 code validation
  • Money amount validation
  • Luhn checksum validation
  • XRechnung routing validation
  • European VAT ID validation
  • XML validation
  • Conditional value requirements
  • Enumeration value validation

It has been developed with the goal to offer an automatted deep object validation with support for deep dictionaries and lists contents, too.

All public instance properties with a public getter method will be validated, unless there's a NoValidationAttribute present. You can use any ValidationAttribute to define property value constraints.

Also the IValidatableObject interface is supported by using the Validator.TryValidateObject method with the validateAllProperties parameter having the value true (the IValidationObject validation method will be called in a second round). Using this interface you can implement some more advanced custom validations, if required.

NOTE: The ObjectValidation doesn't replace the classic built in .NET object validation - it extends the existing routines with deep validation and more. Btw. you don't need to use the ObjectValidation methods, if you only want to profit from the included general validation attributes for SWIFT etc.

How to get it

The libraries are available as NuGet packages:

License

The ObjectValidation is licensed using the MIT license.

The ObjectValidation-CountryValidator extension is licensed using the Apache-2.0 license.

Additional validations

Validation Attribute
Validations by event handlers (see ValidationExtensions)
Dictionary list and enumerable count limit CountLimitAttribute
Nullable types (null values in non-nullable properties will fail) NullableAttribute
Dictionary and list key/value validation (including validating (non-)null items ItemNullableAttribute
ISO 13616 IBAN and ISO 9362 BIC (SWIFT code) validation IbanAttribute, BicAttribute
ABA RTN validation (MICR/fraction) validation AbaRtnAttribute
IP address validation IpAttribute
Country ISO 3166-1 alpha-2 code validation CountryAttribute
Currency ISO 4217 code validation CurrencyAttribute
Money amount validation AmountAttribute
Luhn checksum validation LuhnChecksumAttribute
XRechnung routing validation XRechnungRouteAttribute
European VAT ID validation EuVatIdAttribute
XML validation XmlAttribute
Conditional value requirement RequiredIfAttribute
Allowed/denied values AllowedValuesAttribute, DeniedValuesAttribute
Enumeration value (none - using the type)

Deep object validation

Nested property object value validation is supported for:

  • Dictionaries
  • Lists
  • Arrays
  • Collections
  • Enumerables
  • Any non-value types

Length/count validation

The length/count of dictionaries, lists, arrays and enumerables can be validated using the CountLimitAttribute (which may not work for enumerables, if not used trough ObjectValidation methods!).

Limiting countables

By implementing the ICountable or ILongCountable interfaces you can use the CountLimitAttribute for limiting the minimum/maximum count of an object.

null values

The object validation will generate an error, if a non-null property has a null value (or any non-null-expected value is null).

public string StringProperty { get; set; } = null!// This will generate an error

If a non-null property was initialized with null, and the code forgot to set another value, the ObjectValidation will create an error for this property.

Another feature is the validation of (non-)null dictionary or list items:

[ItemNullable]
public List<string?> NullableList { get; set; } = new() { null };// Ok

[ItemNullable]
public List<string> NonNullList { get; set; } = new() { null };// Ok

public List<string?> NullableList2 { get; set; } = new() { null };// This will generate an error

public List<string> NonNullList2 { get; set; } = new() { null! };// This will generate an error

Using the ItemNullableAttribute you can define, if the dictionary value or the list item may be null.

General examples

// Only determine if valid/invalid
if(!anyObject1.TryValidateObject())
{
    // Object invalid
}

// Get validation messages in a new list
if(!anyObject2.TryValidateObject(out List<ValidationResult> results))
{
    // Object invalid
}

// Get validation messages in an existing list
results.Clear();
if(!anyObject3.TryValidateObject(results)) 
{
    // Object invalid
}

// Check for exceptions during validation
if(results.HasValidationException())
{
    Console.WriteLine(results.First(r => r.ErrorMessage?.StartsWith(ValidationExtensions.VALIDATION_EXCEPTION_PREFIX)).ErrorMessage);
}

// Fluent syntax without the "Try" method name prefix (will throw a ObjectValidationException on error)
return anyObject4.ValidateObject();

NOTE: Any ValidationResult will let the validation fail in total!

In case you want to forward error messages to a peer, you may want to exclude exceptions and their stack trace. You can use the HasValidationException method and a LINQ expression like the one from the example to filter out any exception in the results.

ValidatableObject types may be validated automatic (depending on the app context), because they implement the IValidatableObject interface. The ValidatableObject executes the ObjectValidation method internal. For using the ValidatableObject base class, simply extend from it, and call the base constructor from your constructor methods.

TIP: You should use the ValidatableObject base type, if possible! By only implementing the IValidatableObject interface your type may be validated automatic, but not by the ObjectValidation library!

In case you can't extend from ValidatableObject, you can implement the IValidatableObject and IObjectValidatable interfaces like this:

public class YourType : AnyBaseType, IObjectValidatable
{
    ...

    // Implement IValidatableObject
    IEnumerable<ValidationResult> IValidatableObject.Validate(ValidationContext context)
        => ValidatableObject.ObjectValidatable(this);
}

Conditional value requirement

If a property value is required in case another property has a specified value:

[Bic]
public string BIC { get; set; }

[Iban, RequiredIf(nameof(ABA), RequiredIfNull = true)]
public string? IBAN { get; set; }

[AbaRtn, RequiredIf(nameof(IBAN), RequiredIfNull = true)]
public string? ABA { get; set; }

In this example, a BIC is required in combination with an IBAN or an ABA RTN. The RequiredIfAttribute.RequiredIfNull is set to true to check for IBAN and ABA, if the other property value is null: In case ABA is null, IBAN is required. In case IBAN is null, ABA is required.

Another example:

public bool DeliveryAddress { get; set; }

[RequiredIf(nameof(DeliveryAddress), true)]
public string? DeliveryName { get; set; }

[RequiredIf(nameof(DeliveryAddress), true)]
public string? DeliveryStreet { get; set; }

[RequiredIf(nameof(DeliveryAddress), true)]
public string? DeliveryZip { get; set; }

[RequiredIf(nameof(DeliveryAddress), true)]
public string? DeliveryCity { get; set; }

[RequiredIf(nameof(DeliveryAddress), true), Country]
public string? DeliveryCountry { get; set; }

In case the value of DeliveryAddress is true, all delivery address properties are required to have a value.

NOTE: Because the validation attribute needs to access the validated object properties, it's required to work with valid vlaidation contexts, which contain the validated object instance.

Dictionary and list key/value validation

By implementing the IItemValidationAttribute interface, you can define validation attributes that are going to be applied on each key/value of a dictionary or a list. There's an almost generic ItemValidationAttribute which allows to construct with any ValidationAttribute. For the .NET built in validation attributes (most of them) there are adapting attributes, which start with Item and continue with the original attribute name. By setting a target in the ItemValidationAttribute constructor, you can define, if a validation should only be applied to a dictionary key. Examples:

[CountLimit(1, 10)]// Dictionary object validation
[ItemRequired(ItemValidationTarget.Key), ItemStringLength(100, ItemValidationTarget.Key)]// Key validation
[ItemRequired, ItemStringLength(255)]// Value validation
public Dictionary<string, string> Dict { get; }

[CountLimit(1, 10)]// List object validation
[ItemRequired, ItemStringLength(255)]// Item validation
public List<string> List { get; }

The dictionary needs to have at last one, maximum 10 key/value pairs, where the key length needs to be between 1-100 characters, and the value length needs to be 1-255 characters.

The list needs to have at last one, maximum 10 items, where each item length is limited to 1-255 characters.

Using the ItemNoValidationAttribute you can disable key/value validation for a property. In case of an enumerable value, the enumeration needs to be executed in case there's a CountLimitAttribute present.

These item validation adapters exist:

Property validation Item validation
ValidationAttribute ItemValidationAttribute
CountLimitAttribute ItemCountLimitAttribute
RequiredAttribute ItemRequiredAttribute
CompareAttribute ItemCompareAttribute
CreditCardAttribute ItemCreditCardAttribute
EmailAddressAttribute ItemEmailAddressAttribute
MaxLengthAttribute ItemMaxLengthAttribute
MinLengthAttribute ItemMinLengthAttribute
NoValidationAttribute ItemNoValidationAttribute
PhoneAttribute ItemPhoneAttribute
RangeAttribute ItemRangeAttribute
RegularExpressionAttribute ItemRegularExpressionAttribute
StringLengthAttribute ItemStringLengthAttribute
UrlAttribute ItemUrlAttribute
DataTypeAttribute ItemDataTypeAttribute
IbanAttribute ItemIbanAttribute
BicAttribute ItemBicAttribute
AbaRtnAttribute ItemAbaRtnAttribute
IpAttribute ItemIpAttribute
CountryAttribute ItemCountryAttribute
CurrencyAttribute ItemCurrencyAttribute
AmountAttribute ItemAmountAttribute
LuhnChecksumAttribute ItemLuhnChecksumAttribute
XRechnungRouteAttribute ItemXRechnungRouteAttribute
EuVatIdAttribute ItemEuVatIdAttribute
AllowedValuesAttribute ItemAllowedValuesAttribute
DeniedValuesAttribute ItemDeniedValuesAttribute
CustomValidationAttribute ItemCustomValidationAttribute

You can use the ItemNoValidationAttribute at the class level to prevent from validating and dictionary or list contents.

NOTE: By setting the ArrayLevel (starts with zero) of an ItemValidationAttribute, you can specify to use the item for the desired array level only. This enables array of array etc. item validations. Entering a deeper array level counts as recursion. The array level can be set for dictionary keys, too, if they're a dictionary or a list.

Use the ItemNullableAttribute, if the dictionary value or list item may be null (even if you wrote T? in your code, because the nullability information my not be available during validation!).

Enumeration value validation

An enumeration can be a value list or combined flags. Both variants are validated by checking if

  • the value contains undefined flags
  • the value is an undefined enumeration value

This ensures, that only defined enumeration (flag) values can be used.

Force to fail with an exception

If you set the parameter throwOnError value to true, the validation will throw a ObjectValidationException, as soon as an object was invalidated.

Validation of a property group

By giving a list of member names to validate as members parameter, you can avoid validating all properties which could be validated (f.e. you could validate only a property group).

Handling validation events

Event Description
OnObjectValidation You can perform validations before any other validations have been executed. When the event was cancelled, there won't be any following validation, and the produced result will be used.
OnObjectValidationFailed Raised, if the object validation failed. You may add additional error messages, before the validation method returns.
OnObjectPropertyValidation You can perform validations before any other validations have been executed for the property. When the event was cancelled, there won't be any following validation, and the produced result will be used for the current property. The validation will then continue with the next property.
OnObjectPropertyValidationFailed Raised, if the object validation failed. You may add additional error messages, before the validation method continues with the next property. When the event was cancelled, the following property validations will be skipped.

If the event arguments don't offer a PropertyInfo in the Property property, the event was raised for the validated object.

An event handler can set a failed state by setting the Result property of the event arguments to false.

WARNING: Do not set the value true to the Result property! An already failed state can't be deleted, unless the original result OriginalResult was true.

The object validation will fail, if there was any validation result, or the overall result is false.

Found a bug?

If the object validation doesn't work for you as expected, or you have any idea for improvements, please open an issue - I'd be glad to help and make ObjectValidation become even better! Push requests are welcome, too 😃

Good to know

Recursion protection

The object validation implements a recursion detection and won't end up in an endless loop, unless you produce an endless loop within your custom IValidatableObject validation implementation or the event handling.

You can define a maximum recursion depth in the ValidationExtensions.MaxDepth property (the default is 32). If the depth would be exceeded during an object validation, this would result in a ObjectValidationException, which would be catched as a validation error result, when not setting throwOnError to true.

WARNING: If you disable the maximum recursion depth validation, this may end up in a StackOverflowException, which will crash your application.

Maximum number of error messages

You can limit the maximum number of returned error messages in the ValidationExtensions.MaxErrors property (the default is 200). The object validation would stop, once it was detected that the number of errors exceeds this limit. Your validation result list will never contain more error messages than defined in this limit.

WARNING: Disabling the maximum number of error messages may end up in huge object lists, which could lead to memory usage problems and finally a crash of your application!

ASP.NET

ASP.NET (7) automatic validates IValidatableObject objects when unserializing for an API controller call. The ObjectValidation library will be used automatic, if you've used the ValidatableObject base class for your types, which you want to be validated automatic. Simply use it as base class for your DTO objects, and you don't need to care about validation anymore.

You should also understand the difference between NoValidateAttribute and NeverValidateAttribute: NoValidateAttribute affects the ObjectValidation validation, while NeverValidateAttribute affects all validations. So you can use the NoValidateAttribute to skip double validation by the ObjectValidation methods, if required.

Nullability

The nullability of properties can be determined using reflections, even if one returns a generic type. But if f.e. the return type is a YourType : Dictionary<string, string?>, the object validation isn't able to determine the nullability of the second generic type argument of the base type, because the nullability information will be discarded during your code compilation. For the dictionary item validation please specify the ItemNullableAttribute in that case. The same is valid for deep array validations.

More validations

Have a look at the CountryValidator project for more validations like

  • Social Security Numbers
  • Personal Identity Numbers
  • More VAT IDs
  • Tax IDs for individuals
  • Tax IDs for companies
  • ZIP codes

for many countries.

The ObjectValidation-CountryValidator packet includes references to this packet, and exports item validation attributes:

  • ItemCompanyTINAttribute
  • ItemPersonTINAttribute
  • ItemSSNAttribute
  • ItemVATAttribute
  • ItemZipCodeAttribute

NOTE: The main ObjectValidation library includes validation for European VAT IDs only. By using this packet, you can use VAT ID validation for many countries around the world.

CAUTION: Since the CountryValidator is licensed under Apache-2.0 license, I decided to license the ObjectValidation-CountryValidator under the same license:

Copyright 2023 Andreas Zimmermann, wan24.de

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Internal validation information object

An event handler can access the list of seen objects, which is used to prevent an endless recursion. The first object of that list is an IValidationInfo object, which contains some validation context information:

Property Description
Seen Seen objects list
CurrentDepth Current recursion depth
ArrayLevel Current array level

CAUTION: Please DO NOT remove or exchange this object!

Since array item validations don't call event handlers, the ArrayLevel property will alwys be 0.

Upcoming changes with .NET 8

Some object validations which I've implemented in the ObjectValidation library are now part of the .NET 8 preview. I won't remove them in v1.x, but in v2.x, which will target .NET 8.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.5.0 96 9/21/2024
2.4.0 129 8/25/2024
2.3.0 127 3/24/2024
2.2.0 111 3/2/2024
2.1.0 127 2/10/2024
2.0.0 207 12/17/2023
1.11.1 213 10/15/2023
1.11.0 168 9/10/2023
1.10.0 166 9/7/2023
1.9.0 176 9/3/2023
1.8.1 198 8/12/2023
1.6.0 200 5/20/2023
1.5.0 173 5/6/2023
1.3.0 210 4/30/2023
1.2.2 220 4/25/2023
1.2.1 177 4/25/2023
1.2.0 207 4/22/2023
1.1.0 223 4/7/2023
1.0.0 231 4/2/2023