Showing posts with label JAXB. Show all posts
Showing posts with label JAXB. Show all posts

Sunday, July 28, 2013

Spring OXM

OXM is Spring's Object/XML Mapping support which helps in conversion of Object to XML(Marshalling) and XML to Object(Unmarshalling). For this, spring has two interfaces Marshaller and Unmarshaller defined in spring-oxm under the package org.springframework.oxm.Marshaller . These can be defined as normal beans like other spring beans.
Marshaller and Unmarshaller has one method each for the actions as below.
public interface Marshaller {
    /**
     * Marshals the object graph with the given root into the provided Result.
     */
    void marshal(Object graph, Result result) throws XmlMappingException, IOException;
}
public interface Unmarshaller {
    /**
     * Unmarshals the given provided Source into an object graph.
     */
    Object unmarshal(Source source) throws XmlMappingException, IOException;
}
Implementation:
There are different marshaller instances in spring-oxm. We will go through CastorMashaller because it's easy to implement and configure.
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

 <bean id="converter" class="com.test.spring.oxm.converter.Converter">
  <property name="marshaller" ref="castorMarshaller" />
  <property name="unmarshaller" ref="castorMarshaller" />
 </bean>
 
 <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller">
  <property name="mappingLocation" value="classpath:mapping.xml" />
 </bean>
</beans>
CastorMarshaller is the marshaller and it requires one optional property "mappingLocation" which defines the mapping of class to xml. Sample mapping.xml file is
<?xml version="1.0" encoding="UTF-8"?>
<mapping>
 <class name="com.test.spring.oxm.entity.Employee">
  <map-to xml="employee" />
  <field name="empid" type="integer">
   <bind-xml name="id" node="element" />
  </field>
  <field name="name" type="string">
   <bind-xml name="ename" node="element" />
  </field>
   <field name="dob" type="date" >
   <bind-xml name="dob" node="element"  />
  </field>
   <field name="salary" type="double">
   <bind-xml name="salary" node="element" />
  </field>
 </class>
</mapping>
Points to be noted
  • property "mappingLocation" is optional. We can create CastorMarshaller without mappingLocation but it takes default values while binding
  • Each class tag defines the Object which to be marshalled/unmarshalled with set of field tags. Each corresponds to XML and object mapping.
  • type on the field indicates the type of the node and is mapped to Java Type.
  • map-to and bind-xml tags defines the elements and attributes in the xml. 
  • node attribute in bind-xml defines whether the field should be attribute or element

Code for bean "converter" is as below. Converter internally calls the marshall and unmarshall methods of CastorMarshaller.
package com.test.spring.oxm.converter;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;

public class Converter
{
    private Marshaller marshaller;
    private Unmarshaller unmarshaller;

    public Marshaller getMarshaller()
    {
        return marshaller;
    }

    public void setMarshaller(Marshaller marshaller)
    {
        this.marshaller = marshaller;
    }

    public Unmarshaller getUnmarshaller()
    {
        return unmarshaller;
    }

    public void setUnmarshaller(Unmarshaller unmarshaller)
    {
        this.unmarshaller = unmarshaller;
    }
    
    public String convertToXml(Object obj,String filepath)
    {
        String finalString = new String();
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(filepath);
            getMarshaller().marshal(obj, new StreamResult(fos));
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally {
            if(fos != null)
            {
                try
                {
                    fos.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
        return finalString;
    }
    
    public Object convertToObject(String filepath)
    {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(filepath);
            return getUnmarshaller().unmarshal(new StreamSource(fis));
        } 
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally {
            if(fis != null)
            {
                try
                {
                    fis.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}
The object which to marshalled or unmarshalled is
package com.test.spring.oxm.entity;

import java.util.Date;

public class Employee
{
    private int empid;
    private String name;
    private Date dob;
    private Double salary;

    public int getEmpid()
    {
        return empid;
    }
    public void setEmpid(int empid)
    {
        this.empid = empid;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    public Date getDob()
    {
        return dob;
    }
    public void setDob(Date dob)
    {
        this.dob = dob;
    }
    public Double getSalary()
    {
        return salary;
    }
    public void setSalary(Double salary)
    {
        this.salary = salary;
    }
}
Finally, the main program is
package com.test.spring.oxm;

import java.util.Date;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.test.spring.oxm.converter.Converter;
import com.test.spring.oxm.entity.Employee;

public class Application
{
    public static void main(String[] args)
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        Converter converter = (Converter)context.getBean("converter");
        Employee emp = new Employee();
        
        emp.setDob(new Date());
        emp.setEmpid(1234);
        emp.setName("Employee Name");
        emp.setSalary(2500.122);
       
        converter.convertToXml(emp,"cust.xml");
        
        Employee emp2 = (Employee)converter.convertToObject("cust.xml");
        System.out.println("Empid : "+emp2.getEmpid());
        System.out.println("Name  : "+emp2.getName());
        System.out.println("Salary: "+emp2.getSalary());
        System.out.println("DOB   : "+emp2.getDob());
    }
}
The xml file generated out of it is :
<?xml version="1.0" encoding="UTF-8"?>
<employee>
 <id>1234</id>
 <ename>Employee Name</ename>
 <dob>2013-07-28T21:17:29.331+05:30</dob>
 <salary>2500.122</salary>
</employee>

Sunday, May 19, 2013

JAXB XML Adapter

JAXB provides XMLAdapter to change the format of default JAXB types. For example, formatting decimal places in Decimal or BigDecimal, Date formatting etc.
How to do it?
First look at the following plain vanilla example of JAXB
package com.test;

import java.math.BigDecimal;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.datatype.XMLGregorianCalendar;

@XmlRootElement
public class Loan {
	private String name;
	private Long amount;
	private XMLGregorianCalendar startDate;
	private BigDecimal interestRate;
	public String getName() {
		return name;
	}
	@XmlElement
	public void setName(String name) {
		this.name = name;
	}
	public BigDecimal getInterestRate() {
		return interestRate;
	}
	@XmlElement
	public void setInterestRate(BigDecimal interestRate) {
		this.interestRate = interestRate;
	}
	public XMLGregorianCalendar getStartDate() {
		return startDate;
	}
	@XmlElement
	public void setStartDate(XMLGregorianCalendar startDate) {
		this.startDate = startDate;
	}
	public Long getAmount() {
		return amount;
	}
	@XmlElement
	public void setAmount(Long amount) {
		this.amount = amount;
	}	
}
If we marshall the Loan object, the output is similar to
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<loan>
    <amount>200000</amount>
    <interestRate>15.6</interestRate>
    <name>Name</name>
    <startDate>2013-05-19T16:02:51.080+05:30</startDate>
</loan>
Now, format the XMLGregorianCalendar to YYYY-MM-DD in XML. For this, we need to add a new XMLAdapter which can convert(marshall) from Calendar format to String format.
package com.test;

import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.datatype.XMLGregorianCalendar;

public class CalendarAdaptor extends XmlAdapter<String, XMLGregorianCalendar> {

	@Override
	public String marshal(XMLGregorianCalendar v) throws Exception {
		return v.getYear()+"-"+v.getMonth()+"-"+v.getDay();
	}

	@Override
	public XMLGregorianCalendar unmarshal(String v) throws Exception {
		return null;
	}
}
and Add the Adapter to the property where we would like to change. So the Loan.java file will be changed at the setStartDate method to following
        @XmlJavaTypeAdapter(CalendarAdaptor.class)
	public void setStartDate(XMLGregorianCalendar startDate) {
		this.startDate = startDate;
	}
When we marshall the same Loan object now, with the above change, the xml string will be
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<loan>
    <amount>200000</amount>
    <interestRate>15.6</interestRate>
    <name>Name</name>
    <startDate>2013-5-19</startDate>
</loan>
But the thing is, we need to change each property for the class definitions to affect this change. Instead of changing the classes, we can provide this adapter change at the package level by defining a class called package-info.java without changing the individual class properties. By doing this, all the XMLGregorianCalendar attributes in the package will follow the format defined by the Adapter. package-info.java
@XmlJavaTypeAdapter(value=CalendarAdaptor.class, type=XMLGregorianCalendar.class)
package com.test;

import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;