Showing posts with label JSON. Show all posts
Showing posts with label JSON. Show all posts

Tuesday, October 15, 2019

Spring MVC - HTTP message converter

Quite often you need to provide users with the same data, but in different forms, like JSON, PDF, XLS, etc. If your application is Spring Framework based, this task can be achieved using HTTP message converters.

HTTP message converters are applied when HTTP request (or its parts) needs to be converted into type required for handler method argument (see: Handler methods - method arguments), or when value returned by handler method needs to be converted somehow to create HTTP response (see: Handler methods - Return values).

Spring Framework provides you with a set of predefined HTTP message converters, for ex. for byte arrays, JSON, etc. - This set can be modified or extended to your needs.
 
In this post we will focus on converting value returned from handler method into desired form, using example provided by me (see below for the link to the source code repository).

 Suppose that we have a controller returning some Team data, like this (yes, I know, I've ignored team Id)
@RestController
public class TeamDetailsController {

    @GetMapping("/teams/{teamId}/")
    public Team read() {
        final Set<TeamMember> members = new LinkedHashSet<>();
        members.add(new TeamMember("Albert Einstein", LocalDate.of(1879, 3, 14)));
        members.add(new TeamMember("Benjamin Franklin", LocalDate.of(1706, 1, 17)));
        members.add(new TeamMember("Isaac Newton", LocalDate.of(1643, 1, 4)));
        return new Team(members);
    }

}
In our example, handler method response will be, by default, converted into JSON:
{
  "members": [
    {
      "dateOfBirth": "1879-03-14",
      "name": "Albert Einstein"    },
    {
      "dateOfBirth": "1706-01-17",
      "name": "Benjamin Franklin"    },
    {
      "dateOfBirth": "1643-01-04",
      "name": "Isaac Newton"    }
  ]
}
If we would like to convert the data returned by the handler into XLS file, we can simply define a bean being HTTP message converter implementation, which will be activated by the HTTP Accept header:
@Service
public class TeamToXlsConverter extends AbstractHttpMessageConverter<Team> {

    private static final MediaType EXCEL_TYPE = MediaType.valueOf("application/vnd.ms-excel");

    TeamToXlsConverter() {
        super(EXCEL_TYPE);
    }

    @Override
    protected Team readInternal(final Class<? extends Team> clazz, final HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override
    protected boolean supports(final Class<?> clazz) {
        return (Team.class == clazz);
    }

    @Override
    protected void writeInternal(final Team team, final HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        try (final Workbook workbook = new HSSFWorkbook()) {
            final Sheet sheet = workbook.createSheet();
            int rowNo = 0;
            for (final TeamMember member : team.getMembers()) {
                final Row row = sheet.createRow(rowNo++);
                row.createCell(0)
                   .setCellValue(member.getName());
            }
            workbook.write(outputMessage.getBody());
        }
    }

}
You have to keep in mind that in our example, defined HTTP message converter will be applied always when the handler method returns value of type Team (see supports method), and HTTP Accept header matches "application/vnd.ms-excel". In this case, XLS file generated by the HTTP message converter is returned instead of JSON representation of Team.

Few links for the dessert:

Follow-ups:


This article has been republished on DZone's Java Zone (10/19/2019) and on Java Code Geeks (10/21/2019).

Saturday, January 29, 2011

jQuery Templates and JSON

Imagine the Web Application executing some functionality on the server using AJAX and processing data returned in JSON format. Very often such a processing is also updating UI with the provided data - recently we gained a powerful ally in this task - jQuery Templates.

This post will give you quick example of using jQuery Templates. We will focus on the client side of this process, assuming that the JSON response will look like this:
{"employer":{"id":1,"employees":[{"id":1,"firstName":"John","benefits":[{"name":"Healthy Employees","id":1,"type":"HEALTH_COVERAGE","startDate":1104534000000,"endDate":null},{"name":"Gold Autumn","id":2,"type":"RETIREMENT_PLAN","startDate":1104534000000,"endDate":null},{"name":"Always Secured","id":3,"type":"GROUP_TERM_LIFE","startDate":1104534000000,"endDate":null}]},{"id":2,"firstName":"Mary","benefits":[]},{"id":3,"firstName":"Eugene","benefits":[]}],"businessName":"Mighty Ducks"}}
And here comes the HTML/JavaScript part:
<html>
    <head>
    ...
        <script type="text/javascript" src="http://code.jquery.com/jquery-1.4.4.min.js"></script>
        <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
        <script type="text/javascript">
        $(document).ready(
            function() {
                $("#ajax-trigger").click(
                    function(event) {
                        event.preventDefault();
                        $.getJSON("templates.json", { 'employerId' : 1}, 
                            function(data) {
                                $("#employerList").tmpl(data).appendTo("#ajax-target");
                            });
                    });
            });
        </script>
        <script id="employerList" type="text/x-jquery-tmpl">            
            {{tmpl(employer) "#employerTemplate"}}
        </script>
        <script id="employerTemplate" type="text/x-jquery-tmpl">
            <h1>${businessName}</h1>
            <table>
                <caption>Employees</caption>
                {{each(i, employee) employees}}{{tmpl(employee) "#employeeTemplate"}}{{/each}}
            </table>
        </script>
        <script id="employeeTemplate" type="text/x-jquery-tmpl">
            <tr>
                <td>${firstName}</td>
            </tr>        
        </script>    
    </head>
    <body>
        <div>
            <a href="javacsript:void();" id="ajax-trigger">AJAX</a>
        </div>
        <div id="ajax-target"></div>               
    </body>
</html>
As you see the whole machinery is triggered when you click on the "AJAX" link, then JSON response is fetched from server using $.getJSON and processed using following code:

$("#employerList").tmpl(data).appendTo("#ajax-target");

Which in more human friendly language means - find the element having id employerList and render its content as template using data returned from server, then append the result to element having id ajax-target.
Our first template (employerList) extracts the employer information from the provided data, and calls another template for this specific employer:

{{tmpl(employer) "#employerTemplate"}}

The employer template in turn displays the employer's business name, and for each of its employees calls the last template we use in this example - employeeTemplate

<h1>${businessName}</h1>
<table>
    <caption>Employees</caption>
    {{each(i, employee) employees}}{{tmpl(employee) "#employeeTemplate"}}{{/each}}
</table>

The employee template is very simple in this example, it only displays employee's first name

<tr>
    <td>${firstName}</td>
</tr>

Let's take a look at the above code in action - first the page right after loading:
Nothing special, except the big white space ;) - now after processing the JSON response:
As you see it works :) - and it's pretty fast, this very simple template was rendered on my chrome browser in less than 3 ms.

One and only doubt related to this solution which is troubling me is its compliance with HTML standard, because we put here different html tags inside of script tag - and W3C validator is not happy with that ;) - the question is if we should ...

Few links for the dessert:
  1. jQuery API - Templates
  2. Introducing jQuery Templates 1: First Steps
  3. jQuery Templates is now an Official jQuery Plugin
  4. jQuery JavaScript Templates Tutorial: Nesting Templates 

Sunday, November 28, 2010

Spring MVC pitfall - overriding default beans

We all learn the whole life :) My last lesson was related to overriding default beans registered by mvc namespace parsers. It all started when I read Customizing WebDataBinder initialization section of Spring Framework 3.0 documentation. You can read there something like this:
To externalize data binding initialization, you can provide a custom implementation of the WebBindingInitializer interface, which you then enable by supplying a custom bean configuration for an AnnotationMethodHandlerAdapter, thus overriding the default configuration.
Encouraged by this citation I registered my own bean along the usual <mvc:annotation-driven />
<mvc:annotation-driven />

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="webBindingInitializer" ref="MyWebBindingInitializer" />
</bean>
    
<bean class="[...].DefultWebBindingInitializer" id="MyWebBindingInitializer" />
At this point I've encountered first problem - the order of beans registration matters in the above case - first registered handler will be used. This is reasonable, one AnnotationMethodHandlerAdapter is registered by Spring while processing <mvc:annotation-driven /> and one by me. First registered wins :)

I've moved my bean before <mvc:annotation-driven /> and started enjoying its work. After few days I was preparing some functionality returning one value from the handler method, which should be converted automagically to JSON response (see my posts: JSON - Jackson to the rescue and JSON - Jackson Serialization Narrowed). To my surprise, usual configuration didn't worked, I started digging through my own and Spring Framework code, and found the reason (which in fact can be also deduced from mvc:annotation-driven documentation) - my own bean didn't registered the same message converters as parser responsible for processing mvc:annotation-driven (AnnotationDrivenBeanDefinitionParser) and especially one needed by me: MappingJacksonHttpMessageConverter.

At this point I had few possibilities to solve this problem, I could for example register appropriate message converter myself, but I decided to go a different way, because solving the problems generated ourselves, is worse than avoiding those problems :)

I've prepared BeanPostProcessor implementation correcting the one property required by me:
public class MVCConfigurationPostProcessor implements BeanPostProcessor {

    private WebBindingInitializer webBindingInitializer;

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof AnnotationMethodHandlerAdapter) {
            ((AnnotationMethodHandlerAdapter) bean).setWebBindingInitializer(webBindingInitializer);
        }
        return bean;
    }

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }   

    public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
        this.webBindingInitializer = webBindingInitializer;
    }

}
Then I've corrected my Spring configuration to:
<bean class="[...].MVCConfigurationPostProcessor">
    <property name="webBindingInitializer" ref="MyWebBindingInitializer" />
</bean>
    
<bean id="MyWebBindingInitializer" class="[...].DefultWebBindingInitializer" />
    
<mvc:annotation-driven />
Finally I was able to enjoy both AnnotationMethodHandlerAdapter using my own WebBindingInitializer and JSON conversion working correctly :).

Thursday, October 21, 2010

JSON - Jackson Serialization Narrowed

In my last post - JSON - Jackson to the rescue - I've described quick example of JSON response creating. Let's back to this example and take a deeper look at the created response.

[{"id":1, "benefits":[{"name":"Healthy Employees", "id":1, "type":"HEALTH_COVERAGE", "startDate":1104534000000, "endDate":null}, {"name":"Gold Autumn","id":2,"type":"RETIREMENT_PLAN","startDate":1104534000000,"endDate":null},{"name":"Always Secured","id":3,"type":"GROUP_TERM_LIFE","startDate":1104534000000,"endDate":null}],"firstName":"John"},{"id":2,"benefits":[],"firstName":"Mary"},{"id":3,"benefits":[],"firstName":"Eugene"}]

There are situations, where parts of the data represented by domain entities should remain invisible for the public, and therefore shouldn't be serialized and exposed via JSON. We will try to narrow our response down to make it a little more secure from our point of view - by hiding the Employee's benefits.

First method to achieve this goal is usage of @JsonIgnore annotation.

@Entity
@Table(name = "EMPLOYEES")
public class Employee implements Serializable {
   ...
    @JsonManagedReference("employee-benefit")
    @OneToMany(mappedBy = "employee", cascade = CascadeType.PERSIST)    
    @JsonIgnore
    public List getBenefits() {
        return benefits;
    }
    ...
}

The above modification will change the response to:

{"employees":[{"id":1,"firstName":"John"},{"id":2,"firstName":"Mary"},{"id":3,"firstName":"Eugene"}]}

which is exactly what we need - but don't smile too wide yet ;) - consider situation when you want the same entity to be serialized in different ways depending on the request "context". It is very probable that you would like give to customer only the properties necessary in the response, while the admin should see much more in his part of application. @JsonIgnore is unusable in this case :( - but wait, don't panic yet - here we come to the second solution - @JsonView annotation

Let's modify the controller preparing the JSON response in the following manner:

@Controller
@RequestMapping("/employee-list.json")
public class EmployeeListController {

    private static final String MODEL_KEY_EMPLOYEES = "employees";

    @Autowired
    private EmployerDAO employerDAO;

    private final ObjectMapper objectMapper = new ObjectMapper();

    private final MappingJacksonJsonView view = new MappingJacksonJsonView();

    public EmployeeListController() {
        objectMapper.getSerializationConfig().setSerializationView(Employer.PublicView.class);
        view.setObjectMapper(objectMapper);
        view.setRenderedAttributes(new HashSet(Arrays.asList(MODEL_KEY_EMPLOYEES)));
    }

    @RequestMapping(method = RequestMethod.GET)
    public View handleGet(@RequestParam("employerId") Long employerId, Model model) {
        model.addAttribute(MODEL_KEY_EMPLOYEES, employerDAO.getEmployees(employerId));
        return view;
    }
}

As you see we will use the Spring Framework's MappingJacksonJsonView, with our own ObjectMapper instance. In controller's constructor we define which model attributes should be serialized (using setRenderedAttributes method), and switch serialization view to the Employer.PublicView. This will force Jackson to serialize only those properties which don't have @JsonView annotation at all, or have @JsonView annotations matching specified view. If we will modify our entities to look like this:

@Entity
@Table(name = "EMPLOYERS")
public class Employer implements Serializable {

    public interface PrivateView { }

    public interface PublicView { }
    ...
    @Column(name = "BUSINESS_NAME")
    @JsonView(PublicView.class)
    public String getBusinessName() {
        return businessName;
    }

    @JsonManagedReference("employer-employee")
    @OneToMany(mappedBy = "employer", cascade = CascadeType.PERSIST)
    @JsonView(PublicView.class)
    public List getEmployees() {
        return employees;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() {
        return id;
    }
    ...
}

@Entity
@Table(name = "EMPLOYEES")
public class Employee implements Serializable {
    ...
    @JsonManagedReference("employee-benefit")
    @OneToMany(mappedBy = "employee", cascade = CascadeType.PERSIST)
    @JsonView(PrivateView.class)
    public List getBenefits() {
        return benefits;
    }

    @JsonBackReference("employer-employee")
    @ManyToOne(optional = false)
    @JoinColumn(name = "EMPLOYER_ID")
    public Employer getEmployer() {
        return employer;
    }

    @Column(name = "FIRST_NAME")
    public String getFirstName() {
        return firstName;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() {
        return id;
    }
    ...
}

we will get exactly the same response as for @JsonIgnore usage, but in a little more flexible way :)

{"employees":[{"id":1,"firstName":"John"},{"id":2,"firstName":"Mary"},{"id":3,"firstName":"Eugene"}]}

The last possibility worth mentioning here is usage of mix-in annotations, let's change our controller once again to show the idea standing behind it.

@Controller
@RequestMapping("/employee-list.json")
public class EmployeeListController {

    @JsonIgnoreProperties("benefits")
    private static class EmployeePublicView extends Employee {
        // Empty by design ...
    }

    private static final String MODEL_KEY_EMPLOYEES = "employees";

    @Autowired
    private EmployerDAO employerDAO;

    private final ObjectMapper objectMapper = new ObjectMapper();

    private final MappingJacksonJsonView view = new MappingJacksonJsonView();

    public EmployeeListController() {
        objectMapper.getSerializationConfig().addMixInAnnotations(Employee.class, EmployeePublicView.class);
        view.setObjectMapper(objectMapper);
        view.setRenderedAttributes(new HashSet(Arrays.asList(MODEL_KEY_EMPLOYEES)));
    }

    @RequestMapping(method = RequestMethod.GET)
    public View handleGet(@RequestParam("employerId") Long employerId, Model model) {
        model.addAttribute(MODEL_KEY_EMPLOYEES, employerDAO.getEmployees(employerId));
        return view;
    }

}

As you see it is very similar to the controller above, the difference is new class EmployeePublicView extending Employee, and defining that benefits property will be ignored while serialization (using @JsonIgnoreProperties annotation). OK, but how this will help us with desired serialization of Employee instances? - the key factor is addMixInAnnotations usage - this method allows us to override the annotations defined on Employee class with those from the EmployeePublicView! Amazing and effective idea which doesn't need any domain entities modifications (you may safely remove the @JsonView annotations from the preview example)

Monday, October 18, 2010

JSON - Jackson to the rescue

Sometimes you have to fetch some data from the server in JavaScript, JSON is pretty good choice for this task.

Let's play with the Employer - Employee - Benefit example from the post JPA Demystified (episode 1) - @OneToMany and @ManyToOne mappings. We will use it inside the web application based on Spring Framework. Our first controller will return the employees list as the response body, in our case MappingJacksonHttpMessageConverter will be used automagically for converting the value returned by handleGet method to the response send to client.

@Controller
@RequestMapping("/employee-list.json")
public class EmployeeListController {
    @Autowired
    private EmployerDAO employerDAO;

    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public List<Employee> handleGet(@RequestParam("employerId") Long employerId) {
        return employerDAO.getEmployees(employerId);
    }
}

When we try to fetch the data for the first time, we encounter beautiful exception: JsonMappingException: Infinite recursion (StackOverflowError) - caused by bi-directional references between the Employer - Employee - Benefit.

Looking for the possible solution, I've found a note Handle bi-directional references using declarative method(s), and after reading it, I've corrected the domain entities in following way:

@Entity
@Table(name = "EMPLOYERS")
public class Employer implements Serializable {
...
    @JsonManagedReference("employer-employee")
    @OneToMany(mappedBy = "employer", cascade = CascadeType.PERSIST)
    public List getEmployees() {
        return employees;
    }
...
}

@Entity
@Table(name = "EMPLOYEES")
public class Employee implements Serializable {
...
    @JsonManagedReference("employee-benefit")
    @OneToMany(mappedBy = "employee", cascade = CascadeType.PERSIST)
    public List getBenefits() {
        return benefits;
    }

    @JsonBackReference("employer-employee")
    @ManyToOne(optional = false)
    @JoinColumn(name = "EMPLOYER_ID")
    public Employer getEmployer() {
        return employer;
    }
...
}

@Entity
@Table(name = "BENEFITS")
public class Benefit implements Serializable {
...
    @JsonBackReference("employee-benefit")
    @ManyToOne(optional = false)
    @JoinColumn(name = "EMPLOYEE_ID")
    public Employee getEmployee() {
        return employee;
    }
...
}

After performing the above changes, I could finally enjoy the JSON response returned by my code:

[{"id":1, "benefits":[{"name":"Healthy Employees", "id":1, "type":"HEALTH_COVERAGE", "startDate":1104534000000, "endDate":null}, {"name":"Gold Autumn","id":2,"type":"RETIREMENT_PLAN","startDate":1104534000000,"endDate":null},{"name":"Always Secured","id":3,"type":"GROUP_TERM_LIFE","startDate":1104534000000,"endDate":null}],"firstName":"John"},{"id":2,"benefits":[],"firstName":"Mary"},{"id":3,"benefits":[],"firstName":"Eugene"}]

And as usual some links for the dessert: