Search code examples
javastringtemplate-4

how do I iterate though a java list in stringtemplate?


I want to iterate through a hibernate query results inside stringtemplate. I've been looking for examples but I can't find anything.

can you please help? thanks


Solution

  • The syntax looks like

    <items :{ item | <item> }>
    

    Putting it together in Java:

    List<String> teams = Arrays.asList("Cats", "Birds", "Turtles");
    ST s = new ST( "<teams :{team | <team> }>");
    s.add("teams", teams);
    System.out.println(s.render());
    

    In this example, I iterate over the List and print each team that is in the teams list. The result that would be printed is:

    Cats Birds Turtles 
    

    We can explore the syntax that makes this happen. Before we do, remember, that the default delimiters in StringTemplate are less than < and greater than >. Since we didn't specify a different delimiter < > will be what we use in our example.See more about delimiters

    :{ }
    

    This set of symbols, the colon : and the open and closed brace {} can be read as "for each". In the example template, the code reads, for each team in teams print team. The left side of the vertical pipe | indicates the variable that will be created for each iteration. It will hold the current team from the list of teams. The print is composed of the <team> on the right side of the vertical pipe | and the left side of the closing brace }. Anything that is on the right side of the vertical pipe | and before the closing base } will be evaluated to be printed.

    :{ current value | everything in here will be printed }
    

    In order to build on the concept, let's use a more complex data structure.

    public class Player {
        private String name;
        private int age;
    
        public Person(String name, int age) { 
            this.name = name; 
            this.age = age; 
        }
        public int getAge() { return age; }
        public String getName() { return name; }
    }
    

    Now we can create a few players for our team:

    Player[] players = new Player[] {
        new Player("Bill", 29),
        new Player("Steve", 30),
        new Player("Toby", 15)
    };
    
    String playerTemplate = "<players:{ player |<player.name> is <player.age> <\\n>}>"
    ST s = new ST( playerTemplate  );
    s.add("players", Arrays.asList(players));
    System.out.println(s.render());
    

    Giving a result of

    Bill is 29
    Steve is 30
    Toby is 15
    

    Couple of things to note. We didn't access the properties age and name directly. ST called the methods getAge and getName. ST doesn't look to the properties. Instead, it looks to find the access methods.

    What if we just wanted to iterate over a list that contained another list. We can do that as well. First, let's build up our data structure and fill it with a couple of lists.

    List<List<String>> listOfLists = asList(
        asList("One", "Two", "Three"), 
        asList("Four", "Five"), 
        asList("Six", "Seven", "Eight", "Nine")
    );
    

    The template will look like the following.

    <list :{ items |<items :{ item |<item> }><\n>}>
    

    Our template, in this case, will just be a combination. The outer shell will iterate over the list we will hand in.

     <list :{ items |  what we will print   }>
    

    Then for each item, we will print out the items in its list.

    <items :{ item |<item> }>
    

    Once we put it all together

    String template = "<list :{ items |<items :{ item |<item> }><\\n>}>";
    ST st = new ST( template);
    st.add("list", listOfLists);
    System.out.println(st.render());
    

    We get a result that looks like the following.

    One Two Three 
    Four Five 
    Six Seven Eight Nine 
    

    Building on this concept a little more we can create a second data structure that contains a list of players. This will demonstrate how to iterate within iteration.

    The first thing we will need is a data structure that contains a list. For this we can create a Team for our players to be a part.

    public class Team {
        private List<Player> players;
        private String name;
    
        public Team (String name, List<Player> players) {
            this.players = players;
            this.name = name;
        }
    
        public List<Player> getPlayers() {
            return players;
        }
    
        public String getName() {
            return name;
        }
    }
    

    Notice that our team contains players. This composition will allow us to build up two iterations.

    Now that we have our data structure lets set everything together to make a couple of teams with some players.

    List<Team> teams = asList(
            new Team("Billings", asList(
                    new Player("Bill", 29),
                    new Player("Steve", 30),
                    new Player("Toby", 15)
            )),
            new Team("Laurel", asList(
                    new Player("Chad", 32),
                    new Player("Chuck", 29),
                    new Player("Will", 24),
                    new Player("Ben", 26)
            ))
    );
    

    Now lets create a template and fill in a few details:

    String simpleTeamTemplate = "<teams:{ team |<team.name> has <length(team.players)> players<\\n>}>";
    
    ST template = new ST( simpleTeamTemplate );
    template.add("teams", teams);
    
    System.out.println(template.render());
    

    That will print out

    Billings has 3 players
    Laurel has 4 players
    

    Our simple template is just about the same as our first template from above. The only real difference is that we are using a built-in method provided by ST length(). See more on functions here

    Let's increase the complexity of the templates a little to add in our second iteration.

    First, we will create our playersTemplate. This is almost identical to our playerTemplate template from above. The only difference is that we have our players coming from a team: team.players.

    String playersTemplate = "<team.players :{ player |<player.name> is <player.age><\\n>}>";
    

    Now we will construct a second template that contains the first. In this template we can iterate over teams and for each team we will print out the name, number of players length(team.players), and everything in the playersTemplate.

    String teamTemplate = "<teams:{ team |<team.name> has <length(team.players)> players<\\n>"+playersTemplate+"}>"; 
    

    Now let's put that all together.

    ST teamsTemplate = new ST( simpleTeamTemplate);
    teamsTemplate.add("teams", teams);
    
    System.out.println(teamsTemplate.render());
    

    That will print for us the following.

    Billings has 3 players
    Bill is 29
    Steve is 30
    Toby is 15
    Laurel has 4 players
    Chad is 32
    Chuck is 29
    Will is 24
    Ben is 26
    

    Now, you aren't really going to want to combine your templates in this way. Appending strings together to compose templates is rather silly. StringTemplate offers tools to make this combination of partial templates very easy. If you are curious about combining templates you can find out more here