xmldelphixsdmsxml6

Incorrect definition for the root element in XML schema when generating XSD on the fly


Yes, I took a look at this question: Incorrect definition for the root element in XML schema

Here is a sample of my code that loads XML and checks it against XML schema generated on the fly.

The problem is: when I add an XML schema generated as a DOM document object, I get an error:

EOleException: /schema

Incorrect definition for the root element in XML schema

but when I reload the XSD dom document into itself as a string:

xsd.loadXML(xsd.xml);

error disappears.

I made also 2 debug outputs of XSD: before reload and after. Both files are identical byte-to-byte!!!

I have no idea what could be wrong and assume that when string representations of XML documents are identical, then the objects' structures are identical too.

program XsdValidatorMCV;

uses
  Winapi.MSXMLIntf, System.Win.ComObj, Winapi.ActiveX;

var
  xsd: IXMLDOMDocument2;
  xsdl: IXMLDOMSchemaCollection;
  root: IXMLDOMElement;
  el: IXMLDOMElement;

begin
  CoInitialize(nil);
  xsd := CreateOleObject('Msxml2.DOMDocument.6.0') as IXMLDOMDocument2;
  xsdl := CreateOleObject('Msxml2.XMLSchemaCache.6.0') as IXMLDOMSchemaCollection;
  try
    xsd.appendChild(xsd.createProcessingInstruction('xml', 'version="1.0"'));
    root := xsd.createElement('xs:schema');
    root.setAttribute('xmlns:xs', 'http://www.w3.org/2001/XMLSchema');
    xsd.appendChild(root);
    el := xsd.createElement('xs:element');
    root.appendChild(el);
    el.setAttribute('name', 'data');

    xsd.save('generated1.xsd'); //Debug output
    //Workaround: reloading itself as string eliminates strange schema error:
    //EOleException: /schema Incorrect definition for the root element in XML schema.
    xsd.loadXML(xsd.xml);
    xsd.save('generated2.xsd'); //Debug output

    xsdl.add('', xsd); //Here is an error when without xsd.loadXML(xsd.xml)
  finally
    xsdl := nil;
    xsd := nil;
  end;
end.

Solution

  • The problem was the absence of namespaceURI for all nodes. It must be http://www.w3.org/2001/XMLSchema and it will not be set automatically for every new node.

    Creating name-spaced elements with MSXML is not so easy. The amount of code triples:

    const
      NODE_ELEMENT = 1;
    var
      xsd: IXMLDOMDocument2;
      xsdl: IXMLDOMSchemaCollection;
      root: IXMLDOMNode;
      el: IXMLDOMNode;
      att: IXMLDOMAttribute;
    begin
      CoInitialize(nil);
      xsd := CreateOleObject('Msxml2.DOMDocument.6.0') as IXMLDOMDocument2;
      xsdl := CreateOleObject('Msxml2.XMLSchemaCache.6.0') as IXMLDOMSchemaCollection;
    
      xsd.appendChild(xsd.createProcessingInstruction('xml', 'version="1.0"'));
    
      root := xsd.createNode(NODE_ELEMENT, 'xs:schema', 'http://www.w3.org/2001/XMLSchema');
      xsd.appendChild(root);
    
      att := xsd.createAttribute('xmlns:xs');
      att.value := 'http://www.w3.org/2001/XMLSchema';
      root.attributes.setNamedItem(att);
    
    
      el := xsd.createNode(NODE_ELEMENT, 'xs:element', 'http://www.w3.org/2001/XMLSchema');
      root.appendChild(el);
    
      att := xsd.createAttribute('name');
      att.value := 'data';
      el.attributes.setNamedItem(att);
    
      xsdl.add('', xsd);
    end.
    

    More friendly version:

    const
      XS_URI = 'http://www.w3.org/2001/XMLSchema';
    
    function createSchemaNode(const parentNode: IXMLDOMNode; const name: WideString): IXMLDOMNode;
    const
      NODE_ELEMENT = 1;
    var
      doc: IXMLDOMDocument;
    begin
      if Supports(parentNode, IXMLDOMDocument) then
        doc := parentNode as IXMLDOMDocument
      else
        doc := parentNode.ownerDocument as IXMLDOMDocument;
      Result := doc.createNode(NODE_ELEMENT, 'xs:' + name, XS_URI);
      parentNode.appendChild(Result);
    end;
    
    procedure setAttribute(const node: IXMLDOMNode; const name: WideString; value: OleVariant);
    var
      att: IXMLDOMAttribute;
      doc: IXMLDOMDocument2;
    begin
      doc := node.ownerDocument as IXMLDOMDocument2;
      att := doc.createAttribute(name);
      att.value := value;
      node.attributes.setNamedItem(att);
    end;
    
    var
      xsd: IXMLDOMDocument2;
      xsdl: IXMLDOMSchemaCollection;
      root: IXMLDOMNode;
      el: IXMLDOMNode;
    begin
      CoInitialize(nil);
      xsd := CreateOleObject('Msxml2.DOMDocument.6.0') as IXMLDOMDocument2;
      xsdl := CreateOleObject('Msxml2.XMLSchemaCache.6.0') as IXMLDOMSchemaCollection;
    
      xsd.appendChild(xsd.createProcessingInstruction('xml', 'version="1.0"'));
    
      root := createSchemaNode(xsd, 'schema');
      setAttribute(root, 'xmlns:xs', XS_URI);
    
      el := createSchemaNode(root, 'element');
      setAttribute(el, 'name', 'data');
    
      xsdl.add('', xsd);
    end.