デフォルトでは、XMLEncoder
は Java Bean のプロパティがまだデフォルト値を持っている場合、そのプロパティをシリアライズしません。これも奇妙だと思いました。特に、どうしてこんな方法がないんだろうと思った
xmlEncoder.setRedundancyEliminationEnabled(false);
ただし、サードパーティ ライブラリを使用せずにこれを解決するオプションが 1 つあります。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 ではうまく機能し、独自の実装の基礎として機能する可能性があることに注意してください)。