如何使用 Java 反射将数组写入 XML 文件?
我正在尝试编写一个小型库,它将通过反射将 Java 对象转换为 XML。我已经完成了大部分工作,但在尝试迭代数组时遇到了错误。
以下是我用于测试的域对象:
在 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;
}
}
在 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;
}
}
库的核心 (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;
}
}
和测试驱动程序 (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();
}
}
运行此错误时遇到的错误代码如下:
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)
我意识到要发布的代码相当多。我尝试了本文中所示的以下示例(http://stackoverflow.com/questions/2200399/iteating-over-arrays-by-reflection/2200493#2200493),它工作得很好,所以一定有一些细微的差别从简单的例子以及我在上面的代码中所做的事情。
有效的简单测试:
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));
}
}
}
}
至于技术信息,对于这个例子,我使用的是:windows 7、eclipse 3.7、jdk 1.6.0_26、log4j 1.2.16、apache commons-lang3-3.0.1 和 google guava 10.0
我一直在在过去的几周里试图让它发挥作用,所以任何帮助将不胜感激。
编辑:
为了将来参考和其他人的使用,解决该问题的相关代码如下:
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);
}
}
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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先,我赞同使用库而不是自己创建库的建议。我在 JAXB 方面运气非常好,它不需要第三方 jar。对于您的问题,我认为您只是对变量名称“object”和“obj”感到困惑:
无法评估 field.get(obj) 因为参数不是 Company 类型。这是相关的代码:
First, I second the recommendation to use a library rather than rolling your own. I've had really good luck with JAXB which doesn't event require a third party jar. To your question, I think you're simply getting confused between the variable names "object" and "obj":
field.get(obj) cannot be evaluated because the parameter is not of type Company. Here's the relevant bits of code:
更改
为:
据我所知,数组不可序列化,我认为这就是您拥有员工列表和数组的原因。因此,这告诉序列化器在序列化对象时忽略员工数组。
Change:
to:
As far as I know, arrays are not serializable, which, I assume, is why you have an employee list as well as an array. So this tells the serializer to ignore the employee array when serializing the object.