I am fairly new to Freemarker and to template engine in general.
Basicly I want to be able to generate two XML files (from two different templates) using java methods to feed data to the template.
For example:
myTemplate1.ftl contain an opening and closing "res" tag with getRes() method as value (return 1 for exemple)
myTemplate2.ftl contain an opening and closing "result" tag and takes getResult() method as value (return 2 for exemple)
How can I write one class Main to process this without being specific (I do not want to write line per line, because then it would be pointless to create an engine)
Can you please help me understand how it work through an example if possible ?
Regards
EDIT with all the new informations :
#fustaki This is very frustrating. It seems I need to go step by step to understand what I'm doing. So... Here is a very dumb example of where I am :
template1.ftl
<Email>${item.getEmail()}</Email><Language>${item.getLanguage()}</Language>
FillMyTemplate1.java
public String getEmail(){ return "test#test.com" }
public String getLanguage(){ return "EN" }
I am using a property file in order to use introspection : prop.properties which contain :
template1=fr.freemarker.test.xml.FillMyTemplate1
MainTest.java
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
InputStream input = new FileInputStream("prop.properties");
prop.load(input);
Class<?> classe = Class.forName(prop.getProperty(args[0])); //where args[0] is "template1"
Configuration config=new Configuration();
config.setClassForTemplateLoading(MainTest.class, "templates");
config.setObjectWrapper(new DefaultObjectWrapper());
Template template=config.getTemplate(args[0]+".ftl");
Map<String, Object> dataModel = new HashMap<String, Object>();
Writer consoleWriter = new OutputStreamWriter(System.out);
dataModel.put("item", classe.newInstance());
template.process(dataModel, consoleWriter);
}
And the result : <Email>test#test.com</Email><Language>EN</Language>
In order to understand what you said, I need to know how a "data-provider" would look like in my case ? How can I get ride of this "item" in my map ? I understand the principle but not the technical solution...
Your Main process is in charge of producing the data (List, Map or other structured object) and templates will use the data to render your XMLs.
The tags used in the templates are independent from the data inside them.
Example where data are stored in a List that can be retrieved with a getItems() or in general is accessible from the freemarker engine.
<?xml>
<res>
<#list items as item >
<item>${item}</item>
</#list>
</res>
You can create as much templates (views) as you want using the same List items.
I hope this clarify your troubles.
here is good example of freemarker. this application generate spring mvc crud operation
using a freemarker template.
springmvcfreemarker
Related
I'm not going to lie I'm really bad at making regular expressions. I'm currently trying to parse a text file that is giving me a lot of issues. The goal is to extract the data between their respective "tags/titles". The file in question is a .qbo file laid out as follows personal information replaced with "DATA": The parts that I care about retrieving are between the "STMTTRM" and "/STMTTRM" tags as the rest I don't plan on putting in my database, but I figured it would help others see the file content I'm working with. I apologize for any confusion prior to this update.
FXHEADER:100
DATA:OFXSGML
VERSION:102
SECURITY:NONE
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE
<OFX>
<SIGNONMSGSRSV1><SONRS>
<STATUS><CODE>0</CODE><SEVERITY>INFO</SEVERITY></STATUS>
<DTSERVER>20190917133617.000[-4:EDT]</DTSERVER>
<LANGUAGE>ENG</LANGUAGE>
<FI>
<ORG>DATA</ORG>
<FID>DATA</FID>
</FI>
<INTU.BID>DATA</INTU.BID>
<INTU.USERID>DATA</INTU.USERID>
</SONRS></SIGNONMSGSRSV1>
<BANKMSGSRSV1>
<STMTTRNRS>
<TRNUID>0</TRNUID>
<STATUS><CODE>0</CODE><SEVERITY>INFO</SEVERITY></STATUS>
<STMTRS>
<CURDEF>USD</CURDEF>
<BANKACCTFROM>
<BANKID>DATA</BANKID>
<ACCTID>DATA</ACCTID>
<ACCTTYPE>CHECKING</ACCTTYPE>
<NICKNAME>FREEDOM CHECKING</NICKNAME>
</BANKACCTFROM>
<BANKTRANLIST>
<DTSTART>20190717</DTSTART><DTEND>20190917</DTEND>
<STMTTRN><TRNTYPE>POS</TRNTYPE><DTPOSTED>20190717071500</DTPOSTED><TRNAMT>-5.81</TRNAMT><FITID>3893120190717WO</FITID><NAME>DATA</NAME><MEMO>POS Withdrawal</MEMO></STMTTRN>
<STMTTRN><TRNTYPE>DIRECTDEBIT</TRNTYPE><DTPOSTED>20190717085000</DTPOSTED><TRNAMT>-728.11</TRNAMT><FITID>4649920190717WE</FITID><NAME>CHASE CREDIT CRD</NAME><MEMO>DATA</MEMO></STMTTRN>
<STMTTRN><TRNTYPE>ATM</TRNTYPE><DTPOSTED>20190717160900</DTPOSTED><TRNAMT>-201.99</TRNAMT><FITID>6674020190717WA</FITID><NAME>DATA</NAME><MEMO>ATM Withdrawal</MEMO></STMTTRN>
</BANKTRANLIST>
<LEDGERBAL><BALAMT>2024.16</BALAMT><DTASOF>20190917133617.000[-4:EDT]</DTASOF></LEDGERBAL>
<AVAILBAL><BALAMT>2020.66</BALAMT><DTASOF>20190917133617.000[-4:EDT]</DTASOF></AVAILBAL>
</STMTRS>
</STMTTRNRS>
</BANKMSGSRSV1>
</OFX>
I want to be able to end with data that looks or acts like the following so that each row of data can easily be added to a database:
Example Parse
As David has already answered, It is good to parse the POS output XML using Java. If you are more interested about about regex to get all the information, you can use this regular expression.
<[^>]+>|\\n+
You can test in the following sites.
https://rubular.com/
https://www.regextester.com/
Given this is XML, I would do one of two things:
either use the Java DOM objects to marshall/unmarshall to/from Java objects (nodes and elements), or
use JAXB to achieve something similar but with better POJO representation.
Mkyong has tutorials for both. Try the dom parsing or jaxb. His tutorials are simple and easy to follow.
JAXB requires more work and dependencies. So try DOM first.
I would propose the following approach.
Read file line by line with Files:
final List<String> lines = Files.readAllLines(Paths.get("/path/to/file"));
At this point you would have all file line separated and ready to convert the string lines into something more useful. But you should create class beforehand.
Create a class for your data in line, something like:
public class STMTTRN {
private String TRNTYPE;
private String DTPOSTED;
...
...
//constructors
//getters and setters
}
Now when you have a data in each separate string and a class to hold the data, you can convert lines to objects with Jackson:
final XmlMapper xmlMapper = new XmlMapper();
final STMTTRN stmttrn = xmlMapper.readValue(lines[0], STMTTRN.class);
You may want to create a loop or make use of stream with a mapper and a collector to get the list of STMTTRN objects:
final List<STMTTRN> stmttrnData = lines.stream().map(this::mapLine).collect(Collectors.toList());
Where the mapper might be:
private STMTTRN mapLine(final String line) {
final XmlMapper xmlMapper = new XmlMapper();
try {
return xmlMapper.readValue(line, STMTTRN.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
This is my YAML file:
case 1: ["Jackson","23","Salt Lake City"]
case 2: ["Rachael","35","San Diego"]
#this will keep building and possibly have 1000 rows or so
I want these all into a 2D array like this:
{{"Jackson","23","Salt Lake City"},
{"Rachael","35","San Diego"}}
Basically I want to use these 2 sets of data in a TestNG data provider.
So I will create a DataProvider method which will return this 2D object. And the test method using this data provider will iterate through it. First it will take the "Jackson" data. The second round will take "Rachael" data.
I have used Jackson for serialization before like so:
public class TestCase {
#JsonProperty("Test")
private List<String> data;
public List<String> getData() {
return data;
}
public void setData(List<String> data) {
this.data = data;
}
}
But for this to work my YAML should be like so:
Test: ["Jackson","23","Salt Lake City"]
Test: ["Rachael","35","San Diego"]
But then the ObjectMapper only reads the last row. In this case the "Rachael" row.
Actually I don't care about "case 1" or "case 2". I want to write a method which will return an Object[][] containing the following:
{{"Jackson","23","Salt Lake City"},
{"Rachael","35","San Diego"}}
How do I achieve this inside my method?
I want this method to iterate through each row of the YAML file and put each row into an arraylist which stays inside the Object[][].
If you guys have better ways to manage test data using YAML please let me know.
If you don’t need keys (case 1, case 2) for your data, you can simplify your Yaml file
just change to
- ["Jackson","23","Salt Lake City"]
- ["Rachael","35","San Diego"]
then read as ArrayList by using SnakeYAML library and convert to Object[][]
You can try the next snippet in you DataProvider method
InputStream input = new FileInputStream(new File("path_to_your_file"));
ArrayList<ArrayList> list = (ArrayList<ArrayList>) new Yaml().load(input);
Object[][] data = list.stream()
.map(dataSet -> dataSet.toArray())
.toArray(Object[][]::new);
Handling test data is a common problem for automation QA’s. Choosing YAML files is already a good decision. We use Selenium Automation Bundle as it has already integrated a solution for test data. There is no description of the solution in Readme, but here it is an example of a test, and here it is an example of a file with test data which are enough to start using it. Though the bundle uses Groovy, you can write Java code in groovy classes without any doubts.
I'm using the template engine StringTemplate for some templates (obviously).
What I want is to be able to store the templates I have in seperate files, ofcourse I can do that with simple .txt files and reading them into a String, looks a bit like this then
ST template = new ST(readTemplateFromFile("template.txt"))
private String readTemplateFromFile(String templateFile){
//read template from file
}
But what I was wondering is if there's functionality in the StringTemplate engine to do that automatically. SO that i don't have to write code that already exists.
I've read something about Group Files but I don't quite understand that,are those like Template files? Or am I completely missing something?
Yes, there is functionality available that can be used directly without providing your own file loading code.
From the ST JavaDoc:
To use templates, you create one (usually via STGroup) and then inject attributes using add(java.lang.String, java.lang.Object). To render its attacks, use render().
To follow that advice the following code can be used.
First, create a file called exampleTemplate.stg and place it on your classpath.
templateExample(param) ::= <<
This is a template with the following param: (<param>)
>>
Then, render the template by using the following code:
// Load the file
final STGroup stGroup = new STGroupFile("exampleTemplate.stg");
// Pick the correct template
final ST templateExample = stGroup.getInstanceOf("templateExample");
// Pass on values to use when rendering
templateExample.add("param", "Hello World");
// Render
final String render = templateExample.render();
// Print
System.out.println(render);
The output is:
This is a template with the following param: (Hello World)
Some additional notes:
STGroupFile is a subclass of STGroup. There are other subclasses as well that you find out more about in the JavaDoc.
In the example above the template file was placed on the classpath. This is not a requirement, files can be placed in a relative folder or a in an absolute folder as well.
I have been tasked with an assignement at work, that needs persistance of client side settings. These settings are represented by a XML-file in the C# version of the framework I am implementing, and it represents objects with an hierarchical structure of up to six levels. Also, the number of objects in the different levels are sometimes arbitrary, like an ArrayList. Object serialization is unfortunately not an option, due to a requirement of being able to edit the file directly. It has been more than implied that I have to implement the persistance of these settings as a .properties-file, but I am having some concerns about if that is a good idea.
As far as I know, a .properties-file can only be used in the same way as a HashMap, with a key-value pairing of only Strings. The representation of complex objects seems only possible with long, complex key strings, representing the whole "path" of every single element, including it's individual index in possibly several lists.
filters.filter3.traces.rule4.add1.value="8"
I have not been able to find any decent ways to persist hierarchical objects in a .properties-file, but I have not been able to find any definite proof that it is not possible, either.
So, is a .properties-file suitable for storing hierarchical settings-objects? Or should I implement my properties as an XML-file or JSON instead?
You have a perfect example in the form of log4j: here is property sample from here.
log4j.appender.R = org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.File = logs/bensApps.log
log4j.appender.R.Append = true
log4j.appender.R.DatePattern = '.'yyy-MM-dd
log4j.appender.R.layout = org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n
If you have a hierarchy in those keys, like:
Map<String,Object> root = new HashMap<>();
root.put("level1", "foobar");
root.put("level2", Collections.singletonMap("p", "foobar");
It is up to you to translate it into:
level1=foobar
level2.p=foobar
And when reading the file, to split . into submap:
Map<String, Object> root = new HashMap<>();
Properties propz = new Properties();
// load from your file
for (Map.Entry<String,Object> entry : propz.entrySet()) {
String[] path = entry.getKey().split('\\.');
Map<String,Object> parent = root;
int n = path.length - 1;
for (int i = 0; i < n; ++i) {
String p = path[i];
Object child = parent.get(p);
if (null == child) {
Map<String,Object> _p = new HashMap<>();
parent.put(p, _p);
parent = _p;
} else if (child instanceof Map) {
parent = (Map<String,Objext>) child;
} else {
// it is up to you to do something when the path extends beyond a "final" key
}
}
parent.put(path[n], entry.getValue());
}
However, this is a "reinvent the wheel" pattern, the other answers point you probably better solution than doing it by yourself. This example also show you one kind of problem:
p1=foobar
p1.p2=foobar
In the case of a machine generated properties, this won't happen, and an exception is perhaps the best answer. But in the case of human manipulated properties, this may have some meaning.
On a side note, since Java 7 come with JAXB, you can also do it in full XML, without requiring additional libraries. And using a hierarchy of some sort.
I would stick with an XML file. Nothing irritates users more than having different formats with different variants of an API framework.
You can use libraries such as Apache XMLBeans to simplify the process of reading and writing to XML files, using schema files to auto-generate appropriate Java classes.
Properties files can be used to store hierarchical data, but it's a real pain to edit that stuff by hand and it's easy to introduce errors. Some would argue that XML editing isn't a breeze either, but at least there exists editors that can make this easier when linked with an XSD.
I have always found this way of storing hierarchical configuration data quite cumbersome, but it is very well possible. For example, it is (used to be?) the most common way of configuring Log4j, but from my experience, more and more developers are switching to XML here as well.
I really fail to see the point of using properties files for this purpose.
I think you've already got the gist of it - Java properties files can express hierarchies, but awkwardly.
Take a look at the Typesafe Config library, which is what Play Framework uses. Its primary language is HOCON (human-optimized config notation) which is great for expressing hierarchical properties. The library also plays well with both properties files and JSON, so you can easily support any and all of these languages simultaneously.
In a Java properties file, as you've said, you're restricted to only using dotted keys
a.b.c = 42
In HOCON, you can choose to either do that or to nest sections with curly braces. So that example could be expressed in any of the following ways:
a.b.c : 42
a {
b.c : 42
}
a.b {
c : 42
}
a {
b {
c : 42
}
}
Does anyone know if it is possible to get templates from different paths with velocity? After initialization Velocity refuses to change the "file.resource.loader.path".
This is my code:
public Generator(){
Properties p = new Properties();
p.setProperty("resource.loader", "file");
p.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
p.setProperty("file.resource.loader.path", "");
Velocity.init(p);
}
The templates can be located in different locations ( the user can select one with a file dialog ). So I have this code upon fetching the template out of velocity
private Template fetch (String templatePath) {
out_println("Initializing Velocity core...");
int end = templatePath.lastIndexOf(File.separator);
Properties p = new Properties();
p.setProperty("file.resource.loader.path", templatePath.substring(0, end));
Velocity.init(p);
return Velocity.getTemplate(templatePath.substring(end+1));
}
This is not working. It seems that once Velocity is initialized it can't be reset with different properties. Any suggestions on how to solve this problem?
Possible Program flow:
User selects group that needs to be filled into the template
User selects a template to use (can be located anywhere on the hdd)
User presses generate
Velocity can be used in two ways: the singleton model or the separate instance model. You are currently using the singleton model in which only one instance of the Velocity engine in the JVM is allowed.
Instead, you should use the separate instance model which allows you to create multiple instances of Velocity in the same JVM in order to support different template directories.
VelocityEngine ve = new VelocityEngine();
ve.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, "path/to/templates");
ve.init();
Template t = ve.getTemplate("foo.vm");
Adding to the points above:
Even if one is using non-singleton model i.e using VelocityEngine object. Multiple paths can be configured by giving comma separated values to the property.
[file.resource.loader.class=path1,path2]
In such a case velocity engine will look for template in path1 first and then in path2
Consider instead of using singleton Velocity class creating and initializing new VelocityEngine before step 3.
In my case I am using Velocity with Servlets in an Eclipse Dynamic Web Project.
I couldn't actually reset the path, but I could put a subdirectory under /WebContent folder and then organize my templates that way... and have nested subdirectories as well.
RequestDispatcher requestDispatcher =
request.getRequestDispatcher("/velocity_templates/index.vm");
This simple solution was all I needed ... didn't need to mess with velocity.properties in web.xml or setting them programmatically (in each case, neither approach worked for me unfortunately when I tried).
Note that when I do template includes with #parse(..) command, I need to use the same path prefix inside the template .vm file as I did in the example code for my servlet.