Search code examples
c#.netxmlxmldocument

XmlDocument.NodeInserted triggered on XmlDocument.Validate()


I'm currently writing an application to modify and handle specific XML Documents.

My application has a stack for undo / redo of changes and also logs changes on the XML DOM to the Debug console. On every change of the DOM the application validates the document, so the user directly gets feedback on invalid entries.

My problem is, that the XmlDocument.Validate() method triggers an XmlDocument.NodeInserted Action. This leads to an endless callback loop. How can I prevent this behaviour?

Parts of my code:
Initializing / loading document:

private void ParseStyleSheet(FileInfo stylesFile)
    {
        // load document
        XmlDocument document = new XmlDocument();
        document.PreserveWhitespace = true;
        document.Schemas = StyleSchemaSet.GetSchemaset();
        document.Load(stylesFile.FullName);
        // add event handlers
        document.NodeChanged += XmlNodeChangedAction;
        document.NodeChanged += _undoMngr.XmlNodeChangedEventHandler;
        document.NodeInserted += XmlNodeChangedAction;
        document.NodeInserted += _undoMngr.XmlNodeChangedEventHandler;
        document.NodeRemoved += XmlNodeChangedAction;
        document.NodeRemoved += _undoMngr.XmlNodeChangedEventHandler;
        // set namespace
        XmlNamespaceManager nameSpaceMngr = new 
        XmlNamespaceManager(document.NameTable);
        ...
}

Change handler / Validation callback:

    private void XmlNodeChangedAction(object src, XmlNodeChangedEventArgs args)
    {
        // debug outputs
        Debug.WriteLine("XML node is changing");
        Debug.Indent();
        Debug.WriteLine(args.Action);
        Debug.WriteLine("Old: " + args.OldValue);
        Debug.WriteLine("New: " + args.NewValue);
        _document.Validate(this.ValidationEventCallBack);//TODO leads to loop
        // undindent debug
        Debug.Unindent();
    }


    private void ValidationEventCallBack(object sender, ValidationEventArgs args)
    {
        switch (args.Severity)
        {
            case XmlSeverityType.Error:
                //TODO maybe throw for GUI message?
                Debug.WriteLine("XSD Error: " + args.Message);
                break;

            case XmlSeverityType.Warning:
                Debug.WriteLine("XSD Warning: " + args.Message);
                break;

            default:
                break;
        }
    }

As soon as I change a node I get an endless loop of changes, which is also displayed in the Debugger console:

XML node is changing
Change
Old: 20px
New: 25px
XML node is changing
    Insert
    Old: #ebebeb
    New: #ebebeb
    XML node is changing
        Insert
        Old: #ebebeb
        New: #ebebeb
        XML node is changing
            Insert
            Old: #ebebeb
            New: #ebebeb
            XML node is changing
                Insert
                ...




Update (thanks to the hint of Tao):
If the document is already validated directly after loading and before adding the NodeInserted delegate, no loop occurs anymore. Like this all the DOM changes already happened because of the first validate.

private void ParseStyleSheet(FileInfo stylesFile)
    {
        // load document
        XmlDocument document = new XmlDocument();
        document.PreserveWhitespace = true;
        document.Schemas = StyleSchemaSet.GetSchemaset();
        document.Load(stylesFile.FullName);
        document.Validate(ValidationEventCallBack);
        // add event handlers
        document.NodeChanged += XmlNodeChangedAction;
        document.NodeChanged += _undoMngr.XmlNodeChangedEventHandler;
        document.NodeInserted += XmlNodeChangedAction;
        document.NodeInserted += _undoMngr.XmlNodeChangedEventHandler;
        document.NodeRemoved += XmlNodeChangedAction;
        document.NodeRemoved += _undoMngr.XmlNodeChangedEventHandler;
        ...
    }

Solution

  • The documentation for Validate() says:

    The Validate method performs infoset augmentation. Specifically, after successful validation, schema defaults are applied, text values are converted to atomic values as necessary, and type information is associated with validated information items. The result is a previously un-typed XML sub-tree in the XmlDocument replaced with a typed sub-tree.

    It sounds to me like you need to add some additional state to track whether a change is "intentional", or a result of the "infoset augmentation" that happens during validation. Something like tracking an "IsValidating" state and avoiding the _document.Validate() call when in that state.