0

我一直在使用XMLEncoder将 POJO 转换为 XML 并再次转换回来。除了存储 POJO,一些 XML 输出也将被其他应用程序用来生成其他形式的数据,例如报告。

到目前为止,这运作良好。即使 POJO 不断发展,也没有出现任何问题。

最近我意识到并不是所有的值都是实际输出的。默认值未更改的属性将不会被写入。这对我来说是个问题。

来自 Javadoc:

"The XMLEncoder class uses a redundancy elimination algorithm internally so that the default values of a Bean's properties are not written to the stream"

对我来说,完整输出 bean 很重要——包括所有默认值。

有什么办法可以禁用这个功能XMLEncoder吗?

4

2 回答 2

1

默认情况下,XMLEncoder当 Java Bean 的属性仍然具有默认值时,它们不会序列化它们。我也觉得这很奇怪。特别是,我想知道为什么没有像

xmlEncoder.setRedundancyEliminationEnabled(false);

但是,有一个选项可以在没有第三方库的情况下解决这个问题:为了完全序列化 Java Bean,即使它的属性仍然具有默认值,也可以使用自己的PersistenceDelegate.

这是这样的示例实现PersistenceDelegate

import java.beans.BeanInfo;
import java.beans.Encoder;
import java.beans.Expression;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PersistenceDelegate;
import java.beans.PropertyDescriptor;
import java.beans.Statement;
import java.lang.reflect.Method;

/**
 * Implementation of a PersistenceDelegate that serializes all properties
 * of a Java Bean, even if they still have their default values.
 */
class FullPersistenceDelegate extends PersistenceDelegate
{
    @Override
    protected Expression instantiate(Object oldInstance, Encoder out)
    {
        return new Expression(
            oldInstance, oldInstance.getClass(), "new", null);
    }

    @Override
    protected void initialize(Class<?> type, Object oldInstance,
        Object newInstance, Encoder out)
    {
        super.initialize(type, oldInstance, newInstance, out);
        if (oldInstance.getClass() == type)
        {
            initializeProperties(type, oldInstance, out);
        }
    }

    /**
     * Write all statements to initialize the properties of the given 
     * Java Bean Type, based on the given instance, using the given
     * encoder
     *   
     * @param type The Java Bean Type
     * @param oldInstance The base instance
     * @param out The encoder
     */
    private void initializeProperties(
        Class<?> type, Object oldInstance, Encoder out)
    {
        BeanInfo info = null;
        try
        {
            info = Introspector.getBeanInfo(type);
        }
        catch (IntrospectionException ie)
        {
            out.getExceptionListener().exceptionThrown(ie);
            return;
        }
        PropertyDescriptor[] pds = info.getPropertyDescriptors();
        for (int i = 0; i < pds.length; ++i)
        {
            try
            {
                initializeProperty(type, pds[i], oldInstance, out);
            }
            catch (Exception e)
            {
                out.getExceptionListener().exceptionThrown(e);
            }
        }
    }

    /**
     * Write the statement to initialize the specified property of the given 
     * Java Bean Type, based on the given instance, using the given
     * encoder
     *   
     * @param type The Java Bean Type
     * @param pd The property descriptor
     * @param oldInstance The base instance
     * @param out The encoder
     * @throws Exception If the value can not be obtained
     */
    private void initializeProperty(
        Class<?> type, PropertyDescriptor pd, Object oldInstance, Encoder out) 
        throws Exception
    {
        Method getter = pd.getReadMethod();
        Method setter = pd.getWriteMethod();
        if (getter != null && setter != null)
        {
            Expression oldGetExpression =
                new Expression(oldInstance, getter.getName(), new Object[] {});
            Object oldValue = oldGetExpression.getValue();
            Statement setStatement =
                new Statement(oldInstance, setter.getName(), 
                    new Object[] { oldValue });
            out.writeStatement(setStatement);
        }
    }
}

使用它很简单:它只需要为编码器注册:

encoder.setPersistenceDelegate(ExampleBean.class,
    new FullPersistenceDelegate());

就是这样。

这是一个使用示例。给定一个简单的示例 bean 类...

public class ExampleBean
{
    enum ExampleBeanEnum
    {
        DEFAULT_ENUM_VALUE,
        MODIFIED_ENUM_VALUE,
    }
    private String stringValue;
    private int intValue;
    private ExampleBeanEnum enumValue;

    public ExampleBean()
    {
        stringValue = "Default String Value";
        intValue = 123;
        enumValue = ExampleBeanEnum.DEFAULT_ENUM_VALUE;
    }

    public String getStringValue()
    {
        return stringValue;
    }
    public void setStringValue(String stringValue)
    {
        this.stringValue = stringValue;
    }
    public int getIntValue()
    {
        return intValue;
    }
    public void setIntValue(int intValue)
    {
        this.intValue = intValue;
    }
    public ExampleBeanEnum getEnumValue()
    {
        return enumValue;
    }
    public void setEnumValue(ExampleBeanEnum enumValue)
    {
        this.enumValue = enumValue;
    }
}

和一个测试班...

import java.beans.ExceptionListener;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

public class FullPersistenceDelegateExample
{
    public static void main(String[] args)
    {
        // Create an XMLEncoder that writes to a byte array
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        XMLEncoder encoder = new XMLEncoder(stream);
        encoder.setExceptionListener(new ExceptionListener()
        {
            @Override
            public void exceptionThrown(Exception e)
            {
                e.printStackTrace();
            }
        });

        // Set the FullPersistenceDelegate for the ExampleBean class
        encoder.setPersistenceDelegate(ExampleBean.class,
            new FullPersistenceDelegate());

        // Write an instance of the ExampleBean, where only one property
        // was modified, as compared to the default value.
        // The default persistence delegate would cause only the modified
        // property to be written. However, the FullPersistenceDelegate
        // will cause ALL properties to be written
        ExampleBean oldExampleBean = new ExampleBean();
        oldExampleBean.setIntValue(234);

        encoder.writeObject(oldExampleBean);
        encoder.flush();
        encoder.close();

        // Print the encoding result
        System.out.println(stream);

        // Read the instance back and print its properties
        XMLDecoder d =
            new XMLDecoder(new ByteArrayInputStream(stream.toByteArray()));
        ExampleBean newExampleBean = (ExampleBean) d.readObject();
        System.out.println("stringValue: " + newExampleBean.getStringValue());
        System.out.println("intValue   : " + newExampleBean.getIntValue());
        System.out.println("enumValue  : " + newExampleBean.getEnumValue());
    }
}

可以看到输出包含所有字段的属性值,即使它们仍然具有默认值:

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_65" class="java.beans.XMLDecoder">
 <object class="ExampleBean">
  <void property="enumValue">
   <object class="java.lang.Enum" method="valueOf">
    <class>ExampleBean$ExampleBeanEnum</class>
    <string>DEFAULT_ENUM_VALUE</string>
   </object>
  </void>
  <void property="intValue">
   <int>234</int>
  </void>
  <void property="stringValue">
   <string>Default String Value</string>
  </void>
 </object>
</java>

(请注意,这尚未针对更复杂的场景进行测试,但它适用于简单的 bean,并且可以作为自己实现的基础)。

于 2014-12-26T17:46:11.587 回答
0

我放弃了 XMLEncoder 并转而使用 XStream(请参阅http://x-stream.github.io/)。

到目前为止运行良好...

于 2014-06-03T04:22:18.483 回答