.netvb.netlinq-to-xmlxnamespace

XDocument.Validate namespace problems


I have modified the MSDN example to reflect my problem.

When using a namespace I can't get the document to validate as I would expect and when validating a document that doesnt have a namespace it validates regardless of whether or not it has an error in it or not.

Dim errors As Boolean = False

Private Sub XSDErrors(ByVal o As Object, ByVal e As ValidationEventArgs)
    Console.WriteLine("{0}", e.Message)
    errors = True
End Sub

Private Function AddNameSpace(ByVal xDoc As XDocument, ByVal ns As XNamespace) As XDocument
    For Each element As XElement In xDoc.Descendants
        element.Name = ns + element.Name.LocalName
    Next
    Return xDoc
End Function

Sub Main()
    Dim xsdMarkup As XElement = _
        <xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns="http://somenamespace.com" targetNamespace="http://somenamespace.com">
            <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>
    Dim schemas As XmlSchemaSet = New XmlSchemaSet()
    schemas.Add("http://somenamespace.com", xsdMarkup.CreateReader)

    Dim doc1 As XDocument = _
        <?xml version='1.0'?>
        <Root>
            <Child1>content1</Child1>
            <Child2>content1</Child2>
        </Root>

    Dim doc2 As XDocument = _
        <?xml version='1.0'?>
        <Root>
            <Child1>content1</Child1>
            <Child3>content1</Child3>
        </Root>

    Dim ns As XNamespace = "http://somenamespace.com"
    doc1 = AddNameSpace(doc1, ns)

    Console.WriteLine("Validating doc1")
    errors = False
    doc1.Validate(schemas, AddressOf XSDErrors)
    Console.WriteLine("doc1 {0}", IIf(errors = True, "did not validate", "validated"))

    Console.WriteLine()
    Console.WriteLine("Validating doc2")
    errors = False
    doc2.Validate(schemas, AddressOf XSDErrors)
    Console.WriteLine("doc2 {0}", IIf(errors = True, "did not validate", "validated"))

End Sub

Output:

Validating doc1

The element 'Root' in namespace 'http://somenamespace.com' has invalid child element 'Child1' in namespace 'http://somenamespace.com'. List of possible elements expected: 'Child1'.

doc1 did not validate

Validating doc2

doc2 validated


Solution

  • Well you will need to add elementFormDefault="qualified" to your schema (on the xsd:schema element) if you want your doc1 where you put the namespace on each element to be valid. With your current schema a valid instance would be one where the Root is in the targetNamespace but the ChildX elements are in no namespace.

    The second issue is a known problem with schema validation and namespaces, the validating parser looks for a matching schema for the root element, if there is none that it does lax validation so you don't get a validation error. With the XmlReader API you can ask for warning to be emitted in that case but I don't know how to do that with the Validate method. So you would need code like

    Imports System
    Imports System.Xml
    Imports System.Xml.Linq
    Imports System.Xml.Schema
    
    Module Module1
    
        Dim errors As Boolean = False
    
        Private Sub XSDErrors(ByVal o As Object, ByVal e As ValidationEventArgs)
            Console.WriteLine("{0}", e.Message)
            errors = True
        End Sub
    
        Private Function AddNameSpace(ByVal xDoc As XDocument, ByVal ns As XNamespace) As XDocument
            For Each element As XElement In xDoc.Descendants
                element.Name = ns + element.Name.LocalName
            Next
            Return xDoc
        End Function
    
        Sub Main()
            Dim xsdMarkup As XElement = _
                <xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns="http://somenamespace.com" targetNamespace="http://somenamespace.com" elementFormDefault="qualified">
                    <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>
            Dim schemas As XmlSchemaSet = New XmlSchemaSet()
            schemas.Add("http://somenamespace.com", xsdMarkup.CreateReader)
    
            Dim doc1 As XDocument = _
                <?xml version='1.0'?>
                <Root>
                    <Child1>content1</Child1>
                    <Child2>content1</Child2>
                </Root>
    
            Dim doc2 As XDocument = _
                <?xml version='1.0'?>
                <Root>
                    <Child1>content1</Child1>
                    <Child3>content1</Child3>
                </Root>
    
            Dim ns As XNamespace = "http://somenamespace.com"
            doc1 = AddNameSpace(doc1, ns)
    
            Console.WriteLine("Validating doc1")
            errors = False
            doc1.Validate(schemas, AddressOf XSDErrors)
            Console.WriteLine("doc1 {0}", IIf(errors = True, "did not validate", "validated"))
    
            Console.WriteLine()
            Console.WriteLine("Validating doc2")
            Dim xrs As New XmlReaderSettings()
            xrs.ValidationType = ValidationType.Schema
            xrs.ValidationFlags = xrs.ValidationFlags Or XmlSchemaValidationFlags.ReportValidationWarnings
            xrs.Schemas = schemas
            AddHandler xrs.ValidationEventHandler, AddressOf XSDErrors
            errors = False
            Using xr1 As XmlReader = doc2.CreateReader()
                Using xr2 As XmlReader = XmlReader.Create(xr1, xrs)
                    While xr2.Read()
    
                    End While
                End Using
            End Using
            Console.WriteLine("doc2 {0}", IIf(errors = True, "did not validate", "validated"))
    
        End Sub
    
    End Module