Samstag, 15. Mai 2010

Failed XML Validation of integer values

I recently came around a strange issue while validating a xml file. The content looked similar to this one:

<main xmlns="http://tempuri.org/myNamespace">
<number>298660059755687851616176132193422926953</number>
<note>Test</note>
</main>

While validating this file using C# .NET 3.5 I received the following error within the ValidationEventHandler:

"Das Element 'http://tempuri.org/myNamespace:Number' hat gemäß seinem Datentyp einen ungültigen Wert."


The element itself is specified within the schema file:

<xs:element name="Number" type="xs:integer"/>

A quick research about the "xs:integer" data type showed, that an integer value within xml is unbounded. Due to limitations of our beloved working machines integers are limited in most frameworks (e.g. 2.147.483.647 for Int32 in .NET).

My dilemma was that I wanted to use the build-in validation mechanism with elements containing integers exceeding the framework bounds.

So the key was to check what causes a validation failure and if this failure was "wrong" due to the framework limitations. The following will roughly sketch the solution to this.

Within the ValidationEventHandler a method named "IsWrongFormatException" is called to check if the error can be ignored:

private void ValidatingReader_ValidationEventHandler(object sender, ValidationEventArgs e)
// check if the error is a wrong format exception
if(!IsWrongFormatException(sender, e)) {
// tread as "correct" error
}
}

When the value exceeds the framework bounds a FormatException is thrown. This exception will be placed in the InnerException property of the ValidationEventArgs argument. So we have to check if the error was caused by a FormatException:

private bool IsWrongFormatException(object sender, ValidationEventArgs e) {
// check for non null Exception-Property
// Get the inner Exception
FormatException exception = e.Exception.InnerException as FormatException;
// check if is a FormatException
if (null != exception) {

If the exception is not null, we know a FormatException occured. The type used to check the format of the value is stored in the sender object (in a property named "ValueType"). I used reflection here to get the PropertyInfo and the value of the property.

// use reflection to get the info
PropertyInfo infoValueType = sender.GetType().GetProperty("ValueType");
// read the validated value type
Type valueType = infoValueType.GetValue(sender, null) as Type;

For validating integer values the framework uses the Decimal-class. So we check if the type equals Decimal.

if ((null != valueType) && (valueType.Equals(typeof (Decimal)))) {

Now we know that a FormatException occured within an xml element that should contain an integer value. It would be easy to ignore the error at this point, but it could be possible that the value is really invalid (e.g. containing a character). So we need the value that was validated itself. This is more difficult because the text (containing the value) is burried as a non-public property of the sender. But reflection will help us at this moment. First we read the property "validator" of the sender

FieldInfo info = sender.GetType().GetField("validator", BindingFlags.Instance | BindingFlags.NonPublic);
object validator = info.GetValue(sender);

The validator object contains the property "textValue" with the searched string:

info = validator.GetType().GetField("textValue", BindingFlags.Instance | BindingFlags.NonPublic);
StringBuilder textValue = info.GetValue(validator) as StringBuilder;

The StringBuilder named textValue contains the validated value. To check if the value is a valid integer we use a regular expression to check.

result = ((null != value) && (System.Text.RegularExpressions.Regex.IsMatch(textValue.ToString(), "^[0-9]+$")));

If result is true, then the FormatException was thrown wrongly during the validation and we can ignore the error.

Remarks:
I read that there is a special xml datatype named xs:int to reflect bounded integer values.