让我们假设以下情况:
我有以下类型的对象的列表:
public class MyObject {
private String name
private SomeClass someField
private List<Fact> facts
}
name
和someField
字段只是为了表明该类具有一些常规成员。您可以假设知道如何将这些类转换为xml。
Fact
是一个接口,其中我不知道实现,但由插件提供。可能需要插件来提供任意代码,但我想使其尽可能简单。
我想保存这些对象并将其加载到xml。请注意,在加载xml时,并非所有实现都可能存在(xml可能是使用一组不同的插件编写的)。我希望能够再次读取xml而不丢失任何信息。换句话说:我愿意在类中添加List<Element>
或List<String>
之类的字段,并且在读取xml时,应该将存在插件的所有部分都读取到相应的{{1}中} s,尽管所有没有插件的部件都应存储在Fact
或Element
中,并且再次保存时,两个列表都会被保存,并且可以由具有所有插件的程序读取。
如何最好地使用JAXB做到这一点?
我可以看到的一种方法是使用String
而不是Map<Class, org.w3c.dom.Element>
,后者可以由JaxB转换为xml,然后让任何插件使用{ {1}} API,但是使用该API有点麻烦,所以我想知道是否有更好的方法?
答案 0 :(得分:1)
不知道best
,但是一种与您描述的相近的方法是:
JAXB无法与接口一起使用;最好能做的就是抽象类。意味着您需要使用List<Object>
或List<AbstractFact>
。 (但是您可以在getter,pluginresolver或afterUnmarshall()中实施一些限制。)
您的插件提供了扩展的基本类(SPI是常用的方法)。您收集它们并(在验证之后)使用它们创建您的JAXBContext
。 (如果要支持多个接口,则可以通过不同的方法提供它们。)
在xml中,您需要具有以下类型标记:<fact xsi:type=\"aFact\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">
。如果使用jaxb创建xml,则会自动创建。 (这些类需要具有@XmlRootElement批注)。
这是一个简化的示例:
interface Fact {
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
class R {
@XmlElement(name = "fact")
private List<Object> facts;
@SuppressWarnings("unchecked")
public List<Fact> getTest() {
if (facts == null) {
facts = new ArrayList<>();
}
return (List<Fact>) (Object) facts;
}
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
// check if all facts implement same interface
for(Object object:facts) {
if (!(object instanceof Fact)) {
throw new IllegalArgumentException("Unsupported type in facts list");
}
}
}
}
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "aFact")
class AFact implements Fact {
@XmlElement
private String a;
public AFact() {
}
public AFact(String a) {
this.a = a;
}
public String getA() {
return a;
}
@Override
public String toString() {
return "AFact [a=" + a + "]";
}
}
public class Jax {
public static void main(String[] args) throws JAXBException {
String xml = "<r><fact xsi:type=\"aFact\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><a>ba</a></fact></r>";
List<Class<?>> contextClasses = new ArrayList<>();
contextClasses.add(R.class);
contextClasses.addAll(getClassesFromPlugin());
JAXBContext context = JAXBContext.newInstance(contextClasses.toArray(new Class<?>[0]));
R entity = (R) context.createUnmarshaller().unmarshal(new StringReader(xml));
System.out.println(entity.getTest());
R r = new R();
r.getTest().add(new AFact("ab"));
context.createMarshaller().marshal(r, System.out);
}
private static List<Class<?>> getClassesFromPlugin() {
List<Class<?>> asList = Arrays.asList(AFact.class);
for(Class<?> cls:asList) {
if (!Fact.class.isAssignableFrom(cls)) {
throw new IllegalArgumentException("Unsupported class");
}
}
return asList;
}
}