Я работаю над сериализацией части моей структуры объектов с JAXB
.У меня иногда бывает несколько уровней наследования.Вот где я наткнулся на проблему.
Давайте рассмотрим, что я хочу сериализовать атрибуты Memory
.Memory
содержит коллекцию AbstractClass
, в которую я добавляю все реализации AbstractClass
.Это AbstractSubClass1
и AbstractSubClass2
как абстрактные классы и SubClass1
и SubClass2
как фактические реализации.На данный момент мне достаточно этой информации.
Чтобы обойти такие ошибки, как JAXBException: Neither class SubClass1 nor one of the associated superclasses is known in this context.
, я могу добавить
@XmlSeeAlso({
SubClass1.class
,SubClass2.class
})
public static class Memory {
...
}
к Memory
или AbstractClass
.Однако, поскольку моя объектная модель довольно большая, я не хотел добавлять все классы, которые могут содержаться в Memory
, в список XmlSeeAlso
.Поэтому я подумал, что могу просто добавить @XmlSeeAlso
теги ко всем суперклассам, содержащим список их реализаций, поэтому
@XmlSeeAlso({
AbstractSubClass1.class
,AbstractSubClass2.class
})
public static abstract class AbstractClass {
...
}
и
@XmlSeeAlso({
SubClass1.class
})
public static abstract class AbstractSubClass1{
...
}
и
@XmlSeeAlso({
SubClass2.class
})
public static abstract class AbstractSubClass2{
...
}
Но за такой подход я получаю StackOverFlowError
:
Exception in thread "main" java.lang.StackOverflowError
at java.base/java.util.Arrays$ArrayItr.<init>(Arrays.java:4439)
at java.base/java.util.Arrays$ArrayList.iterator(Arrays.java:4431)
at java.base/java.util.AbstractList.hashCode(AbstractList.java:566)
at java.base/java.util.Objects.hashCode(Objects.java:116)
at java.base/jdk.internal.loader.AbstractClassLoaderValue$Sub.hashCode(AbstractClassLoaderValue.java:430)
at java.base/java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936)
at java.base/jdk.internal.loader.AbstractClassLoaderValue.computeIfAbsent(AbstractClassLoaderValue.java:194)
at java.base/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:423)
at java.base/java.lang.reflect.Proxy.newProxyInstance(Proxy.java:1004)
at com.sun.xml.bind.v2.model.annotation.LocatableAnnotation.create(LocatableAnnotation.java:85)
at com.sun.xml.bind.v2.model.annotation.RuntimeInlineAnnotationReader.getClassAnnotation(RuntimeInlineAnnotationReader.java:106)
at com.sun.xml.bind.v2.model.annotation.RuntimeInlineAnnotationReader.getClassAnnotation(RuntimeInlineAnnotationReader.java:57)
at com.sun.xml.bind.v2.model.impl.ModelBuilder.getClassInfo(ModelBuilder.java:287)
at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.getClassInfo(RuntimeModelBuilder.java:103)
at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.getClassInfo(RuntimeModelBuilder.java:84)
at com.sun.xml.bind.v2.model.impl.ModelBuilder.getClassInfo(ModelBuilder.java:254)
at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.getClassInfo(RuntimeModelBuilder.java:103)
at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.getClassInfo(RuntimeModelBuilder.java:84)
at com.sun.xml.bind.v2.model.impl.ModelBuilder.getClassInfo(ModelBuilder.java:227)
at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.getClassInfo(RuntimeModelBuilder.java:98)
at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.getClassInfo(RuntimeModelBuilder.java:84)
...
Почему это так?Нет ли другого способа, кроме как объявить реализации на Memory
и AbstractClass
?Не возможно ли иметь каскадный список?
MWE
import java.util.ArrayList;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
public class MWE {
public static void main(String args[]) throws JAXBException{
SubClass1 item1 = new SubClass1();
SubClass2 item2 = new SubClass2();
item1.setName(item1.getClass().getSimpleName());
item2.setName(item2.getClass().getSimpleName());
Memory memory = new Memory();
memory.addItem(item1);
memory.addItem(item2);
JAXBContext jaxbContext = JAXBContext.newInstance(Memory.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(memory, System.out);
}
@XmlRootElement
public static class Memory {
@XmlElementWrapper(name="items")
@XmlElement(name="item")
private ArrayList<AbstractClass> items = new ArrayList<>();
public void addItem(AbstractClass v){this.items.add(v);}
public ArrayList<AbstractClass> getItems(){return items;}
}
@XmlTransient
@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({
AbstractSubClass1.class
,AbstractSubClass2.class
})
public static abstract class AbstractClass {
@XmlAttribute
@XmlID
private String name;
public void setName(String name) {this.name = name;}
public String getName() {return name;}
}
@XmlTransient
@XmlSeeAlso({
SubClass1.class
})
public static abstract class AbstractSubClass1 extends AbstractClass {}
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class SubClass1 extends AbstractSubClass1 {
private double value = 1.0;
public void setValue(double value) {this.value = value;}
public double getValue() {return value;}
}
@XmlTransient
@XmlSeeAlso({
SubClass2.class
})
public static abstract class AbstractSubClass2 extends AbstractClass {}
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class SubClass2 extends AbstractSubClass2 {
private int value = 1;
public void setValue(int value) {this.value = value;}
public int getValue() {return value;}
}
}