I want to load object which contains array list of objects based on abstract class from yaml file. And i get this error message:
Exception in thread "LWJGL Application" Cannot create property=arrayListOfAbstractObjects for JavaBean=com.myyaml.test.ImplementationOfExampleClass@7a358cc1
in 'reader', line 1, column 1: dummyLong: 1 ^
java.lang.InstantiationException in 'reader', line 3, column 3: - dummyFloat: 444 ^
YAML file
dummyLong: 1
arrayListOfAbstractObjects:
- dummyFloat: 444
- dummyDouble: 123
Java classes:
public abstract class ExampleClass {
protected ArrayList<AbstractClass> arrayListOfAbstractObjects;
protected long dummyLong = 111;
public ExampleClass() {
}
public void setArrayListOfAbstractObjects(ArrayList<AbstractClass> arrayListOfAbstractObjects) {
this.arrayListOfAbstractObjects = arrayListOfAbstractObjects;
}
public void setDummyLong(long dummyLong) {
this.dummyLong = dummyLong;
}
}
public class ImplementationOfExampleClass extends ExampleClass {
public ImplementationOfExampleClass() {
}
}
public abstract class AbstractClass {
private int dummyInt = 22;
public AbstractClass() {
}
public void setDummyInt(int dummyInt) {
this.dummyInt = dummyInt;
}
}
public class FirstImplementationOfAbstractClass extends AbstractClass {
float dummyFloat = 111f;
public FirstImplementationOfAbstractClass() {
}
public void setDummyFloat(float dummyFloat) {
this.dummyFloat = dummyFloat;
}
}
public class SecondImplementationOfAbstractClass extends AbstractClass {
double dummyDouble = 333f;
public SecondImplementationOfAbstractClass() {
}
public void setDummyDouble(double dummyDouble) {
this.dummyDouble = dummyDouble;
}
}
My guess is that yaml doesn't know which sort of abstract class implementation to use. FirstImplementationOfAbstractClass or SecondImplementationOfAbstractClass. Is it possible to load an object by yaml with such classes?
This is only possible if you tell the YAML processor which class you want to instantiate on the YAML side. You do this with tags:
dummyLong: 1
arrayListOfAbstractObjects:
- !first
dummyFloat: 444
- !second
dummyDouble: 123
Then, you can instruct your YAML processor to properly process the items based on their tags. E.g. with SnakeYAML, you would do
class MyConstructor extends Constructor {
public MyConstructor() {
this.yamlConstructors.put(new Tag("!first"), new ConstructFirst());
this.yamlConstructors.put(new Tag("!second"), new ConstructSecond());
}
private class ConstructFirst extends AbstractConstruct {
public Object construct(Node node) {
// raw values, as if you would have loaded the content into a generic map.
final Map<Object, Object> values = constructMapping(node);
final FirstImplementationOfAbstractClass ret =
new FirstImplementationOfAbstractClass();
ret.setDummyFloat(Float.parseFloat(values.get("dummyFloat").toString()));
return ret;
}
}
private class ConstructSecond extends AbstractConstruct {
public Object construct(Node node) {
final Map<Object, Object> values = constructMapping(node);
final SecondImplementationOfAbstractClass ret =
new SecondImplementationOfAbstractClass();
ret.setDummyFloat(Double.parseDouble(values.get("dummyFloat").toString()));
return ret;
}
}
}
Note: You can be more intelligent when loading the content, avoiding toString
and instead process the node content directly; I use a dumb implementation for easy demonstration.
Then, you use this constructor:
Yaml yaml = new Yaml(new MyConstructor());
ExampleClass loaded = yaml.loadAs(input, ImplementationOfExampleClass.class);