If the origin is XML, I suggest to go for a completely different approach: XSL. Facelets is XHTML based. You can easily use XSL to go from XML to XHTML. This is doable with a bit decent Filter
which kicks in before JSF is doing the works.
Here's a kickoff example.
persons.xml
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person>
<name>one</name>
<age>1</age>
</person>
<person>
<name>two</name>
<age>2</age>
</person>
<person>
<name>three</name>
<age>3</age>
</person>
</persons>
persons.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<xsl:output method="xml"
doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
<xsl:template match="persons">
<html>
<f:view>
<head><title>Persons</title></head>
<body>
<h:panelGrid columns="2">
<xsl:for-each select="person">
<xsl:variable name="name"><xsl:value-of select="name" /></xsl:variable>
<xsl:variable name="age"><xsl:value-of select="age" /></xsl:variable>
<h:outputText value="{$name}" />
<h:outputText value="{$age}" />
</xsl:for-each>
</h:panelGrid>
</body>
</f:view>
</html>
</xsl:template>
</xsl:stylesheet>
JsfXmlFilter
which is mapped on <servlet-name>
of the FacesServlet
and assumes that the FacesServlet
itself is mapped on an <url-pattern>
of *.jsf
.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
HttpServletRequest r = (HttpServletRequest) request;
String rootPath = r.getSession().getServletContext().getRealPath("/");
String uri = r.getRequestURI();
String xhtmlFileName = uri.substring(uri.lastIndexOf("/")).replaceAll("jsf$", "xhtml"); // Change this if FacesServlet is not mapped on `*.jsf`.
File xhtmlFile = new File(rootPath, xhtmlFileName);
if (!xhtmlFile.exists()) { // Do your caching job.
String xmlFileName = xhtmlFileName.replaceAll("xhtml$", "xml");
String xslFileName = xhtmlFileName.replaceAll("xhtml$", "xsl");
File xmlFile = new File(rootPath, xmlFileName);
File xslFile = new File(rootPath, xslFileName);
Source xmlSource = new StreamSource(xmlFile);
Source xslSource = new StreamSource(xslFile);
Result xhtmlResult = new StreamResult(xhtmlFile);
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer(xslSource);
transformer.transform(xmlSource, xhtmlResult);
} catch (TransformerException e) {
throw new RuntimeException("Transforming failed.", e);
}
}
chain.doFilter(request, response);
}
Run by http://example.com/context/persons.jsf and this filter will kick in and transform persons.xml
to persons.xhtml
using persons.xsl
and finally put persons.xhtml
there where JSF expect it is.
True, XSL has a bit of learning curve, but it's IMO the right tool for the job since the source is XML and destination is XML based as wel.
To do the mapping between the form and the managed bean, just use a Map<String, Object>
. If you name the input fields like so
<h:inputText value="#{bean.map.field1}" />
<h:inputText value="#{bean.map.field2}" />
<h:inputText value="#{bean.map.field3}" />
...
The submitted values will be available by Map
keys field1
, field2
, field3
, etc.