4

I'm working with some xml 'snippets' that form elements down the xml. I have the schema but I cannot validate these files because they are not complete xml documents. These snippets are wrapped with the necessary parent elements to form valid xml when they are used in other tools so I don't have much option in making them into valid xml or in changing the schema.

Is it possible to validate an element, rather than the whole document? If not, what workarounds could be suggested?

I'm working in C# with .NET 2.0 framework.

ipwnponies
  • 219
  • 1
  • 4
  • 10
  • I am not sure how you could use `SelectSingleNode` if you cannot use the root node of the `XmlDocument`, which you would have to load your Xml into, which would fail if it is not valid. It makes me think that you cannot use `XPath` unless your document itself is valid. Or is there a way to do so? –  Dec 09 '10 at 12:49

6 Answers6

4

I had a similar problem where I could only validate parts of my XML document. I came up with this method here:

private void ValidateSubnode(XmlNode node, XmlSchema schema)
{
    XmlTextReader reader = new XmlTextReader(node.OuterXml, XmlNodeType.Element, null);

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ConformanceLevel = ConformanceLevel.Fragment;
    settings.Schemas.Add(schema);
    settings.ValidationType = ValidationType.Schema;
    settings.ValidationEventHandler += new ValidationEventHandler(XSDValidationEventHandler);

    using (XmlReader validationReader = XmlReader.Create(reader, settings))
    {     
        while (validationReader.Read())
        {
        }
    }
}

private void XSDValidationEventHandler(object sender, ValidationEventArgs args)
{
    errors.AppendFormat("XSD - Severity {0} - {1}", 
                        args.Severity.ToString(), args.Message);
}

Basically, I pass it an XmlNode (which I select from the entire XmlDocument by means of .SelectSingleNode), and an XML schema which I load from an embedded resource XSD inside my app. Any validation errors that might occur are being stuffed into a "errors" string builder, which I then read out at the end, to see if there were any errors recorded, or not.

Works for me - your mileage may vary :-)

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • This solution works quite well. In fact, this looks strikingly similar to the validation I have in place right now for documents. I did not know it could be applied to individual nodes as well. Looking back, I suppose the root is a node in itself. – ipwnponies Apr 08 '09 at 17:43
  • Thank you, Marc! This helped me a lot as well. – Kizz Jul 11 '12 at 23:24
  • Marc, could you edit this answer to not use `XmlTextReader`, and also to use `using` blocks? I just saw this code copy/pasted. – John Saunders Oct 07 '13 at 18:40
  • @JohnSaunders: I updated my post - the `using` for the second `XmlReader` is not an issue. For the `XmlTextReader` - not quite sure how I could replace that. According to the `XmlReader` docs even for .NET 4.5, reading from an `XmlNode` seems to be one of those "edge cases" where the "old-style" readers are still acceptable/recommended. – marc_s Oct 07 '13 at 20:31
2

There is a XmlDocument.Validate method that takes an XmlNode as argument an validates only this node. That may be what you're looking for ...

MartinStettner
  • 28,719
  • 15
  • 79
  • 106
  • Unfortunately, I don't have access to my project as of now, so I cannot verify this. But from reading MSDN, it does seem that this is what I was looking for. All this time, I've been looking under XmlSchema... – ipwnponies Apr 04 '09 at 16:21
  • This does not work. The node to be validated, has to be at the correct 'depth'. This method would be useful for if you wanted to partially validate your xml file. I don't have a valid xml file to begin with. – ipwnponies Apr 06 '09 at 22:33
1

Ok, here's another approach:

You could transform your schema file using an XSLT transformation into a new schema that has your snippet elements as root. Say your original schema would be

<xs:schema id="MySchema" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="RootElement">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="NestedElement">
          <xs:complexType>
            <xs:attribute name="Name" type="xs:string" use="required"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

You have snippets of type NestedElement that you want to validate:

<NestedElement Name1="Name1" />

Then you could use an XSLT template like

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="xs:element[@name='NestedElement']"
                xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:schema id="MySchema">
      <xsl:copy-of select="."/>
    </xs:schema>
  </xsl:template>
</xsl:stylesheet>

To create a new schema that has NestedElement as root. The resulting schema would look like

<xs:schema id="MySchema" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="NestedElement">
    <xs:complexType>
      <xs:attribute name="Name" type="xs:string" use="required" />
    </xs:complexType>
  </xs:element>
</xs:schema>

You can then validate a snippet document against this new schema using a code like

XmlSchema schema;
using (MemoryStream stream =    new MemoryStream())
using (FileStream fs = new FileStream("MySchema.xsd", FileMode.Open))
using(XmlReader reader = XmlReader.Create(fs)) {
  XslCompiledTransform transform = new XslCompiledTransform();
  transform.Load("SchemaTransform.xslt");
  transform.Transform(reader, null, stream);
  stream.Seek(0, SeekOrigin.Begin);
  schema = XmlSchema.Read(stream, null);
}
XmlDocument doc = new XmlDocument();
doc.Schemas.Add(schema);
doc.Load("rootelement.xml");
doc.Validate(ValidationHandler);

MySchema.xsd is the original schema, SchemaTransform.xslt is the transformation (as shown above), rootelement.xml is an XML document containing a single snippet node.

MartinStettner
  • 28,719
  • 15
  • 79
  • 106
  • This is a very elegant solution. But there is one thing keeping me from using it. The transform requires that knowledge of the 'NestedElement' be known. This is an issue as there may be updates to the schema by third parties and that would require an update/maintenance to the xslt. – ipwnponies Apr 08 '09 at 17:47
  • You could make the element name (i.e. 'NestedElememt') a parameter and read it from the top level element of the snippet inside the code. Then it would also work for multiple snippet elements ... – MartinStettner Apr 08 '09 at 17:59
  • If you have time, perhaps you can demonstrate the "feeding" parameter trick. I literally wasted 3-4 hours on this, and got nowhere. Actually, feeding the parameter is easy, the trick is to use it on XSLT 1.0: http://stackoverflow.com/questions/6117460/xsl-how-to-use-parameter-inside-match but I never got anywhere, namely the template never matched what I wanted. Going to string.Replace everything for now, as this is ridiculous. +1 for solution though. – Erti-Chris Eelmaa Jul 04 '14 at 07:28
0

I had the same problem. Even asked here for solution. I have found a workaround.

The problem is that only root elements can be validated. So... I edit the scheme IN MEMORY and add the element/type to validate to the root

public static void AddElementToSchema(XmlSchema xmlSchema, string elementName, string elementType, string xmlNamespace)
{
    XmlSchemaElement testNode = new XmlSchemaElement();
    testNode.Name = elementName;
    testNode.Namespaces.Add("", xmlNamespace);
    testNode.SchemaTypeName = new XmlQualifiedName(elementType, xmlNamespace);
    xmlSchema.Items.Add(testNode);
    xmlSchema.Compile(XMLValidationEventHandler);
}

Just a couple of lines and you must not modify or add any XSD files :) With this simple change to your in-memory schema, you can validate the fragment with the same code you use to validate a full document. Just be sure that the root element of the fragment to validate includes the namespace. :)

Kaikus
  • 1,001
  • 4
  • 14
  • 26
0

You can use a special namespace alias to earmark the elements you want to validate and then only add schema for that namespace alias but not for others. This way only those elements with your special namespace prefix will get validated.

Andriy Volkov
  • 18,653
  • 9
  • 68
  • 83
  • From what I understand, this would not be suitable as it requires to editing/creating schemas. I cannot change the schema because it is a protocol that is used and updated by others on a yearly basis. I am only creating an internal tool, so maintenance must be kept to a minimum. – ipwnponies Apr 04 '09 at 16:19
0

It doesn't not seem possible to do what I aspire to do. My current work around is to create a blank template xml document. Then replace the desired element with my snippet. From there, I believe the Validate method would then be viable. But to create this template dynamically seems to be another daunting task in its own right. There doesn't seem to be any easy way to create a 'skeleton' document.

ipwnponies
  • 219
  • 1
  • 4
  • 10