Friday, 23 September 2011

Reading XML files in java

Example description: I have to create a custom group store so I could add them to the groups retrieved from LDAP. My choice is storing them in XML files. So I thought someone might find my experience interesting.

Prerequisites: Eclipse for java development with m2e plugin.

Create XSD file

Why? This will make creating XML files very easy and will ensure that those files will be readable as expected. I won't go into details, since it's not so hard to create even very complex schema definition files. But here's the result (file /src/main/resources/lv/rtu/itd/vivs/user-groups-1.0.xsd):

<schema elementformdefault="qualified" targetnamespace="http://vivs.rtu.lv/schemas/user-groups" xmlns:tns="http://vivs.rtu.lv/schemas/user-groups" xmlns="http://www.w3.org/2001/XMLSchema">

  <element name="user-groups" type="tns:user-groups-type">

  <complextype name="user-groups-type">
    <sequence>
      <element maxoccurs="unbounded" name="user-groups" type="tns:user-group" />
    </sequence>
  </complextype>

  <complextype name="user-group">
    <sequence>
      <element name="uid" type="string" />
      <element maxoccurs="unbounded" name="group" type="string" />
    </sequence>
  </complextype>

</element>

Configure class generation

Create file src/main/binding/bindings.xjb (this will instruct the generator how to create new classes):

<bindings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns="http://java.sun.com/xml/ns/jaxb"
  xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" 
  xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
  version="2.1">

  <bindings>
    <globalBindings>
      <serializable uid="1" />
      <xjc:simple />
    </globalBindings>
  </bindings>

  <bindings schemaLocation="../resources/lv/rtu/itd/vivs/user-groups-1.0.xsd">
    <schemaBindings>
      <package name="lv.rtu.itd.vivs.xml.user_groups" />
    </schemaBindings>
  </bindings>

</bindings>

Configure pom.xml and add something like this in order to tell Maven that it must now generate classes using jaxb2:

  <build>
    <plugins>
      <plugin>
        <groupId>org.jvnet.jaxb2.maven2</groupId>
        <artifactId>maven-jaxb2-plugin</artifactId>
        <version>0.7.4</version>
        <executions>
          <execution>
            <id>generate-xjc-sources</id>
            <phase>generate-sources</phase>
            <goals>
              <goal>generate</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <bindingDirectory>src/main/binding</bindingDirectory>
          <schemaDirectory>src/main/resources</schemaDirectory>
          <schemaIncludes>
            <include>lv/rtu/itd/vivs/*.xsd</include>
          </schemaIncludes>
          <extension>true</extension>
          <args>
            <arg>-no-header</arg>
            <arg>-readOnly</arg>
            <arg>-mark-generated</arg>
          </args>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-xjc</artifactId>
            <version>2.2.1</version>
          </dependency>
        </dependencies>
      </plugin>
    </plugins>
  </build>

Now, probably, you must click on project and choose Maven > Update Project Configuration

Java code

Create interface for class:

package lv.rtu.itd.vivs.service;

import java.util.Set;

public interface GroupService {
    Set getGroups(String uid);
}

Create class that will unmarshal (read) the XML document:

package lv.rtu.itd.vivs.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;

import lv.rtu.itd.vivs.service.GroupService;
import lv.rtu.itd.vivs.xml.user_groups.UserGroup;
import lv.rtu.itd.vivs.xml.user_groups.UserGroups;

public class XmlGroupService implements GroupService {
    
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    private UserGroups groups;
    
    public void setFilename(String filename) {
        JAXBContext ctx;
        try {
            ctx = JAXBContext.newInstance("lv.rtu.itd.vivs.xml.user_groups");
            Unmarshaller unmarshaller = ctx.createUnmarshaller();
            InputStream is = new ClassPathResource(filename).getInputStream();
            this.groups = (UserGroups) unmarshaller.unmarshal(is);
        } catch (JAXBException e) {
            logger.warn(e.getMessage(), e);
        } catch (IOException e) {
            logger.warn(e.getMessage(), e);
        }
    }
    
    @Override
    public Set getGroups(String uid) {
        for (UserGroup group : groups.getUserGroups()) {
            if (group.getUid().equals(uid)) {
                return new HashSet(group.getGroups());
            }
        }
        return new HashSet();
    }
}

Create test class (under src/test/java):

package lv.rtu.itd.vivs.service;

import java.util.HashSet;
import java.util.Set;

import lv.rtu.itd.vivs.service.impl.XmlGroupService;

import org.junit.Assert;
import org.junit.Test;

public class XmlGroupsTester {
    
    @Test()
    public void testGetGroups() {
        XmlGroupService service = new XmlGroupService();
        service.setFilename("user-groups.xml");
        
        Set expected = new HashSet();
        Assert.assertEquals(expected, service.getGroups("not-existing-user"));
        
        expected.add("my/demo/group");
        expected.add("my/demo/group2");
        expected.add("my/demo/group3");
        Assert.assertEquals(expected, service.getGroups("user.demo"));
    }
}

Now create XML file (src/test/resources/user-groups.xml:

<?xml version="1.0" encoding="UTF-8"?>
<user-groups xmlns="http://vivs.rtu.lv/schemas/user-groups" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://vivs.rtu.lv/schemas/user-groups http\://vivs.rtu.lv/schemas/user-groups">
  <user-groups>
    <uid>user.demo</uid>
    <group>my/demo/group</group>
    <group>my/demo/group2</group>
    <group>my/demo/group3</group>
  </user-groups>
</user-groups>

Now you can run tests and check the results.

No comments:

Post a Comment