Search code examples
javaxml-deserializationsimple-framework

Deserialize immutable class with Simple XML framework


Assume I have a class like this:

public class Measurement {
    /** The date and time at which the measurement was taken. */
    public final Date date;

    /** The measurement value. */
    public final float value;

    /** The name of the engineer who took the measurement. */
    public final String engineer;

    public Measurement(Date date, float value, String engineer) {
        super();
        this.date = date;
        this.value = value;
        this.engineer = engineer;
    }
}

Each Measurement instance is immutable. Once created, its members cannot be modified. One can, however, create a new instance, with some values copied from an existing instance and some set differently.

If things get more complex, e.g. because there is a large number of fields and most of them are not mandatory, the constructor would be private and the class would be accompanied by a builder class instead. (Indeed the actual code is more complex; this is just a minimal example.)

Now I can quite easily add a few SimpleXML annotations to serialize this to XML, like this:

@Root(name="measurement")
@Default
public class Measurement {
    /** The date and time at which the measurement was taken. */
    @Attribute
    public final Date date;

    /** The measurement value. */
    @Attribute
    public final float value;

    /** The name of the engineer who took the measurement. */
    @Attribute
    public final String engineer;

    public Measurement(Date date, float value, String engineer) {
        super();
        this.date = date;
        this.value = value;
        this.engineer = engineer;
    }
}

This would then serialize to something like:

<measurement date="2019-11-01 11:55:42.0 CET" value="42.0" engineer="Doe"/>

How would I then go about de-serializing the resulting XML code back to a class?


Solution

  • The official way to do that seems to be constructor injection: The only way to set final members is through the constructor, and the constructor has an argument for each member. So the constructor would look something like this:

    public Measurement(@Attribute(name="date") Date date,
                       @Attribute(name="value") float value,
                       @Attribute(name="engineer") String engineer) {
        super();
        this.date = date;
        this.value = value;
        this.engineer = engineer;
    }
    

    As far as I know, it is necessary to specify the attribute name here, even if it corresponds to the arguments.

    In the example the constructor is public. Not sure what is required here, as SimpleXML seems to rely on reflection to find the right constructor.