1

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>
Miguel Costa
  • 627
  • 1
  • 12
  • 30
  • Do you need the set of endpoints to be unique, i.e. if serviceB is already present it shouldn't be added again? – martin clayton May 23 '19 at 08:30
  • I assumed so, I finally made it work (with help from a colleague) but with very ugly work around Also very ugly but I could not call this target repeatedly, I assume, because the destination file is only closed when the main target from where I call my macrodef is working. For example if I called serviceA, serviceB, serviceB in a row the file would always be empty at when doing the check. I needed to move this to a wrapper task – Miguel Costa May 24 '19 at 06:58

1 Answers1

1

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.

martin clayton
  • 76,436
  • 32
  • 213
  • 198
  • took me a while to understand what you said but you are correct. I did not use the local because the xpath does not let me, or I don't know how, but doing un unset let me fixed the issue. – Miguel Costa May 24 '19 at 11:24