Reading and writing a XML file in JSF

2019-07-19 19:22发布

问题:

I found nothing online about how to read/write an XML document in JSF. I know something in JSP along with JSTL using XALAN. For example,

The following XML file is defined under /WEB-INF.

<?xml version="1.0" encoding="UTF-8"?>

<fruits>
    <fruit>
        <name>Orange</name>
        <price>10</price>
    </fruit>

    <fruit>
        <name>Banana</name>
        <price>20</price>
    </fruit>

    <fruit>
        <name>Apple</name>
        <price>30</price>
    </fruit>
</fruits>

This document can be read in JSP like the following.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>

<c:import var="items" url="/WEB-INF/TextXML.xml"/>
<x:parse var="fruits" doc="${items}"/>

<table rules="all" border="1">
    <tr>
        <th>index</th>
        <th>Fruit Name</th>
        <th>Price</th>
    </tr>

    <x:forEach var="item" select="$fruits/fruits/fruit" varStatus="loop">
        <tr>
            <td><c:out value="${loop.index+1}"/></td>
            <td><x:out select="$item/name" /></td>
            <td><x:out select="$item/price" /></td>
        </tr>
    </x:forEach>
</table>

This would populate an HTML table with three columns.

How can the same thing be achieved in JSF, maybe by using JAXB or something else?

回答1:

You can indeed use JAXB for this.

Let's assume that you already have a javabean representing <fruit>. You can even reuse an existing JPA entity for this.

public class Fruit {

    private String name;
    private BigDecimal price;

    // Add/generate getters+setters.
}

(note that javabean class and property names must exactly match XML element names <fruit>, <name> and <price>, otherwise you need a @XmlElement(name="actualXmlElementName") on either of those)

Now, create another javabean representing <fruits>, purely for usage in JAXB (it namely requires a @XmlRootElement class representing the XML root element, even though the XML document basically only contains a list of entities).

@XmlRootElement
public class Fruits { // Classname must match <fruits>, otherwise set it as @XmlRootElement(name="fruits")

    @XmlElement(name="fruit") // Name must thus match <fruit>. We could also make the property name "fruit" so that we can omit @XmlElement, but a getFruit() method returning a list of fruits isn't self-documenting.
    private List<Fruit> list;

    public Fruits() {
        // Keep default c'tor alive.
    }

    public Fruits(List<Fruit> list) {
        this.list = list;
    }

    public List<Fruit> getList() { 
        return list;
    }

}

As to reading/writing, you can in JSF perfectly read from /WEB-INF as follows:

InputStream input = externalContext.getResourceAsStream("/WEB-INF/fruits.xml");

But writing is a story apart. You aren't supposed to write files to deployment space. It isn't intented as permanent storage location for the obvious reason that all changes will get lost whenever you redeploy the WAR. You need to write it to a fixed path somewhere outside the deploy space. In the below example I'll assume that the file is moved to /var/webapp/fruits.xml on the server's disk file system.

So, you can use JAXB to read and write the XML file in a managed bean as follows:

@Named
@RequestScoped
public class Bean {

    private File file = new File("/var/webapp/fruits.xml");
    private JAXBContext jaxb; // Can be application scoped. It's thread safe.
    private List<Fruit> fruits;

    @PostConstruct
    public void init() throws JAXBException, IOException {
        jaxb = JAXBContext.newInstance(Fruits.class);
        fruits = ((Fruits) jaxb.createUnmarshaller().unmarshal(file)).getList();
    }

    public void save() throws JAXBException {
        jaxb.createMarshaller().marshal(new Fruits(fruits), file);
    }

    public List<Fruit> getFruits() { 
        return fruits;
    }

}

You can display it for editing in a JSF <h:dataTable> the usual way.

<h:dataTable value="#{bean.fruits}" var="fruit">
    <h:column><h:inputText value="#{fruit.name}" /></h:column>
    <h:column><h:inputText value="#{fruit.price}" /></h:column>
</h:dataTable>


标签: xml jsf jsf-2.2