Skip to main content
BobKing
Gold ⭐️
June 6, 2025
Solved

Groovy script to validate XML against schema (XSD) always returns a valid XML even when it is invalid

Since the later versions of NiFi no longer provide the detailed reason an XML FlowFile fails validation against a schema (just a generic Validation Failed), I have been trying to create my own using the ExecuteGroovyScript processor. I am new to NiFI and Groovy, but I am an experienced .Net developer in C#.

Below is my Groovy script in the procesor:

import groovy.xml.XmlUtil
import javax.xml.transform.stream.StreamSource
import javax.xml.validation.SchemaFactory
import javax.xml.XMLConstants

def flowFile = session.get()
if (!flowFile) return

def xmlContent = flowFile.read().getText("UTF-8")

def schemaFile = new File("My_XML_Schema.xsd")

def schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
def schema = schemaFactory.newSchema(schemaFile)
def validator = schema.newValidator()

def validationErrors = new StringBuilder()
validator.setErrorHandler(new org.xml.sax.helpers.DefaultHandler() {
@Override
void warning(org.xml.sax.SAXParseException e) throws org.xml.sax.SAXException {
validationErrors.append("Warning: ${e.message}\n")
}
@Override
void error(org.xml.sax.SAXParseException e) throws org.xml.sax.SAXException {
validationErrors.append("Error: ${e.message}\n")
}
@Override
void fatalError(org.xml.sax.SAXParseException e) throws org.xml.sax.SAXException {
validationErrors.append("Fatal Error: ${e.message}\n")
}
})

try {
validator.validate(new StreamSource(new StringReader(xmlContent)))
session.transfer(flowFile, REL_SUCCESS)
} catch (org.xml.sax.SAXParseException e) {
validationErrors.append("Validation failed: ${e.message}\n")
flowFile = session.putAttribute(flowFile, "xml.validation.errors", validationErrors.toString())
session.transfer(flowFile, REL_FAILURE)
}

When I test it, it will always rout to Success. Even if I put elements or attributes in the XML file that are not in the schema, it always produces a Valid response. If I test the XML and XSD with the ValidateXML processor it does properly mark as Valid or Invalid.

I have also tried parsing the FlowFile into XmlSlurper and then using XmlUtil.serialize in the new StringReader and get the same results. Always valid even if it is not.

Has anyone successfully been able to validate XML to schema with a Groovy script and record the details validation errors?

BobKing
BobKingAnswer
Gold ⭐️
October 24, 2025

I found the solution. While the code did validate the XML to the schema, I needed to add a conditional statement to transfer to failure:

import groovy.xml.XmlSlurper
import groovy.xml.XmlUtil
import javax.xml.transform.stream.StreamSource
import javax.xml.validation.SchemaFactory
import javax.xml.XMLConstants
import org.xml.sax.ErrorHandler
import org.xml.sax.SAXParseException
 
def flowFile = session.get()
if (!flowFile) return
 
def fileXML = flowFile.read().getText("UTF-8")
def xmlContent = new XmlSlurper().parseText(fileXML.trim().replaceFirst("^([\\W]+)<","<").replaceAll('\'','\'\''))
 
def schemaFile = flowFile.getAttribute('XMLSchema')
def schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
def schema = schemaFactory.newSchema(new File(schemaFile))
def validator = schema.newValidator()
def validationErrors = new StringBuilder()
 
validator.setErrorHandler(new ErrorHandler() {
    @Override
    void warning(org.xml.sax.SAXParseException e) throws org.xml.sax.SAXException {
        validationErrors.append("<p>Warning: ${e.message}</p>")
    }
    @Override
    void error(org.xml.sax.SAXParseException e) throws org.xml.sax.SAXException {
        validationErrors.append("<p>Error: ${e.message}</p>")
    }
    @Override
    void fatalError(org.xml.sax.SAXParseException e) throws org.xml.sax.SAXException {
        validationErrors.append("<p>Fatal Error: ${e.message}</p>")
    }
})
 
try {
    validator.validate(new StreamSource(new StringReader(XmlUtil.serialize(xmlContent))))
    if (validationErrors.length() > 0) {
      flowFile = session.putAttribute(flowFile, "xml.validation.errors", validationErrors.toString())
    session.transfer(flowFile, REL_FAILURE)
    } else {
    session.transfer(flowFile, REL_SUCCESS)    
    }
} catch (org.xml.sax.SAXParseException e) {
    validationErrors.append("Validation failed: ${e.message}\n")
    flowFile = session.putAttribute(flowFile, "xml.validation.errors", validationErrors.toString())
    session.transfer(flowFile, REL_FAILURE)
}

2 replies

DianaTorres
Community Manager
Community Manager
June 6, 2025

Hi @MattWho @ArtiW @gtorres Do you have any insights here? Thanks!

Translate
Regards, Diana Torres, Senior Community Moderator
BobKing
BobKingAuthorAnswer
Gold ⭐️
October 24, 2025

I found the solution. While the code did validate the XML to the schema, I needed to add a conditional statement to transfer to failure:

import groovy.xml.XmlSlurper
import groovy.xml.XmlUtil
import javax.xml.transform.stream.StreamSource
import javax.xml.validation.SchemaFactory
import javax.xml.XMLConstants
import org.xml.sax.ErrorHandler
import org.xml.sax.SAXParseException
 
def flowFile = session.get()
if (!flowFile) return
 
def fileXML = flowFile.read().getText("UTF-8")
def xmlContent = new XmlSlurper().parseText(fileXML.trim().replaceFirst("^([\\W]+)<","<").replaceAll('\'','\'\''))
 
def schemaFile = flowFile.getAttribute('XMLSchema')
def schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
def schema = schemaFactory.newSchema(new File(schemaFile))
def validator = schema.newValidator()
def validationErrors = new StringBuilder()
 
validator.setErrorHandler(new ErrorHandler() {
    @Override
    void warning(org.xml.sax.SAXParseException e) throws org.xml.sax.SAXException {
        validationErrors.append("<p>Warning: ${e.message}</p>")
    }
    @Override
    void error(org.xml.sax.SAXParseException e) throws org.xml.sax.SAXException {
        validationErrors.append("<p>Error: ${e.message}</p>")
    }
    @Override
    void fatalError(org.xml.sax.SAXParseException e) throws org.xml.sax.SAXException {
        validationErrors.append("<p>Fatal Error: ${e.message}</p>")
    }
})
 
try {
    validator.validate(new StreamSource(new StringReader(XmlUtil.serialize(xmlContent))))
    if (validationErrors.length() > 0) {
      flowFile = session.putAttribute(flowFile, "xml.validation.errors", validationErrors.toString())
    session.transfer(flowFile, REL_FAILURE)
    } else {
    session.transfer(flowFile, REL_SUCCESS)    
    }
} catch (org.xml.sax.SAXParseException e) {
    validationErrors.append("Validation failed: ${e.message}\n")
    flowFile = session.putAttribute(flowFile, "xml.validation.errors", validationErrors.toString())
    session.transfer(flowFile, REL_FAILURE)
}
Translate