antxmltask

Is there a way to append elements to xml if they are not already existing


Having an existing xml I want to append a new node if it's not already existing in the xml.

I am a complete newbie in XML path and I started by googling because I believe this would be pretty standard problem.

I'm looking into using xmltask for this.

I also found a small example here

I want to create a macrodef in Ant that will allow my consumers to register their endpoints.

The final structure of my XML would look like this:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <endpoints>
    <endpoint url="serviceA" />
  </endpoints>
</configuration>

My macrodef would be like this:

  <macrodef name="appendConfigEndpoints">
    <attribute name="filePath" />
    <attribute name="endpointUrl" />
    <sequential>
      <xmltask source="@{filePath}">
        <copy path="count(/configuration/endpoints/endpoint)" property="existsEndpoint" /> <!-- how to check if the endpoint exists -->
      </xmltask>
      <echo message="found ${existsEndpoint} in xml" />
      <if>
        <isfalse value="${existsEndpoint}"/>
        <then>
          <concat destfile="${currentScriptDirectory}/tempFile.xml">
            <string>&lt;endpoint url="@{endpointUrl}" /&gt;${line.separator}</string>
          </concat>
          <xmltask source="@{filePath}" dest="@{filePath}" outputter="simple:2">
            <insert path="/configuration/endpoints" file="${currentScriptDirectory}/tempFile.xml" position="under" />
          </xmltask>
          <delete file="${currentScriptDirectory}/tempFile.xml"/>
        </then>
        <else>
          <echo message="already exists @{endpointUrl} in xml" />
        </else>
      </if>
    </sequential>
  </macrodef>

To generate my xml I would want to call the target like this

<appendConfigEndpoints filePath="file.xml" endpointUrl="serviceA" />
<appendConfigEndpoints filePath="file.xml" endpointUrl="serviceB" />
<appendConfigEndpoints filePath="file.xml" endpointUrl="serviceC" />
<appendConfigEndpoints filePath="file.xml" endpointUrl="serviceB" />

But I'm not there yet, currently I can't even get the count right

07:29:32.844: found 0 in xml
07:29:32.876: found 0 in xml
07:29:32.882: found 0 in xml
07:29:32.889: found 0 in xml

but my output is ok-ish just really missing the counter work

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<configuration>
  <endpoints>
    <endpoint url="serviceA"></endpoint>
    <endpoint url="serviceB"></endpoint>
    <endpoint url="serviceC"></endpoint>
    <endpoint url="serviceB"></endpoint>
  </endpoints>
</configuration>

UPDATE: I finally made it work and mostly was a problem of my not understanding the sequence and forgetting about the property being immutable... thank you for the help on the response, it helped me get there. ended up looking like this:

  <macrodef name="appendConfigEndpoints">
    <attribute name="filePath" />
    <attribute name="endpointUrl" />
    <sequential>
      <if>
        <not>
          <available file="@{filePath}"/>
        </not>
        <then>
          <echo message="@{filePath} not available, copy template and enrich." />
          <copy file="${currentScriptDirectory}/default/appl/sample.endpoints.xml" tofile="@{filePath}"/>
        </then>
      </if>
      <xmltask source="@{filePath}">
        <copy path="count(//endpoint[@url='@{endpointUrl}'])" property="endpointsCount" />
      </xmltask>
      <if>
        <equals arg1="${endpointsCount}" arg2="0" />
        <then>
          <xmltask source="@{filePath}" dest="@{filePath}" outputter="simple:2" clearBuffers="true">
            <insert path="/configuration/endpoints" xml="&lt;endpoint url='@{endpointUrl}' /&gt;${line.separator}" position="under" />
          </xmltask>
        </then>
        <else>
          <echo message="@{endpointUrl} already found in @{filePath}" />
        </else>
      </if>
      <var name="endpointsCount" unset="true" />
    </sequential>
  </macrodef>

Solution

  • Property existsEndpoint only ever having one value is due to the immutability of Ant properties. You can though make a localised property in a macro by adding

    <local name="existsEndpoint"/>
    

    at the beginning of the <sequential> block. Then each macro invocation can have its own property value.