Home > Blockchain >  Attempt at using libxml2 to validate xml file with xsd file
Attempt at using libxml2 to validate xml file with xsd file

Time:07-20

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...

  • Related