Search code examples
javaxmlxstreamxmlmapper

How to configure XStream to map to different classes depending on XML attributes?


I have the following Java object hierarchy:

public interface Function {
    public void calculate(long t);
}

public class ConstantFunction implements Function {
    private double constant;

    @Override
    public void calculate(long t) {
        // ...
    }
}

public class LinearFunction implements Function {
    private double slope;

    private double yIntercept;

    @Override
    public void calculate(long t) {
        // ...
    }
}

Users can create ConstantFunction and LinearFunction instances by defining them inside XML like so:

<myapp>
    <function type="ConstantFunction>
        <!-- ... -->
    </function>
    <function type="LinearFunction>
        <!-- ... -->
    </function>
</myapp>

I am using XStream to OX-map the user-defined XML into Java POJOs. I'm currently trying to configure the XStream mapper with aliases so that it knows what Java class to bind to the function element:

XStream oxmapper = new XStream();
oxmapper.alias("myapp", MyApp.class);
oxmapper.alias("function", ???);

The problem is, I need to configure XStream with logic that says: *if function/type is ConstantFunction, then use oxmapper.alias("function", ConstantFunction.class); but if its value is LinearFunction, then use oxmapper.alias("function", LinearFunction.class).

The problem is, I don't think XStream API gives provides a way to inspect the XML the way I need it to in order to implement this logic. If I am incorrect, please point me in the right direction!

If I am correct, then the only solution I can think of would be to have a really nasty "hodgepodge" class that forms a union of all the Function concretions like so:

public class FunctionFactory implements Function {
    private double constant;
    private double slope;
    private double yIntercept;

    private Class<? extends Function> concreteClass;

    @Override
    public void calculate(long t) {
        // Do nothing. This class is a workaround to limitations with XStream.
        return;
    }
}

In the OX-mapper config:

oxampper.alias("function", FunctionFactory.class);
oxmapper.aliasField("function", "type", "concreteClass");

Now, every time I read an XML instance into a MyApp instance, I need to correct the conversion:

XStream oxmapper = getConfiguredMapper();
MyApp app = oxmapper.fromXml("<myapp>...</myapp>");

FunctionFactory factory = app.getFunction();
Function concretion = factory.getConcreteClass();

app.setFunction(concretion);

This is the only workaround I can cook up, but it feels really nasty, and I have to believe there's a better way to do this. Thanks in advance!


Solution

  • When you want to have custom behaviors in XStream, you should use Converters as described in Converter tutorial.