How do I combine filter and projection operation in Spring EL?

Using Spring Expression Language (SpEL) we can filter a collection based on some criteria. We can also create a projection of a collection by collecting only a particular property from the collection objects.

Now you know that you have two good features of SpEL that are really powerful to use when working with collection objects manipulation. But you are wondering how to combine both of these filter and projection in one expression. Can you do this in Spring EL? The answer is yes! You can combine them both in one expression. Let’s see an example below.

We are going to use the same configuration used in the previous example:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <util:list id="books">
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="Essential C# 4.0" p:author="Michaelis" p:pages="450"/>
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="User Stories Applied" p:author="Mike Cohen" p:pages="268"/>
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="Learning Android" p:author="Marco Gargenta" p:pages="245"/>
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="The Ruby Programming Language"
              p:author="David Flanagan &amp; Yukihiro Matsumoto" p:pages="250"/>
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="Einstein" p:author="Walter Isaacson" p:pages="1000"/>
    </util:list>

    <bean id="library" class="org.kodejava.example.spring.model.Library">
        <property name="bookTitles" value="#{books.?[pages gt 250].![title]}"/>
    </bean>
</beans>

In the configuration above, when we define the library bean we set its bookTitles property using the filtering and projection operator. First we take only books that have more that 250 pages and then we create the projection that contains only the book title. So this expression give us all the book’s title of a book that has more than 250 pages.

To make the example complete here again the definition of the Book and the Library class.

package org.kodejava.example.spring.model;

public class Book {
    private Long id;
    private String title;
    private String author;
    private String type;
    private int pages;

    //
    // Getters & Setters
    //
}
package org.kodejava.example.spring.model;

import java.util.List;

public class Library {
    private List<Book> books;
    private List<String> bookTitles;
    
    //
    // Getters & Setters
    //
}

The main class the run the configuration file:

package org.kodejava.example.spring;

import org.kodejava.example.spring.model.Library;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpELFilterProjectionExample {
    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spel-filter-projection.xml");

        Library library = context.getBean("library", Library.class);

        for (String title : library.getBookTitles()) {
            System.out.println("title = " + title);
        }
    }
}

The result of the code snippet:

INFO: Loading XML bean definitions from class path resource [spel-filter-projection.xml]
title = Essential C# 4.0
title = User Stories Applied
title = Einstein

How do I create a projection of a collection using Spring EL?

In this example you will learn how to create a projection of a collection object. Using projection we can create a new collection with only a specific property from the original collection.

As an example, instead of returning a collection of Book objects we would like only to have the titles of the books. To do this we can use the Spring EL projection operator. The symbol use for this operator is ![].

Let’s begin by creating the Spring configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <util:list id="books">
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="Essential C# 4.0" p:author="Michaelis" p:pages="450"/>
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="User Stories Applied" p:author="Mike Cohen" p:pages="268"/>
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="Learning Android" p:author="Marco Gargenta" p:pages="245"/>
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="The Ruby Programming Language"
              p:author="David Flanagan &amp; Yukihiro Matsumoto" p:pages="250"/>
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="Einstein" p:author="Walter Isaacson" p:pages="1000"/>
    </util:list>

    <bean id="library" class="org.kodejava.example.spring.model.Library">
        <property name="bookTitles" value="#{books.![title]}"/>
    </bean>
</beans>

Here are the definition of the Book and Library class. The getters and setters methods were removed for simplicity of the snippet.

package org.kodejava.example.spring.model;

public class Book {
    private Long id;
    private String title;
    private String author;
    private String type;
    private int pages;

    //
    // Getters & Setters
    //
}
package org.kodejava.example.spring.model;

import java.util.List;

public class Library {
    private List<Book> books;
    private List<String> bookTitles;

    //
    // Getters & Setters
    //
}

Now, let’s talk about the Spring configuration above. The configuration was start by creating a collection of Books using the <util:elements>. The part that use the projection operator is this part of the configuration:

<bean id="library" class="org.kodejava.example.spring.model.Library">
    <property name="bookTitles" value="#{books.![title]}"/>
</bean>

The bean element above create a library bean of type org.kodejava.example.spring.model.Library. We assign the bean’s bookTitles property with values that are a projection of the +books+ collection where we take only the title of the books. ![projectionExpression] is the syntax of the projection operator.

The code snippet below will demonstrate and run our configuration file and print out the result:

package org.kodejava.example.spring;

import org.kodejava.example.spring.model.Library;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpELProjectionExample {
    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spel-projection.xml");

        Library library = context.getBean("library", Library.class);

        for (String title : library.getBookTitles()) {
            System.out.println("title = " + title);
        }
    }
}

And here are the result:

INFO: Loading XML bean definitions from class path resource [spel-projection.xml]
title = Essential C# 4.0
title = User Stories Applied
title = Learning Android
title = The Ruby Programming Language
title = Einstein

How do I filter collection members based on some criteria using Spring EL?

In some of the previous example you have seen that we use the square-braces [] operator to select items from collection. In this blog post you will learn how to filter some members of a collection with a certain criteria while using the Spring EL. To do this Spring EL give you another special operator, the filter operator which can be typed like .?[]. Where you can define the filter criteria inside the braces.

Beside the filter operator .?[]> there are also operator that can select the first and the last matching items from collection. You can use the .^[] for selecting the first match and the .$[] operator to select the last match items from collection respectively.

As an example we will demonstrate here that we want to find a collection of books that have pages more than 250 and assign it to Library bean’s books properties. So let’s begin by defining a class called Book and Library which will have the following properties:

package org.kodejava.example.spring.model;

public class Book {
    private Long id;
    private String title;
    private String author;
    private String type;
    private int pages;

    //
    // Getters & Setters
    //

    @Override
    public String toString() {
        return "Book{" +
                "title='" + title + ''' +
                ", author='" + author + ''' +
                ", pages=" + pages +
                '}';
    }
}
package org.kodejava.example.spring.model;

import java.util.List;

public class Library {
    private List books;

    // 
    // Getter & Setter
    //
}

After creating the Book and the Library class let’s now create the Spring configuration file for our demo. We will create a file and call it as spel-select-collection.xml with the following lines of configuration in it.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <util:list id="books">
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="Essential C# 4.0" p:author="Michaelis" p:pages="450"/>
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="User Stories Applied" p:author="Mike Cohen" p:pages="268"/>
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="Learning Android" p:author="Marco Gargenta" p:pages="245"/>
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="The Ruby Programming Language"
              p:author="David Flanagan &amp; Yukihiro Matsumoto" p:pages="250"/>
        <bean class="org.kodejava.example.spring.model.Book"
              p:title="Einstein" p:author="Walter Isaacson" p:pages="1000"/>
    </util:list>

    <bean id="lib1" class="org.kodejava.example.spring.model.Library">
        <property name="books" value="#{books.?[pages gt 250]}"/>
    </bean>

    <bean id="lib2" class="org.kodejava.example.spring.model.Library">
        <property name="books" value="#{books.^[pages gt 250]}"/>
    </bean>

    <bean id="lib3" class="org.kodejava.example.spring.model.Library">
        <property name="books" value="#{books.$[pages gt 250]}"/>
    </bean>
</beans>

The first thing that you can see in the configuration above is the <util-list> where we create a list of Book beans. Next we have three beans definition of the type Library where the books property was assigned with a collection of beans selected from the books list.

The lib1 will contains books that have pages more than 250. The lib2 will contains the first book found to have more than 250 pages while the lib3 will contains the last book found to have more than 250 pages.

Below is the code that will run our configuration file and the example output it will produce.

package org.kodejava.example.spring;

import org.kodejava.example.spring.model.Book;
import org.kodejava.example.spring.model.Library;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpELFilterCollection {
    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spel-filter-collection.xml");

        System.out.println("Library 1");
        Library lib1 = (Library) context.getBean("lib1");
        for (Book book : lib1.getBooks()) {
            System.out.println("    Book = " + book);
        }

        System.out.println("Library 2");
        Library lib2 = (Library) context.getBean("lib2");
        for (Book book : lib2.getBooks()) {
            System.out.println("    Book = " + book);
        }

        System.out.println("Library 3");
        Library lib3 = (Library) context.getBean("lib3");
        for (Book book : lib3.getBooks()) {
            System.out.println("    Book = " + book);
        }
    }
}
INFO: Loading XML bean definitions from class path resource [spel-filter-collection.xml]
Library 1
    Book = Book{title='Essential C# 4.0', author='Michaelis', pages=450}
    Book = Book{title='User Stories Applied', author='Mike Cohen', pages=268}
    Book = Book{title='Einstein', author='Walter Isaacson', pages=1000}
Library 2
    Book = Book{title='Essential C# 4.0', author='Michaelis', pages=450}
Library 3
    Book = Book{title='Einstein', author='Walter Isaacson', pages=1000}