I'm attempting to write a small library which will convert Java objects, through reflection, into XML. I've got the majority of it working, but ran into an error when attempting to iterate through an array.
Here are the domain objects i'm using for testing:
In Company.java:
import java.util.List;
import com.google.common.collect.Lists;
public class Company
{
public Employee employeeArray[];
public Employee[] getEmployeeArray()
{
return employeeArray;
}
public void setEmployeeArray(Employee[] employeeArray)
{
this.employeeArray = employeeArray;
}
}
In Employee.java:
public class Employee
{
public String firstName;
public String lastName;
public Employee() {}
public Employee(String firstName, String lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
}
The core of the library (ObjectXMLWriter.java):
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.List;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import com.falcondev.web.DOMFactory;
import com.google.common.collect.Lists;
public class ObjectXMLWriter
{
private static final Logger logger = Logger.getLogger(ObjectXMLWriter.class);
private String fileLocation;
private Object object;
private boolean shouldOverride;
public ObjectXMLWriter(String fileLocation, Object object) {
this.fileLocation = fileLocation;
this.object = object;
this.shouldOverride = false;
}
public ObjectXMLWriter(String fileLocation, Object object, boolean shouldOverride) {
this(fileLocation, object);
this.shouldOverride = shouldOverride;
}
public String getFileLocation()
{
return fileLocation;
}
public void setFileLocation(String fileLocation)
{
this.fileLocation = fileLocation;
}
public Object getObject()
{
return object;
}
public void setObject(Object object)
{
this.object = object;
}
public boolean isShouldOverride()
{
return shouldOverride;
}
public void setShouldOverride(boolean shouldOverride)
{
this.shouldOverride = shouldOverride;
}
public boolean saveObject() throws Exception {
boolean saveSuccessful = false;
Document document = createDocument();
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
StringWriter stringWriter = new StringWriter();
StreamResult result = new StreamResult(stringWriter);
DOMSource source = new DOMSource(document);
transformer.transform(source, result);
String xmlString = stringWriter.toString();
//print XML
System.out.println("Here's the xml:\n\n" + xmlString);
//TODO save XML file
return saveSuccessful;
}
private Node createNode(Document document, Object object) throws Exception {
Node node = document.createElement(getObjectClassName(object));
logger.trace("NODE: " + node);
if(node != null) {
//create children nodes from object fields
List<Field> fields = Lists.newArrayList(object.getClass().getFields());
for(Field field: fields) {
Object obj = field.get(object);
logger.trace("OBJECT: " + obj);
if(obj == null || !checkInstantiability(obj) || field.getType().isAssignableFrom(List.class) || field.getType().isArray()) {
logger.debug("ATTEMPTING TO CREATE NODE FOR FIELD: " + field.getName());
logger.debug("FIELD TYPE: " + field.getType());
//TODO add types as needed
String fieldValue = "";
if(List.class.isAssignableFrom(field.getType())) { //TODO check if object is iterable instead
//TODO figure out how to iterate through iterable's
}
else if(field.getType().isArray()) {
Object array = field.get(obj); //Fails here
int length = Array.getLength(array);
for (int i = 0; i < length; i++) {
System.out.println(Array.get(array, i));
node.appendChild(createNode(document, Array.get(array, i)));
}
}
else if(field.getType() == Class.class) {
fieldValue = obj.toString().replaceFirst("class ", "");
}
else {
fieldValue = obj.toString();
}
logger.debug("FIELD OBJECT VALUE: '" + fieldValue + "'");
//TODO check for annotation to choose whether to create element or attribute
Element element = document.createElement(field.getName());
element.setTextContent(fieldValue);
node.appendChild(element);
}
else {
logger.debug("ATTEMPTING TO CREATE OBJECT NODE FOR FIELD: " + field.getName());
node.appendChild(createNode(document, obj));
}
}
}
return node;
}
private Document createDocument() throws Exception {
Document document = DOMFactory.create();
if(checkInstantiability(object)) {
Node rootNode = createNode(document, object);
document.appendChild(rootNode);
}
else {
logger.error("CANNOT SAVE UNINSTANTIABLE OBJECT. DOCUMENT MUST HAVE A ROOT NODE");
}
return document;
}
private String getObjectClassName(Object object) {
return object.getClass().getSimpleName().toLowerCase();
}
private boolean checkInstantiability(Object object) {
try
{
object.getClass().newInstance();
}
catch (InstantiationException exception)
{
return false;
}
catch (IllegalAccessException exception)
{
return false;
}
return true;
}
}
And the Test driver (Test.java):
public class Test
{
public static void main(String[] args) throws Exception
{
Employee[] employees = new Employee[3];
employees[0] = new Employee("Tom", "C");
employees[1] = new Employee("Paul", "E");
employees[2] = new Employee("George", "A");
Company company = new Company();
company.setEmployeeArray(employees);
new ObjectXMLWriter("resources/test2.xml", company).saveObject();
}
}
The error I get when running this code is as follows:
Exception in thread "main" java.lang.IllegalArgumentException: Can not set [Lcom.falcondev.orm.test.Employee; field com.falcondev.orm.test.Company.employeeArray to [Lcom.falcondev.orm.test.Employee;
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:37)
at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:18)
at java.lang.reflect.Field.get(Field.java:358)
at com.falcondev.orm.ObjectXMLWriter.createNode(ObjectXMLWriter.java:116)
at com.falcondev.orm.ObjectXMLWriter.createDocument(ObjectXMLWriter.java:149)
at com.falcondev.orm.ObjectXMLWriter.saveObject(ObjectXMLWriter.java:74)
at com.falcondev.orm.test.Test.main(Test.java:37)
I realize this is quite a bit of code to post. I tried the following example as shown in this post (http://stackoverflow.com/questions/2200399/iterating-over-arrays-by-reflection/2200493#2200493), and it worked fine, so there must be some subtle difference from the simple example and what I am doing in the above code.
Simple Test which works:
public class Test
{
public static void main(String[] args) throws Exception
{
Employee[] employees = new Employee[3];
employees[0] = new Employee("Tom", "C");
employees[1] = new Employee("Paul", "E");
employees[2] = new Employee("George", "A");
Company company = new Company();
company.setEmployeeArray(employees);
Field field = company.getClass().getField("employeeArray");
if (field.getType().isArray()) {
Object array = field.get(company);
int length = Array.getLength(array);
for (int i = 0; i < length; i++) {
System.out.println(Array.get(array, i));
}
}
}
}
As for technologies info, for this example I am using: windows 7, eclipse 3.7, jdk 1.6.0_26, log4j 1.2.16, apache commons-lang3-3.0.1, and google guava 10.0
I've been trying to get this to work for the past few weeks, so any help would be greatly appreciated.
Edit:
For future reference and other's use, the relevant code to fix the issue is as follows:
boolean shouldSaveFieldValue = true;
if(... || field.getType() == String.class) {
..
else if(field.getType().isArray()) {
Object array = field.get(object);
...
shouldSaveFieldValue = false;
}
if(shouldSaveFieldValue) {
...
Element element = document.createElement(field.getName());
element.setTextContent(fieldValue);
node.appendChild(element);
}
}