【问题标题】:How to write arrays to XML file using Java reflection?如何使用 Java 反射将数组写入 XML 文件?
【发布时间】:2023-03-29 23:16:01
【问题描述】:

我正在尝试编写一个小型库,该库将通过反射将 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/iterating-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);
  }
}

【问题讨论】:

  • 你拥有的列表和数组之间的逻辑区别是什么?
  • @glowcoder 如果您谈论员工列表字段和employeeArray 数组字段。我最初试图让它与列表一起使用,我不小心在 Employee.java 和我的帖子标题中留下了员工列表字段。让它与列表一起工作会很好,但我只能使用数组来处理它。将编辑我的帖子。
  • 目前只有十几个开源库可以完成这项工作。与编写自己的代码相比,使用 JAX-B、XML-Beans 或来自 Apache CXF 的 Aegis 可能更快乐。
  • @bmargulies 这些看起来可以工作,但我故意避开其他库。简要查看了 JAX-B,但它看起来有些复杂,我的目标是编写一个简单的库。如果我需要更复杂的库,我肯定会考虑使用您提到的其中一些库,而不是为此编写更多功能。
  • @nickg - 以下示例将帮助您解决 JAXB:wiki.eclipse.org/EclipseLink/Examples/MOXy/GettingStarted

标签: java xml arrays list reflection


【解决方案1】:

首先,我建议使用库而不是自己编写库。我对 JAXB 真的很幸运,它不需要第三方 jar。对于您的问题,我认为您只是对变量名“object”和“obj”感到困惑:

Company object = a Company
Field field = [ Company.Employee[] employeeArray ]
Employee[] obj = object.employeeArray

field.get(obj) 无法评估,因为参数不是 Company 类型。以下是相关的代码:

private Node createNode(Document document, Object object) {
    ..
    List<Field> fields = Lists.newArrayList(object.getClass().getFields());
        for(Field field: fields) {
        ...
        Object obj = field.get(object);
        ...
        else if(field.getType().isArray()) {
            Object array = field.get(obj); //Fails here

【讨论】:

  • +1 在失败的行中,obj 应该已经是数组,而错误只是措辞不直观。
  • 这正是需要改变的地方。我不得不更改其他一些小事情以使其正确处理字符串并且不在数组末尾的额外标记“”标记中写入,并将在稍后发布这些以供将来参考并供他人使用。谢谢!
【解决方案2】:

变化:

public Employee employeeArray[];

到:

public transient Employee employeeArray[];

据我所知,数组是不可序列化的,我认为这就是为什么你有一个员工列表和一个数组。所以这告诉序列化程序在序列化对象时忽略员工数组。

【讨论】:

    猜你喜欢
    • 2012-11-22
    • 2010-12-27
    • 2014-03-12
    • 2011-09-06
    • 1970-01-01
    • 2012-03-29
    • 2014-03-20
    • 1970-01-01
    相关资源
    最近更新 更多