I am creating a tool that gets an xml file that contains input data for set tool, generated by some other program. Before I can use this input data, I should validate the xml file, make sure all the data is there. I am attempting to create an xsd file to do the validation (using the libxml2 library).
The c code that attempts at validating this, is quite rudimentary for now:
XmlValidator inputXmlValidator{};
res = inputXmlValidator.ConfigureValidationSchema("../temp_test_inputs/xml_validation/input.xsd");
if (!res)
return -1;
res = xmlValidator.ValidateXml("../temp_test_inputs/xml_validation/input.xml");
if (!res)
return -1;
bool XmlValidator::ConfigureValidationSchema(const char* xsdFilename)
{
if (schemaValidationContext != nullptr)
{
F1_ERROR("This instance already has a validation scheme configured!");
return false; // there is already a schema present...
}
xmlSchemaParserCtxtPtr schemaParserContext = nullptr;
schemaParserContext = xmlSchemaNewParserCtxt(xsdFilename);
if (schemaParserContext)
{
xmlSchemaPtr parsedSchema = nullptr;
xmlSchemaSetParserStructuredErrors(schemaParserContext, ProcessParsingError, nullptr);
parsedSchema = xmlSchemaParse(schemaParserContext);
xmlSchemaFreeParserCtxt(schemaParserContext);
if (parsedSchema)
schemaValidationContext = xmlSchemaNewValidCtxt(parsedSchema);
}
return schemaValidationContext != nullptr;
}
bool XmlValidator::ValidateXml(const char* xmlFilename)
{
if (schemaValidationContext == nullptr)
{
F1_ERROR("No validation scheme configured! Failed to validate '{0}'.", xmlFilename);
return false; // there is no validation schema present...
}
// read the xml file
xmlTextReaderPtr xmlTextReader = xmlReaderForFile(xmlFilename, NULL, 0);
if (xmlTextReader == nullptr)
{
F1_ERROR("Failed to open '{0}'.", xmlFilename);
return false; // failed to read xml file...
}
// configure schema validation
int hasSchemeErrors = 0;
xmlTextReaderSchemaValidateCtxt(xmlTextReader, schemaValidationContext, 0);
xmlSchemaSetValidStructuredErrors(schemaValidationContext, ProcessValidatorError, &hasSchemeErrors);
// process the xml file
int hasValidationErrors = 0;
do
{
hasValidationErrors = xmlTextReaderRead(xmlTextReader);
} while (hasValidationErrors == 1 && !hasSchemeErrors);
// process errors
//if (hasValidationErrors != 0)
//{
// xmlErrorPtr err = xmlGetLastError();
// F1_ERROR("Failed to parse '{0}' at line {1}, col {2}! Error {3}: {4}", err->file, err->line, err->int2, err->code, err->message);
//}
// free up the text reader memory
xmlFreeTextReader(xmlTextReader);
return hasValidationErrors == 0; // return true if no errors found
}
I am quite sure this code works, as I attempted it with the shiporder example. Now I attempt at moving away from the example and alter it for my own xml file.
I went with the most basic xml file (only the root element and an attribute)
<?xml version="1.0" encoding="UTF-8"?>
<root timestamp="20220714 1324">
</root>
And I cannot create a suitable xsd file that can validate this:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema" >
<xs:element name="root">
<xs:complexType>
<xs:attribute type="xs:string" name="timestamp"/>
</xs:complexType>
</xs:element>
</xs:schema>
I keep getting the error
Failed to validate '../temp_test_inputs/xml_validation/input.xml' at line 2, col 0! Error 1845: Element 'root': No matching global declaration available for the validation root.
After 3 days of searching, I could not find any solution that got me further than this error... Any ideas? In need I could alter the xml file, but preferably I would like to only tweak the xsd file (if possible). I'll take any solution at this point...
CodePudding user response:
I found the mistake... And of course it's a stupid one...
My main function should have looked like
auto res = xmlValidator.ConfigureValidationSchema("../temp_test_inputs/xml_validation/shiporder.xsd");
if (!res)
return -1;
res = xmlValidator.ValidateXml("../temp_test_inputs/xml_validation/shiporder.xml");
if (!res)
return -1;
F1_TEST_COMMON::XmlValidator inputXmlValidator{};
res = inputXmlValidator.ConfigureValidationSchema("../temp_test_inputs/xml_validation/input.xsd");
if (!res)
return -1;
res = inputXmlValidator.ValidateXml("../temp_test_inputs/xml_validation/input.xml");
if (!res)
return -1;
However, it had a 'minor' mistake, where it was using the shiporder.xsd
(xmlValidator
) rather than the input.xsd
(inputXmlValidator
)...
xmlValidator.ValidateXml("../temp_test_inputs/xml_validation/input.xml");
Only one variable mistake while copy pasting... Wasted over a week of debugging...