Markku Rintala
Markku Rintala

Reputation: 149

How to remove a comment tag from XmlDocument with Inno Setup

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?

Upvotes: 2

Views: 307

Answers (1)

Markku Rintala
Markku Rintala

Reputation: 149

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;

Upvotes: 1

Related Questions