XDocument.Validate - Expected Data Type of error element
Asked Answered
P

1

6

I have a class which validates the supplied XML document against the supplied XSD. In the class I call the XDocument.Validate method to perform validation, and getting the following error:

The 'http://www.example.com/enrollrequest1:requested-payment-date' element is invalid - The value '2015-05-28T00:00:00' is invalid according to its datatype 'http://www.w3.org/2001/XMLSchema:date' - The string '2015-05-28T00:00:00' is not a valid XsdDateTime value.

The value for element has been set from a .NET DateTime variable, which ultimately sets it with the time part included, since there is no equivalent of xs:date type in .NET.

The values for the elements are being set from a generic module, so I can't pick and choose elements and customize setting their values. The developer sends me value in a .NET DateTime type, which my program in turn calls the XElemet.SetValue(value) method to set it.

Also, the XSD file is out of my control. So modifying the XSD is not an option.

Is there a way to know what is the expected type of the XElement that caused the error? Once I know it, I can just typecast or customize my code accordingly. So for example in this case, if I know that the expected type is xs:date (and not xs:datetime), I can simply typecast the incoming value.

Here is my validator class, if this helps:

Option Strict On
Imports System.XML.Schema

Public Class XmlSchemaValidator
    Public ReadOnly Errors As New List(Of String)

    Private XDoc As XDocument
    Private Schemas As XmlSchemaSet

    Public Sub New(ByVal doc As XDocument, ByVal schemaUri As String, ByVal targetNamespace As String)
        Me.XDoc = doc
        Me.Schemas = New XmlSchemaSet
        Me.Schemas.Add(targetNamespace, schemaUri)
    End Sub

    Public Sub Validate()
        Errors.Clear()
        XDoc.Validate(Schemas, AddressOf XsdErrors)
    End Sub

    Private Sub XsdErrors(ByVal sender As Object, ByVal e As ValidationEventArgs)
        Errors.Add (e.Message)
    End Sub
End Class

Here is the function that is sets the xml node values.

Function SetValue(ByVal xmlDoc As XDocument, ByVal keyValues As Dictionary(Of String, Object)) As Boolean
    '' set values
    For Each kvp In keyValues
        Dim xe As XElement = xmlDoc.Root.XPathSelectElement(kvp.Key)

        ''-- this is buggy implementation for handling xs:date vs xs:datetime that needs to be corrected...
        'If TypeOf kvp.Value Is DateTime AndAlso DirectCast(kvp.Value, DateTime).TimeOfDay = TimeSpan.Zero Then
        '    xe.SetValue(DirectCast(kvp.Value, DateTime).ToString("yyyy-MM-dd"))
        'Else
        xe.SetValue(kvp.Value)
        'End If
    Next

    '' validate final document
    Dim schemaValidator As New XmlSchemaValidator(xmlDoc, schemaFile, "")
    schemaValidator.Validate()
    If schemaValidator.Errors.Count > 0 Then
        'Error Logging code goes here...
        Return False
    End If
    Return True
End Function
Pontifical answered 29/5, 2015 at 11:19 Comment(13)
What would you typecast to? As you said, there is no System.Date class.Scullion
I want to know what the XSD is expecting for that element - xs:date or xs:time or xs:datetime, irrespective of what value is set for the element. The rest I can handle appropriately.Pontifical
So for example if I know that the error was because I provided datetime instead of only date, I can simply do XElement.SetValue(value.ToString("yyyy-MM-dd")).Pontifical
See msdn.microsoft.com/en-us/library/xya3a402.aspxScullion
hmm.. http://www.w3.org/2001/XMLSchema:date meaning that the expected type is exactly xs:date. So why don't you get the expected type from the validation error message?Tryma
You may be thinking too generically. Could you solve this particular problem by simply serializing this particular DateTime value correctly? Maybe use XmlConvert.ToString(dateTime, "yyyy-MM-dd").Scullion
That's my point. Why do you need to know at runtime, dynamically? Why is the code that generates the XML that dynamic? Maybe show the part of the code that generates the XML.Scullion
So how do I know which one these XmlConvert.ToString(dateTime, "yyyy-MM-dd") or XmlConvert.ToString(dateTime, "yyyy-MM-ddThh:mm:ss") will not throw error when validated against the xsd file?Pontifical
Also, I don't want to get into the mess of parsing the error message as a string (unless of-course there is no other option). I'm looking for some good way to handle this in a generic way.Pontifical
I just updated my post and provided a trimmed down version of the validator class.. just in case it helps.Pontifical
Edit #2.. added function that sets the element values and calls the validator.Pontifical
Would it help if you actually had System.Date and System.TimeOfDay data types that used xs:date and xs:time appropriately, such as these ones?Quemoy
Matt, this was one of the ideas that came up during the meetings but the idea was rejected. The other suggestion that came up (and was rejected) was to pass me the date/datetime as string, and I could directly embed the value into the xml element. Both these approaches would need us to tell the developer implementing the client side code to specifically implement the custom class or pass us something which is not in a variable of its own type. We would rather like to contain the problem inside out code rather than relying on client code.Pontifical
P
0

You wrote in an earlier comment:

"I want to know what the XSD is expecting for that element"

Then, keep in mind that you can exploit the first parameter "sender" of your validation handlers, for instance, adapting this MSDN sample as, e.g.,

        string xsdMarkup =
 @"<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
   <xsd:element name='Root'>
    <xsd:complexType>
     <xsd:sequence>
      <xsd:element name='Child1' minOccurs='1' maxOccurs='1'/>
      <xsd:element name='Child2' minOccurs='1' maxOccurs='1'/>
     </xsd:sequence>
    </xsd:complexType>
   </xsd:element>
  </xsd:schema>";
        XmlSchemaSet schemas = new XmlSchemaSet();
        schemas.Add("", XmlReader.Create(new StringReader(xsdMarkup)));

        // (just for debug spying)
        var schemata = new XmlSchema[1];
        schemas.CopyTo(schemata, 0);

        XDocument errorDoc = new XDocument(
            new XElement("Root",
                new XElement("Child1", "content1"),
                new XElement("Child2", "content2"),
                new XElement("Child2", "content3") // (must fail validation on maxOccurs)
            )
        );

        Console.WriteLine();
        Console.WriteLine("Validating errorDoc");
        errorDoc.Validate(schemas, (sender, args) =>
        {
            Console.WriteLine("{0}", args.Message); // (what you're already doing)
            Console.WriteLine();
            // but there's also:
            var xElement = sender as XElement;
            if (xElement != null)
            {
                Console.WriteLine("Element {0} invalid : {1}", xElement, e.Exception.Message);
            }
        });

        Console.ReadKey();

This can yield an output with enough information on the recognizable culprits within the document, hopefully:

Validating errorDoc
The element 'Root' has invalid child element 'Child2'.

Element <Child2>content3</Child2> invalid : The element 'Root' has invalid child element 'Child2'.

Anyway, once you know the culprit in the invalid document, then you have better chances to correlate more reliably with the corresponding definitions in the schema(s) used for this validation (than just relying on the schema validation error string).

(sorry for the answer in C# syntax, but I'd rather not write in incorrect VB.NET, I'm getting too rusty with, by now)

'Hope this helps.

Pavel answered 14/10, 2016 at 5:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.