2

I would like to remove a comment tag from a XmlDocument using Inno Setup. My xml looks approximately like this.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="AuthenticationService">
        <!--
        <endpoint contract="System.Web.ApplicationServices.AuthenticationService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        -->
      </service>
    </services>
  </system.serviceModel>
</configuration>

I would like to uncomment using Inno Setup the section

<endpoint>
 ... 
</endpoint>

, so remove the comment tags around it.

I found from here that this could be done using the following procedure:

  1. Get the value of the comment node
  2. Create a new XmlNode with the value from step 1
  3. Delete the comment node
  4. Add the new node from step 2 to the DOM tree

Unfortunately the example in the answer is in C#.

if (commentNode != null)
{
  XmlReader nodeReader = XmlReader.Create(new StringReader(commentNode.Value));
  XmlNode newNode = xdoc.ReadNode(nodeReader);
  commentNode.ParentNode.ReplaceChild(newNode, commentNode);
}

So far I haven't found how to implement XmlReader with Inno Setup. So, I tried this.

APath := '//configuration/system.serviceModel/services/service/comment()';
XMLCommentNode := XMLDoc.selectSingleNode(APath);

if Not (IDispatch(XMLCommentNode) = nil) then
begin
  Log('Remove comment tag ' + APath + ' value is: ' + XMLCommentNode.Text);
  newNode := XMLDoc.createElement(XMLCommentNode.Text);
  XMLCommentNode.ParentNode.ReplaceChild(newNode, XMLCommentNode);
end

The text value of XMLCommentNode seems correct to me when I write it to log.

[08.59.44,190]   Remove comment tag //configuration/system.serviceModel/services/service/comment() value is: 
            <endpoint contract="System.Web.ApplicationServices.AuthenticationService">
                <identity>
                    <dns value="localhost" />
                </identity>
            </endpoint>
    

However, when creating a new element from that I get an error message

Internal error: Expression error 'Runtime error (at 20:2651):

msxml3.dll: This name may not contain the '
' character:

-->
<--         <endpoint contract="System.Web.ApplicationServices.AuthenticationService">
                <identity>
                    <dns value="localhost" />
...
'

How proceed and correct this error?

Markku Rintala
  • 127
  • 1
  • 17
  • The `createElement` cannot accept an XML fragment. I'm not even sure if there's such functionality in the `DOMDocument` API. Did you consider just removing the `` from the (plain text) file? – Martin Prikryl Oct 05 '21 at 07:10
  • Yes, I slightly considered also removing from plain text, but not started to implement that yet. There are many other comments too in that xml file, so finding the correct one seemed too hard task so far. – Markku Rintala Oct 05 '21 at 08:03
  • As I wrote before, I do not think there's such functionality in the `DOMDocument` API. The `appendXML` seems to be PHP-only thing. – Martin Prikryl Oct 05 '21 at 09:38

1 Answers1

1

I solved my problem, as Martin Prikryl suggested, by removing comment tags <!-- and --> from plain text file. For that I wrote the following function.

XML structure is assumed to be like:

<node1>
  <node2>
    <node3>
      <node4 name="AuthenticationService">
        <!-- This comment should be removed
        <attributeNode some_data="some data">
          <subnode>
            <specifier value="special" />
          </subnode>
        </attributeNode>
        -->
      </node4>
    </node3>
  </node2>
</node1>

Call example:

RemoveCommentTag(ConfigFileName, '//node1/node2/node3/node4', 'attributeNode');

Code:

function RemoveCommentTag(const FileName, TagPath, Attribute: string): Boolean;
var
  I: Integer;
  Line: string;
  AttributeLine: string;
  TagPos, AttributePos, CommentPos: Integer;
  FileLines: TStringList;

  TagPaths: TArrayOfString;
  Tag: string;
  TagCntr: Integer;
  Found: boolean;

begin
  Result := False;

  TagPaths:= StrSplit(TagPath, '/');

  FileLines := TStringList.Create;
  try
    //Tag := TagName + '=';
    FileLines.LoadFromFile(FileName);
    I := 0;
    for TagCntr := 0 to GetArrayLength(TagPaths) -1 do
    begin
      Tag := TagPaths[TagCntr];
      if Tag <> '' then
      begin
        // Search for Tag
        Found := false;
        while (I < FileLines.Count) and not Found do
        begin
          Line := FileLines[I];
          TagPos := Pos(Tag, Line);
          if TagPos > 0 then
            Found := True;
          I := I + 1;
        end;
      end;
    end;

    // After this I should have the line number where the searched section begins.
    // Then search for comment opening tag '<!--'
    // Check, that the comment is before correct attribute
    Found := false;
    while (I < FileLines.Count) and not Found do
    begin
      Line := FileLines[I];
      // Check also, that there really is comment inside this tag.
      TagPos := Pos('/'+Tag, Line);
      if TagPos > 0 then
      begin
        // Malformed XML file. There is no comment inside this tag.
        // We can exit function.
        exit;
      end;

      CommentPos := Pos('<!--', Line);
      if CommentPos > 0 then
      begin
        AttributeLine := FileLines[I+1];
        AttributePos := Pos(Attribute, AttributeLine);
        if AttributePos > 0 then 
          Found := True
      end       
      else
        I := I + 1;
    end;

    // Correct comment tag <!-- should now be on line number I
    // Delete whole line from the beginning of comment tag or
    // add comment ending tag to the end of this line

    //Delete(Line, TagPos + Length('<!--'), MaxInt);
    //Delete(Line, TagPos, MaxInt);

    // Check that there is no comment ending tag on the same line.
    // Otherwise add comment ending tag.
    CommentPos := Pos('-->', Line);

    if (I < FileLines.Count) and (CommentPos = 0) then
    begin
      Line := Line + ' -->';
      FileLines[I] := Line;
      FileLines.SaveToFile(FileName);
    end;

    // Then continue from next line and search for comment ending tag -->
    I := I + 1;
    Found := false;
    while (I < FileLines.Count) and not Found do
    begin
      Line := FileLines[I];

      // Check also, that there really is comment inside this tag.
      TagPos := Pos('/'+Tag, Line);
      if TagPos > 0 then
      begin
        // There is no comment ending tag inside this tag. We can exit function.
        // Probably script has already been run.
        exit;
      end;

      CommentPos := Pos('-->', Line);
      if CommentPos > 0 then
        Found := True
      else
        I := I + 1;
    end;
    
    // <!-- should be now on line number I
    // Delete it to the end of the line

    if I < FileLines.Count then
    begin
      Delete(Line, CommentPos, MaxInt);
      FileLines[I] := Line;
      FileLines.SaveToFile(FileName);
    end;

   finally
    FileLines.Free;
  end;
end;
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Markku Rintala
  • 127
  • 1
  • 17