0

私は、XMLEncoderPOJO を 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);

ただし、サードパーティ ライブラリを使用せずにこれを解決するオプションが 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 ではうまく機能し、独自の実装の基礎として機能する可能性があることに注意してください)。

于 2014-12-26T17:46:11.587 に答える