2

I am integrating a FMPP transformation into our Java code base. For this, I am using the FMPP Java API. For practical reasons, I have two separate directories:

  • one which contains the template: <absolute path template>/template.ftlx
  • one which contains the data: <absolute path data>/data.xml

This somehow complicates things, as I have to work with absolute paths here. Typically, the data is inside the template directory (together with a ignoredir.fmpp file). However, in our application, the data is coming from an external source (it is uploadable via a REST API), while the template is in the classpath. This also means that the data directory isn't static.

I am struggling to define all this and get the transformation happening via the Java API. Currently, I have the following:

Settings s = new Settings(new File("."));

s.set(Settings.NAME_SOURCES, new File("<absolute path template>/template.ftlx").getAbsolutePath());
s.set(Settings.NAME_OUTPUT_FILE, new File("<absolute path output>/output.xml").getAbsolutePath());
s.execute();

The code snippet above is not complete, as I have to add the data. There are the Settings.NAME_DATA and Settings.NAME_DATA_ROOT properties, but I can't get it working. I tried setting Settings.NAME_DATA_ROOT as following:

s.set(Settings.NAME_DATA_ROOT, new File("<absolute path data>").getAbsolutePath());

Then, I get the exception that FreeMarker cannot find my data:

The following has evaluated to null or missing:
==> d  [in template "template.ftlx" at line 4, column 12]

In the template, I simply do:

<#list d.items>...</#list>

This makes sense that this would not work, as I did nowhere define that the data should be accessible via the d. hash (which I am doing below in config.fmpp). But I don't know how to define that properly via Settings.NAME_DATA and/or Settings.NAME_DATA_ROOT.

How can I inject my data file into all this? It should get the key d, so I can refer to d. in the template.

reference

Just as reference, if I create the following config.fmpp file in <absolute path config>, put the data.xml data file in directory <absolute path data> and call s.load(new File("<absolute path config>/config.fmpp")) before s.execute() above, everything is working fine.

data: {
    d: xml(<absolute path data>/data.xml)
}

All I have to figure out is doing this in a dynamic fashion via the Java API. I cannot use config.fmpp for this, as the location of the data isn't static (and, as far as I know, config.fmpp is not parametrizable).

working solution, with doubts

After some code reading, I got it working if I do the following:

Settings s = new Settings(new File("."));

s.set(Settings.NAME_SOURCES, new File("<absolute path template>/template.ftlx").getAbsolutePath());
s.set(Settings.NAME_OUTPUT_FILE, new File("<absolute path output>/output.xml").getAbsolutePath());
s.set(Settings.NAME_DATA, "{d:xml(<absolute path output>/data.xml)}");

s.execute();

Here, we pass {configuration:xml(<absolute path output>/data.xml)} as a TDD to the NAME_DATA property. Is this the way to go? It "feels" strange to construct a textual definition in our code. Is there a way to do this in pure Java?

  • Similarly as you set `sources` with `Settings.set`, you should be able to set `dataRoot`. You say that it doesn't work, but I think it should, so maybe look into more, why it doesn't work. – ddekany Sep 23 '19 at 22:18
  • I added additional information regarding setting `dataRoot`. Am I doing that right? Is there an example somewhere? I tried looking into `src/docs/examples` on github, but I can't find Java API examples. – Jessica_the_owl Sep 24 '19 at 05:20
  • 1
    @ddekany I got something working, see update at the bottom of the question. It works, but I'm not sure I'm doing it properly. Could you please take a look? Thank you, highly appreciated. – Jessica_the_owl Sep 25 '19 at 08:16
  • 1
    I think that if you have the `Engine` object, then `engine.addData("d", new XmlDataLoader(engine, args))` will do that. Yeah, it still has `List> args`... seems I did not consider calling that API from application code. Still better than calling `Settings` API, which is clearly about config files. And by the way, `Engine` has methods for the other settings you use too. – ddekany Sep 25 '19 at 21:09
  • That makes sense, I'll use `Engine` directly. – Jessica_the_owl Sep 26 '19 at 08:16

0 Answers0